@@ -24,7 +24,7 @@ type TranslationParams = {
2424
2525type Signer = {
2626 email : string
27- identify ?: string | number
27+ identifyMethods ?: Array < { method : string ; value : string ; mandatory : number } >
2828 localKey ?: string
2929 signRequestId ?: number
3030}
@@ -654,10 +654,10 @@ describe('files store - critical business rules', () => {
654654 expect ( store . isTemporaryId ( - 999 ) ) . toBe ( true )
655655 } )
656656
657- it ( 'identifies envelope_ prefix as temporary ID ' , ( ) => {
657+ it ( 'does not identify strings as temporary IDs ' , ( ) => {
658658 const store = useFilesStore ( )
659- expect ( store . isTemporaryId ( 'envelope_123' ) ) . toBe ( true )
660- expect ( store . isTemporaryId ( 'envelope_abc' ) ) . toBe ( true )
659+ expect ( store . isTemporaryId ( 'envelope_123' ) ) . toBe ( false )
660+ expect ( store . isTemporaryId ( 'envelope_abc' ) ) . toBe ( false )
661661 } )
662662
663663 it ( 'does not identify positive numbers as temporary' , ( ) => {
@@ -867,6 +867,29 @@ describe('files store - critical business rules', () => {
867867 expect ( signers [ 0 ] . localKey ) . toBe ( 'signer:10' )
868868 expect ( signers [ 2 ] . localKey ) . toBe ( 'signer:20' )
869869 } )
870+
871+ it ( 'hydrates nested child signers and deep-clones metadata in editable draft' , async ( ) => {
872+ const store = useFilesStore ( )
873+ const sourceFile = {
874+ id : 1 ,
875+ signers : [ { email : 'parent@example.com' , signRequestId : 10 } ] ,
876+ files : [ {
877+ id : 2 ,
878+ metadata : { d : [ { w : 100 , h : 200 } ] } ,
879+ signers : [ { email : 'child@example.com' , signRequestId : 20 } ] ,
880+ } ] ,
881+ }
882+ await store . addFile ( sourceFile , { detailsLoaded : true } )
883+
884+ store . selectedFileId = 1
885+ const draft = store . getEditableFile ( )
886+
887+ expect ( draft . files ?. [ 0 ] ?. signers ?. [ 0 ] ?. localKey ) . toBe ( 'signer:20' )
888+ if ( draft . files ?. [ 0 ] ?. metadata ?. d ?. [ 0 ] ) {
889+ draft . files [ 0 ] . metadata . d [ 0 ] . w = 300
890+ }
891+ expect ( sourceFile . files [ 0 ] . metadata . d [ 0 ] . w ) . toBe ( 100 )
892+ } )
870893 } )
871894
872895 describe ( 'RULE: file lookup by nodeId' , ( ) => {
@@ -1035,7 +1058,12 @@ describe('files store - critical business rules', () => {
10351058 id : 1 ,
10361059 name : 'contract.pdf' ,
10371060 signatureFlow : 'parallel' ,
1038- signers : [ { email : 'signer@example.com' } ] ,
1061+ signers : [ {
1062+ email : 'signer@example.com' ,
1063+ identifyMethods : [ { method : 'email' , value : 'signer@example.com' , mandatory : 0 } ] ,
1064+ localKey : 'draft-signer:1' ,
1065+ statusText : 'Draft' ,
1066+ } ] ,
10391067 }
10401068 axiosMock . mockResolvedValue ( {
10411069 data : { ocs : { data : { id : 1 , nodeId : 99 , signatureFlow : 'parallel' , signers : [ ] } } } ,
@@ -1044,14 +1072,53 @@ describe('files store - critical business rules', () => {
10441072 await store . saveOrUpdateSignatureRequest ( { status : 1 } )
10451073
10461074 const config = axiosMock . mock . calls [ 0 ] [ 0 ]
1047- expect ( config . data . signers ) . toEqual ( [ { email : 'signer@example.com' } ] )
1075+ expect ( config . data . signers ) . toEqual ( [ {
1076+ identifyMethods : [ { method : 'email' , value : 'signer@example.com' , mandatory : 0 } ] ,
1077+ } ] )
1078+ } )
1079+
1080+ it ( 'removes UI-only signer fields and preserves backend-facing fields' , async ( ) => {
1081+ const store = useFilesStore ( )
1082+ store . selectedFileId = 1
1083+ store . files [ 1 ] = {
1084+ id : 1 ,
1085+ name : 'contract.pdf' ,
1086+ signatureFlow : 'parallel' ,
1087+ signers : [ {
1088+ identifyMethods : [ { method : 'email' , value : 'signer@example.com' , mandatory : 1 } ] ,
1089+ displayName : 'Signer' ,
1090+ description : 'Needs review' ,
1091+ notify : 0 ,
1092+ status : 1 ,
1093+ localKey : 'draft-signer:1' ,
1094+ statusText : 'Draft' ,
1095+ me : true ,
1096+ signed : [ 'sig' ] ,
1097+ visibleElements : [ { elementId : 10 } ] ,
1098+ } ] ,
1099+ }
1100+ axiosMock . mockResolvedValue ( {
1101+ data : { ocs : { data : { id : 1 , nodeId : 99 , signatureFlow : 'parallel' , signers : [ ] } } } ,
1102+ } )
1103+
1104+ await store . saveOrUpdateSignatureRequest ( { status : 1 } )
1105+
1106+ const config = axiosMock . mock . calls [ 0 ] [ 0 ]
1107+ expect ( config . data . signers ) . toEqual ( [ {
1108+ identifyMethods : [ { method : 'email' , value : 'signer@example.com' , mandatory : 1 } ] ,
1109+ displayName : 'Signer' ,
1110+ description : 'Needs review' ,
1111+ notify : 0 ,
1112+ status : 1 ,
1113+ } ] )
10481114 } )
10491115
10501116 it ( 'maps numeric signatureFlow to ordered_numeric' , async ( ) => {
10511117 const store = useFilesStore ( )
10521118 store . selectedFileId = 1
10531119 store . files [ 1 ] = {
10541120 id : 1 ,
1121+ nodeId : 99 ,
10551122 name : 'contract.pdf' ,
10561123 signatureFlow : 2 ,
10571124 signers : [ ] ,
@@ -1065,7 +1132,7 @@ describe('files store - critical business rules', () => {
10651132
10661133 const config = axiosMock . mock . calls [ 0 ] [ 0 ]
10671134 expect ( config . data . signatureFlow ) . toBe ( 'ordered_numeric' )
1068- expect ( config . data . file . fileId ) . toBe ( 1 )
1135+ expect ( config . data . file . nodeId ) . toBe ( 99 )
10691136 expect ( config . data . file . settings ) . toEqual ( { path : '/files/contract.pdf' } )
10701137 } )
10711138
@@ -1105,7 +1172,7 @@ describe('files store - critical business rules', () => {
11051172 signatureFlow : 'parallel' ,
11061173 settings : { path : '/files/contract.pdf' } ,
11071174 visibleElements : [ { id : 77 } ] ,
1108- signers : [ { identify : ' signer01@libresign.coop', signRequestId : 10 } ] ,
1175+ signers : [ { identifyMethods : [ { method : 'email' , value : ' signer01@libresign.coop', mandatory : 0 } ] , signRequestId : 10 } ] ,
11091176 }
11101177 axiosMock . mockResolvedValue ( {
11111178 data : {
@@ -1114,7 +1181,7 @@ describe('files store - critical business rules', () => {
11141181 id : 1 ,
11151182 uuid : 'file-uuid' ,
11161183 signatureFlow : 'parallel' ,
1117- signers : [ { identify : ' signer01@libresign.coop', signRequestId : 10 } ] ,
1184+ signers : [ { identifyMethods : [ { method : 'email' , value : ' signer01@libresign.coop', mandatory : 0 } ] , signRequestId : 10 } ] ,
11181185 } ,
11191186 } ,
11201187 } ,
@@ -1177,7 +1244,7 @@ describe('files store - critical business rules', () => {
11771244 id : tempId ,
11781245 nodeId,
11791246 name : 'test.pdf' ,
1180- signers : [ { email : 'signer@example.com' , identify : ' signer@example.com' } ] ,
1247+ signers : [ { email : 'signer@example.com' , identifyMethods : [ { method : 'email' , value : ' signer@example.com', mandatory : 0 } ] } ] ,
11811248 signatureFlow : 'parallel' ,
11821249 }
11831250 store . selectedFileId = tempId
@@ -1192,15 +1259,38 @@ describe('files store - critical business rules', () => {
11921259 expect ( config . data . file ) . toEqual ( { nodeId } )
11931260 } )
11941261
1195- it ( 'includes file.fileId when file has a real positive LibreSign id' , async ( ) => {
1262+ it ( 'serializes envelope files with nodeId-based references for creation flows' , async ( ) => {
1263+ const store = useFilesStore ( )
1264+ store . selectedFileId = - 1
1265+ store . files [ - 1 ] = {
1266+ id : - 1 ,
1267+ name : 'Envelope' ,
1268+ files : [
1269+ { id : 7 , nodeId : 12345 , name : 'first.pdf' , signers : [ { email : 'ignored@example.com' } ] } ,
1270+ { id : - 22 , nodeId : 22 , name : 'second.pdf' , metadata : { d : [ { w : 100 , h : 200 } ] } } ,
1271+ ] ,
1272+ signers : [ { email : 'signer@example.com' } ] ,
1273+ signatureFlow : 'parallel' ,
1274+ }
1275+ axiosMock . mockResolvedValue ( {
1276+ data : { ocs : { data : { id : 12 , nodeId : 'real-node' , signatureFlow : 'parallel' , signers : [ ] } } } ,
1277+ } )
1278+
1279+ await store . saveOrUpdateSignatureRequest ( { } )
1280+
1281+ const config = axiosMock . mock . calls [ 0 ] [ 0 ]
1282+ expect ( config . data . files ) . toEqual ( [ { nodeId : 12345 } , { nodeId : 22 } ] )
1283+ } )
1284+
1285+ it ( 'includes file.nodeId when a known file is turned into a new request' , async ( ) => {
11961286 const store = useFilesStore ( )
11971287 // Simulate the state when LibreSign already knows about the file
11981288 // (returned by getAllFiles after the /api/v1/file POST in init.ts).
11991289 store . files [ 7 ] = {
12001290 id : 7 ,
12011291 nodeId : 12345 ,
12021292 name : 'test.pdf' ,
1203- signers : [ { email : 'signer@example.com' , identify : ' signer@example.com' } ] ,
1293+ signers : [ { email : 'signer@example.com' , identifyMethods : [ { method : 'email' , value : ' signer@example.com', mandatory : 0 } ] } ] ,
12041294 signatureFlow : 'parallel' ,
12051295 }
12061296 store . selectedFileId = 7
@@ -1212,7 +1302,7 @@ describe('files store - critical business rules', () => {
12121302 await store . saveOrUpdateSignatureRequest ( { } )
12131303
12141304 const config = axiosMock . mock . calls [ 0 ] [ 0 ]
1215- expect ( config . data . file ) . toEqual ( { fileId : 7 } )
1305+ expect ( config . data . file ) . toEqual ( { nodeId : 12345 } )
12161306 } )
12171307
12181308 it ( 'does NOT mutate the shared emptyFile when no file is selected' , ( ) => {
0 commit comments