@ -712,9 +712,9 @@ export class K1ImportService {
'1' : 'ordinaryIncome' ,
'1' : 'ordinaryIncome' ,
'2' : 'netRentalIncome' ,
'2' : 'netRentalIncome' ,
'3' : 'otherRentalIncome' ,
'3' : 'otherRentalIncome' ,
'4a ' : 'guaranteedPayments' , // 4a guaranteed payments (services)
'4' : 'guaranteedPayments' , // 4 guaranteed payments (services)
'4b ' : 'guaranteedPayments' , // 4b guaranteed payments (capital) — merged
'4a ' : 'guaranteedPayments' , // 4a guaranteed payments (capital) — merged
'4c ' : 'guaranteedPayments' , // 4c total guaranteed payments
'4b ' : 'guaranteedPayments' , // 4b total guaranteed payments — merged
'5' : 'interestIncome' ,
'5' : 'interestIncome' ,
'6a' : 'dividends' ,
'6a' : 'dividends' ,
'6b' : 'qualifiedDividends' ,
'6b' : 'qualifiedDividends' ,
@ -734,7 +734,7 @@ export class K1ImportService {
'21' : 'foreignTaxesPaid' ,
'21' : 'foreignTaxesPaid' ,
// Section L: Tax basis / capital account fields
// Section L: Tax basis / capital account fields
'L_BEG_CAPITAL' : 'beginningTaxBasis' ,
'L_BEG_CAPITAL' : 'beginningTaxBasis' ,
'L_CAPITAL_C ONTRIBUTED' : 'k1CapitalAccount' ,
'L_CONTRIBUTED' : 'k1CapitalAccount' ,
'L_END_CAPITAL' : 'endingTaxBasis' ,
'L_END_CAPITAL' : 'endingTaxBasis' ,
} ;
} ;
@ -753,69 +753,74 @@ export class K1ImportService {
// Merge named fields into the document data (named fields take precedence for views)
// Merge named fields into the document data (named fields take precedence for views)
const finalDocumentData = { . . . kDocumentData , . . . k1DataSnapshot } ;
const finalDocumentData = { . . . kDocumentData , . . . k1DataSnapshot } ;
// FR-012: Create or update KDocument
// FR-012: Create or update KDocument with K1LineItems in a single transaction
// to ensure consistent state (no superseded-but-unreplaced line items).
let kDocument ;
let kDocument ;
if ( existingKDocument && data . existingKDocumentAction === 'UPDATE' ) {
await this . prismaService . $transaction ( async ( tx ) = > {
kDocument = await this . prismaService . kDocument . update ( {
if ( existingKDocument && data . existingKDocumentAction === 'UPDATE' ) {
where : { id : existingKDocument.id } ,
kDocument = await tx . kDocument . update ( {
data : {
where : { id : existingKDocument.id } ,
filingStatus : data.filingStatus ,
data : {
data : finalDocumentData as any ,
filingStatus : data.filingStatus ,
documentFileId : session.documentId
data : finalDocumentData as any ,
documentFileId : session.documentId
}
} ) ;
// FR-016: Mark existing active K1LineItems as superseded (ESTIMATED→FINAL)
await tx . k1LineItem . updateMany ( {
where : {
kDocumentId : existingKDocument.id ,
isSuperseded : false
} ,
data : { isSuperseded : true }
} ) ;
} else {
// CREATE_NEW or no existing document
if ( existingKDocument && data . existingKDocumentAction === 'CREATE_NEW' ) {
// Delete existing unique constraint holder to create new
await tx . kDocument . delete ( {
where : { id : existingKDocument.id }
} ) ;
}
}
} ) ;
// FR-016: Mark existing active K1LineItems as superseded (ESTIMATED→FINAL)
kDocument = await tx . kDocument . create ( {
await this . prismaService . k1LineItem . updateMany ( {
data : {
where : {
partnershipId : session.partnershipId ,
kDocumentId : existingKDocument.id ,
type : 'K1' ,
isSuperseded : false
taxYear : session.taxYear ,
} ,
filingStatus : data.filingStatus ,
data : { isSuperseded : true }
data : finalDocumentData as any ,
} ) ;
documentFileId : session.documentId
} else {
}
// CREATE_NEW or no existing document
if ( existingKDocument && data . existingKDocumentAction === 'CREATE_NEW' ) {
// Delete existing unique constraint holder to create new
await this . prismaService . kDocument . delete ( {
where : { id : existingKDocument.id }
} ) ;
} ) ;
}
}
kDocument = await this . prismaService . kDocument . create ( {
// Create K1LineItem rows (the authoritative normalized data)
data : {
if ( lineItemsToCreate . length > 0 ) {
partnershipId : session.partnershipId ,
await tx . k1LineItem . createMany ( {
type : 'K1' ,
data : lineItemsToCreate.map ( ( item ) = > ( {
taxYear : session.taxYear ,
kDocumentId : kDocument.id ,
filingStatus : data.filingStatus ,
boxKey : item.boxKey ,
data : finalDocumentData as any ,
amount : item.amount ,
documentFileId : session.documentId
textValue : item.textValue ,
}
rawText : item.rawText ,
} ) ;
confidence : item.confidence ,
}
sourcePage : item.sourcePage ,
sourceCoords : item.sourceCoords ,
// Create K1LineItem rows (the authoritative normalized data)
isUserEdited : item.isUserEdited ,
if ( lineItemsToCreate . length > 0 ) {
isSuperseded : false
await this . prismaService . k1LineItem . createMany ( {
} ) )
data : lineItemsToCreate.map ( ( item ) = > ( {
} ) ;
kDocumentId : kDocument.id ,
boxKey : item.boxKey ,
amount : item.amount ,
textValue : item.textValue ,
rawText : item.rawText ,
confidence : item.confidence ,
sourcePage : item.sourcePage ,
sourceCoords : item.sourceCoords ,
isUserEdited : item.isUserEdited ,
isSuperseded : false
} ) )
} ) ;
this . logger . log (
this . logger . log (
` Session ${ sessionId } : Created ${ lineItemsToCreate . length } K1LineItem rows for KDocument ${ kDocument . id } `
` Session ${ sessionId } : Created ${ lineItemsToCreate . length } K1LineItem rows for KDocument ${ kDocument . id } `
) ;
) ;
}
} ) ;
// Emit event to refresh materialized views (US5)
// Emit event after the transaction commits to refresh materialized views (US5)
if ( kDocument && lineItemsToCreate . length > 0 ) {
this . eventEmitter . emit ( 'k-document.changed' , {
this . eventEmitter . emit ( 'k-document.changed' , {
kDocumentId : kDocument.id ,
kDocumentId : kDocument.id ,
partnershipId : kDocument.partnershipId
partnershipId : kDocument.partnershipId
@ -886,7 +891,7 @@ export class K1ImportService {
// This drives Portfolio Summary (DPI/RVPI/TVPI) computations
// This drives Portfolio Summary (DPI/RVPI/TVPI) computations
const sectionLData = {
const sectionLData = {
beginningCapital : boxValues [ 'L_BEG_CAPITAL' ] ? ? null ,
beginningCapital : boxValues [ 'L_BEG_CAPITAL' ] ? ? null ,
capitalContributed : boxValues [ 'L_CAPITAL_C ONTRIBUTED' ] ? ? null ,
capitalContributed : boxValues [ 'L_CONTRIBUTED' ] ? ? null ,
endingCapital : boxValues [ 'L_END_CAPITAL' ] ? ? null ,
endingCapital : boxValues [ 'L_END_CAPITAL' ] ? ? null ,
withdrawals : boxValues [ 'L_WITHDRAWALS' ] ? ? null
withdrawals : boxValues [ 'L_WITHDRAWALS' ] ? ? null
} ;
} ;