@finsys/core 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -469,6 +469,60 @@ declare enum ExtractionFileType {
469
469
  Ic = "ic"
470
470
  }
471
471
 
472
+ /**
473
+ * IHS application status — canonical source of truth, mirroring finsys-api's
474
+ * `IHS_STATUS` enum and `allowedIhsStatus` list in src/utils/appHelper.ts.
475
+ *
476
+ * finsys-api originates these values (it writes them to the `ihs.status`
477
+ * column). Consumer apps (finhub-adonisjs, finsys-client) read them off the
478
+ * wire and use them for validation and display.
479
+ *
480
+ * Lender rejection is not represented by a distinct status: when a lender
481
+ * rejects an application, finsys-api transitions the record back to
482
+ * `APPLICATION_FINALIZED` from `LENDER_EVALUATION`.
483
+ */
484
+ declare enum IhsStatus {
485
+ CreatingApplication = "CREATING_APPLICATION",
486
+ ApplicationFinalized = "APPLICATION_FINALIZED",
487
+ LenderEvaluation = "LENDER_EVALUATION",
488
+ Approved = "APPROVED",
489
+ LouDelivered = "LOU_DELIVERED",
490
+ AwaitingDisbursement = "AWAITING_DISBURSEMENT",
491
+ Disbursement = "DISBURSEMENT",
492
+ Declined = "DECLINED",
493
+ Expired = "EXPIRED",
494
+ Canceled = "CANCELED"
495
+ }
496
+ /**
497
+ * All valid IHS status values, in display order (earliest lifecycle stage
498
+ * first, terminal states last).
499
+ */
500
+ declare const IHS_VALID_STATUSES: readonly IhsStatus[];
501
+ /**
502
+ * Terminal statuses — the application lifecycle has completed and no further
503
+ * transitions are expected. Useful for suppressing action UI (e.g. re-extract,
504
+ * status update controls) on the application detail view.
505
+ */
506
+ declare const IHS_TERMINAL_STATUSES: readonly IhsStatus[];
507
+ /**
508
+ * Failure statuses — a subset of terminal statuses that represent an
509
+ * unsuccessful outcome. Useful for badge colour / messaging.
510
+ */
511
+ declare const IHS_FAILURE_STATUSES: readonly IhsStatus[];
512
+ /**
513
+ * Type guard: narrows an unknown value to `IhsStatus` iff it is one of the
514
+ * canonical status strings.
515
+ */
516
+ declare function isValidIhsStatus(status: unknown): status is IhsStatus;
517
+ /**
518
+ * Type guard: terminal (no further transitions expected).
519
+ */
520
+ declare function isTerminalIhsStatus(status: unknown): status is IhsStatus;
521
+ /**
522
+ * Type guard: terminal and unsuccessful.
523
+ */
524
+ declare function isFailureIhsStatus(status: unknown): status is IhsStatus;
525
+
472
526
  declare enum DocExtractionStatus {
473
527
  NotUploaded = "not_uploaded",
474
528
  Uploaded = "uploaded",
@@ -504,6 +558,11 @@ interface ExtractionStatusResult {
504
558
  /**
505
559
  * Determines per-document extraction status from IHS record data.
506
560
  *
561
+ * For multi-file document types (bank statements, financial statements, EPF,
562
+ * payslips) the result contains one entry per uploaded file, indexed in the
563
+ * same order as the files appear in the IHS record's aggregate array.
564
+ * For single-file types (SSM, Form 9, IC) there is one entry.
565
+ *
507
566
  * @param ihsRecord - The IHS application record (any shape satisfying Record<string, unknown>)
508
567
  * @param jobRecords - Extraction job records for this IHS. Pass the array (even if empty)
509
568
  * when job records were fetched. Omit or pass undefined when job records are unavailable;
@@ -511,4 +570,4 @@ interface ExtractionStatusResult {
511
570
  */
512
571
  declare function resolveExtractionStatus(ihsRecord: Record<string, unknown>, jobRecords?: ExtractionJobRecord[]): ExtractionStatusResult;
513
572
 
514
- export { BASE_FIELD_SPECS, BasicFormField, type Category, type Choice, DEFAULT_VALIDATOR_DEFINITIONS, type DocExtractionResult, DocExtractionStatus, type DropdownOption, type EditorValidator, ExtractionFileType, type ExtractionJobRecord, ExtractionJobStatus, type ExtractionStatusResult, FIELD_TYPE_DEFINITIONS, type FieldData, type FieldGroup, type FieldReference, FieldType, type FileFieldTableData, type FileFieldTableItem, FileFieldTableType, FileFormField, FormField, FormFieldCategory, type FormFieldInputType, type FormFieldType, type FormFieldTypeDefinitions, FormFieldValidator, FormSpec, type FormValidatorDefinitions, type IhsDetailCategory, type IhsFieldDetail, IhsValueFormat, type PageConfig, type RHFSchemaOutput, type RHFStep, type ResolvedField, Role, type SurveyElementJSON, type SurveyJSON, type SurveyPageJSON, type UnifiedFormConfig, type Validator, applyDynamicTitles, buildFileFieldTables, evaluateExpression, extractTimePeriods, generateRHFSchema, generateSurveyJson, getBaseCategories, getBaseFieldNames, getBaseFieldSpecMap, getBaseFieldSpecs, getCategoryName, getDisplayName, getDisplayNames, getGroupDisplayNames, getPastMonthLabel, getPastYearLabel, getStepDefaultValues, getStepSchema, groupColumnsByTimePeriod, groupDetailsByCategory, groupFieldsByCategory, groupFieldsByPattern, processIhsDetails, resolveExtractionStatus, resolvePageFields, validateFormConfig, validateFormSpec, validatePagesConfig };
573
+ export { BASE_FIELD_SPECS, BasicFormField, type Category, type Choice, DEFAULT_VALIDATOR_DEFINITIONS, type DocExtractionResult, DocExtractionStatus, type DropdownOption, type EditorValidator, ExtractionFileType, type ExtractionJobRecord, ExtractionJobStatus, type ExtractionStatusResult, FIELD_TYPE_DEFINITIONS, type FieldData, type FieldGroup, type FieldReference, FieldType, type FileFieldTableData, type FileFieldTableItem, FileFieldTableType, FileFormField, FormField, FormFieldCategory, type FormFieldInputType, type FormFieldType, type FormFieldTypeDefinitions, FormFieldValidator, FormSpec, type FormValidatorDefinitions, IHS_FAILURE_STATUSES, IHS_TERMINAL_STATUSES, IHS_VALID_STATUSES, type IhsDetailCategory, type IhsFieldDetail, IhsStatus, IhsValueFormat, type PageConfig, type RHFSchemaOutput, type RHFStep, type ResolvedField, Role, type SurveyElementJSON, type SurveyJSON, type SurveyPageJSON, type UnifiedFormConfig, type Validator, applyDynamicTitles, buildFileFieldTables, evaluateExpression, extractTimePeriods, generateRHFSchema, generateSurveyJson, getBaseCategories, getBaseFieldNames, getBaseFieldSpecMap, getBaseFieldSpecs, getCategoryName, getDisplayName, getDisplayNames, getGroupDisplayNames, getPastMonthLabel, getPastYearLabel, getStepDefaultValues, getStepSchema, groupColumnsByTimePeriod, groupDetailsByCategory, groupFieldsByCategory, groupFieldsByPattern, isFailureIhsStatus, isTerminalIhsStatus, isValidIhsStatus, processIhsDetails, resolveExtractionStatus, resolvePageFields, validateFormConfig, validateFormSpec, validatePagesConfig };
package/dist/index.d.ts CHANGED
@@ -469,6 +469,60 @@ declare enum ExtractionFileType {
469
469
  Ic = "ic"
470
470
  }
471
471
 
472
+ /**
473
+ * IHS application status — canonical source of truth, mirroring finsys-api's
474
+ * `IHS_STATUS` enum and `allowedIhsStatus` list in src/utils/appHelper.ts.
475
+ *
476
+ * finsys-api originates these values (it writes them to the `ihs.status`
477
+ * column). Consumer apps (finhub-adonisjs, finsys-client) read them off the
478
+ * wire and use them for validation and display.
479
+ *
480
+ * Lender rejection is not represented by a distinct status: when a lender
481
+ * rejects an application, finsys-api transitions the record back to
482
+ * `APPLICATION_FINALIZED` from `LENDER_EVALUATION`.
483
+ */
484
+ declare enum IhsStatus {
485
+ CreatingApplication = "CREATING_APPLICATION",
486
+ ApplicationFinalized = "APPLICATION_FINALIZED",
487
+ LenderEvaluation = "LENDER_EVALUATION",
488
+ Approved = "APPROVED",
489
+ LouDelivered = "LOU_DELIVERED",
490
+ AwaitingDisbursement = "AWAITING_DISBURSEMENT",
491
+ Disbursement = "DISBURSEMENT",
492
+ Declined = "DECLINED",
493
+ Expired = "EXPIRED",
494
+ Canceled = "CANCELED"
495
+ }
496
+ /**
497
+ * All valid IHS status values, in display order (earliest lifecycle stage
498
+ * first, terminal states last).
499
+ */
500
+ declare const IHS_VALID_STATUSES: readonly IhsStatus[];
501
+ /**
502
+ * Terminal statuses — the application lifecycle has completed and no further
503
+ * transitions are expected. Useful for suppressing action UI (e.g. re-extract,
504
+ * status update controls) on the application detail view.
505
+ */
506
+ declare const IHS_TERMINAL_STATUSES: readonly IhsStatus[];
507
+ /**
508
+ * Failure statuses — a subset of terminal statuses that represent an
509
+ * unsuccessful outcome. Useful for badge colour / messaging.
510
+ */
511
+ declare const IHS_FAILURE_STATUSES: readonly IhsStatus[];
512
+ /**
513
+ * Type guard: narrows an unknown value to `IhsStatus` iff it is one of the
514
+ * canonical status strings.
515
+ */
516
+ declare function isValidIhsStatus(status: unknown): status is IhsStatus;
517
+ /**
518
+ * Type guard: terminal (no further transitions expected).
519
+ */
520
+ declare function isTerminalIhsStatus(status: unknown): status is IhsStatus;
521
+ /**
522
+ * Type guard: terminal and unsuccessful.
523
+ */
524
+ declare function isFailureIhsStatus(status: unknown): status is IhsStatus;
525
+
472
526
  declare enum DocExtractionStatus {
473
527
  NotUploaded = "not_uploaded",
474
528
  Uploaded = "uploaded",
@@ -504,6 +558,11 @@ interface ExtractionStatusResult {
504
558
  /**
505
559
  * Determines per-document extraction status from IHS record data.
506
560
  *
561
+ * For multi-file document types (bank statements, financial statements, EPF,
562
+ * payslips) the result contains one entry per uploaded file, indexed in the
563
+ * same order as the files appear in the IHS record's aggregate array.
564
+ * For single-file types (SSM, Form 9, IC) there is one entry.
565
+ *
507
566
  * @param ihsRecord - The IHS application record (any shape satisfying Record<string, unknown>)
508
567
  * @param jobRecords - Extraction job records for this IHS. Pass the array (even if empty)
509
568
  * when job records were fetched. Omit or pass undefined when job records are unavailable;
@@ -511,4 +570,4 @@ interface ExtractionStatusResult {
511
570
  */
512
571
  declare function resolveExtractionStatus(ihsRecord: Record<string, unknown>, jobRecords?: ExtractionJobRecord[]): ExtractionStatusResult;
513
572
 
514
- export { BASE_FIELD_SPECS, BasicFormField, type Category, type Choice, DEFAULT_VALIDATOR_DEFINITIONS, type DocExtractionResult, DocExtractionStatus, type DropdownOption, type EditorValidator, ExtractionFileType, type ExtractionJobRecord, ExtractionJobStatus, type ExtractionStatusResult, FIELD_TYPE_DEFINITIONS, type FieldData, type FieldGroup, type FieldReference, FieldType, type FileFieldTableData, type FileFieldTableItem, FileFieldTableType, FileFormField, FormField, FormFieldCategory, type FormFieldInputType, type FormFieldType, type FormFieldTypeDefinitions, FormFieldValidator, FormSpec, type FormValidatorDefinitions, type IhsDetailCategory, type IhsFieldDetail, IhsValueFormat, type PageConfig, type RHFSchemaOutput, type RHFStep, type ResolvedField, Role, type SurveyElementJSON, type SurveyJSON, type SurveyPageJSON, type UnifiedFormConfig, type Validator, applyDynamicTitles, buildFileFieldTables, evaluateExpression, extractTimePeriods, generateRHFSchema, generateSurveyJson, getBaseCategories, getBaseFieldNames, getBaseFieldSpecMap, getBaseFieldSpecs, getCategoryName, getDisplayName, getDisplayNames, getGroupDisplayNames, getPastMonthLabel, getPastYearLabel, getStepDefaultValues, getStepSchema, groupColumnsByTimePeriod, groupDetailsByCategory, groupFieldsByCategory, groupFieldsByPattern, processIhsDetails, resolveExtractionStatus, resolvePageFields, validateFormConfig, validateFormSpec, validatePagesConfig };
573
+ export { BASE_FIELD_SPECS, BasicFormField, type Category, type Choice, DEFAULT_VALIDATOR_DEFINITIONS, type DocExtractionResult, DocExtractionStatus, type DropdownOption, type EditorValidator, ExtractionFileType, type ExtractionJobRecord, ExtractionJobStatus, type ExtractionStatusResult, FIELD_TYPE_DEFINITIONS, type FieldData, type FieldGroup, type FieldReference, FieldType, type FileFieldTableData, type FileFieldTableItem, FileFieldTableType, FileFormField, FormField, FormFieldCategory, type FormFieldInputType, type FormFieldType, type FormFieldTypeDefinitions, FormFieldValidator, FormSpec, type FormValidatorDefinitions, IHS_FAILURE_STATUSES, IHS_TERMINAL_STATUSES, IHS_VALID_STATUSES, type IhsDetailCategory, type IhsFieldDetail, IhsStatus, IhsValueFormat, type PageConfig, type RHFSchemaOutput, type RHFStep, type ResolvedField, Role, type SurveyElementJSON, type SurveyJSON, type SurveyPageJSON, type UnifiedFormConfig, type Validator, applyDynamicTitles, buildFileFieldTables, evaluateExpression, extractTimePeriods, generateRHFSchema, generateSurveyJson, getBaseCategories, getBaseFieldNames, getBaseFieldSpecMap, getBaseFieldSpecs, getCategoryName, getDisplayName, getDisplayNames, getGroupDisplayNames, getPastMonthLabel, getPastYearLabel, getStepDefaultValues, getStepSchema, groupColumnsByTimePeriod, groupDetailsByCategory, groupFieldsByCategory, groupFieldsByPattern, isFailureIhsStatus, isTerminalIhsStatus, isValidIhsStatus, processIhsDetails, resolveExtractionStatus, resolvePageFields, validateFormConfig, validateFormSpec, validatePagesConfig };
package/dist/index.js CHANGED
@@ -9495,9 +9495,12 @@ function processIhsDetails(ihsData) {
9495
9495
  const specs = getBaseFieldSpecs();
9496
9496
  const fileColumnSet = /* @__PURE__ */ new Set();
9497
9497
  for (const field of specs) {
9498
- if (field.type === "file" && field.ihs_column_names) {
9499
- for (const col of field.ihs_column_names) {
9500
- fileColumnSet.add(col);
9498
+ if (field.type === "file") {
9499
+ if (field.name) fileColumnSet.add(field.name);
9500
+ if (field.ihs_column_names) {
9501
+ for (const col of field.ihs_column_names) {
9502
+ fileColumnSet.add(col);
9503
+ }
9501
9504
  }
9502
9505
  }
9503
9506
  }
@@ -9570,6 +9573,53 @@ var ExtractionFileType = /* @__PURE__ */ ((ExtractionFileType2) => {
9570
9573
  return ExtractionFileType2;
9571
9574
  })(ExtractionFileType || {});
9572
9575
 
9576
+ // src/ihs-status.ts
9577
+ var IhsStatus = /* @__PURE__ */ ((IhsStatus2) => {
9578
+ IhsStatus2["CreatingApplication"] = "CREATING_APPLICATION";
9579
+ IhsStatus2["ApplicationFinalized"] = "APPLICATION_FINALIZED";
9580
+ IhsStatus2["LenderEvaluation"] = "LENDER_EVALUATION";
9581
+ IhsStatus2["Approved"] = "APPROVED";
9582
+ IhsStatus2["LouDelivered"] = "LOU_DELIVERED";
9583
+ IhsStatus2["AwaitingDisbursement"] = "AWAITING_DISBURSEMENT";
9584
+ IhsStatus2["Disbursement"] = "DISBURSEMENT";
9585
+ IhsStatus2["Declined"] = "DECLINED";
9586
+ IhsStatus2["Expired"] = "EXPIRED";
9587
+ IhsStatus2["Canceled"] = "CANCELED";
9588
+ return IhsStatus2;
9589
+ })(IhsStatus || {});
9590
+ var IHS_VALID_STATUSES = [
9591
+ "CREATING_APPLICATION" /* CreatingApplication */,
9592
+ "APPLICATION_FINALIZED" /* ApplicationFinalized */,
9593
+ "LENDER_EVALUATION" /* LenderEvaluation */,
9594
+ "APPROVED" /* Approved */,
9595
+ "LOU_DELIVERED" /* LouDelivered */,
9596
+ "AWAITING_DISBURSEMENT" /* AwaitingDisbursement */,
9597
+ "DISBURSEMENT" /* Disbursement */,
9598
+ "DECLINED" /* Declined */,
9599
+ "EXPIRED" /* Expired */,
9600
+ "CANCELED" /* Canceled */
9601
+ ];
9602
+ var IHS_TERMINAL_STATUSES = [
9603
+ "DISBURSEMENT" /* Disbursement */,
9604
+ "DECLINED" /* Declined */,
9605
+ "EXPIRED" /* Expired */,
9606
+ "CANCELED" /* Canceled */
9607
+ ];
9608
+ var IHS_FAILURE_STATUSES = [
9609
+ "DECLINED" /* Declined */,
9610
+ "EXPIRED" /* Expired */,
9611
+ "CANCELED" /* Canceled */
9612
+ ];
9613
+ function isValidIhsStatus(status) {
9614
+ return typeof status === "string" && IHS_VALID_STATUSES.includes(status);
9615
+ }
9616
+ function isTerminalIhsStatus(status) {
9617
+ return typeof status === "string" && IHS_TERMINAL_STATUSES.includes(status);
9618
+ }
9619
+ function isFailureIhsStatus(status) {
9620
+ return typeof status === "string" && IHS_FAILURE_STATUSES.includes(status);
9621
+ }
9622
+
9573
9623
  // src/extraction-status.ts
9574
9624
  var DocExtractionStatus = /* @__PURE__ */ ((DocExtractionStatus2) => {
9575
9625
  DocExtractionStatus2["NotUploaded"] = "not_uploaded";
@@ -9599,59 +9649,45 @@ function isPopulated(value) {
9599
9649
  if (value === "Not Specified") return false;
9600
9650
  return true;
9601
9651
  }
9602
- function hasUploadedFile(fields, ihsRecord) {
9603
- return fields.some((field) => {
9604
- if (!field.name) return false;
9605
- return isPopulated(ihsRecord[field.name]);
9606
- });
9607
- }
9608
- function getExtractionColumns(fields) {
9609
- const columns = [];
9610
- for (const field of fields) {
9611
- if (field.ihs_column_names) {
9612
- columns.push(...field.ihs_column_names);
9652
+ function countUploadedFiles(ihsRecord, aggregateKey) {
9653
+ const raw = ihsRecord[aggregateKey];
9654
+ if (!raw) return 0;
9655
+ if (typeof raw === "string") {
9656
+ try {
9657
+ const parsed = JSON.parse(raw);
9658
+ if (Array.isArray(parsed)) return parsed.length;
9659
+ } catch {
9660
+ return 1;
9613
9661
  }
9614
9662
  }
9615
- return columns;
9663
+ if (Array.isArray(raw)) return raw.length;
9664
+ return 1;
9616
9665
  }
9617
9666
  function getPopulatedColumns(columns, ihsRecord) {
9618
9667
  return columns.filter((col) => isPopulated(ihsRecord[col]));
9619
9668
  }
9620
- function resolveExtractionStatus(ihsRecord, jobRecords) {
9621
- const documents = [];
9622
- for (const fileType of Object.values(ExtractionFileType)) {
9623
- const groupName = FILE_TYPE_TO_GROUP[fileType];
9624
- const fields = _grouped[groupName] ?? [];
9625
- const displayName = _groupDisplayNames[groupName] ?? fileType;
9626
- const uploaded = fields.length > 0 && hasUploadedFile(fields, ihsRecord);
9627
- const allColumns = getExtractionColumns(fields);
9628
- const populated = getPopulatedColumns(allColumns, ihsRecord);
9629
- const hasExtractedData = populated.length > 0;
9630
- let status;
9631
- let errorMessage;
9632
- if (!uploaded) {
9633
- status = "not_uploaded" /* NotUploaded */;
9634
- } else if (jobRecords) {
9635
- const job = jobRecords.find((j) => j.fileType === fileType);
9636
- if (job) {
9637
- if (job.status === "queued" /* Queued */) {
9638
- status = "queued" /* Queued */;
9639
- } else if (job.status === "processing" /* Processing */) {
9640
- status = "processing" /* Processing */;
9641
- } else if (job.status === "failed" /* Failed */) {
9642
- if (hasExtractedData) {
9643
- status = "extracted" /* Extracted */;
9644
- errorMessage = job.errorMessage;
9645
- } else {
9646
- status = "failed" /* Failed */;
9647
- errorMessage = job.errorMessage;
9648
- }
9649
- } else if (job.status === "succeeded" /* Succeeded */ || hasExtractedData) {
9669
+ function resolveDocStatus(columns, ihsRecord, uploaded, jobRecord, hasJobRecords) {
9670
+ const populated = getPopulatedColumns(columns, ihsRecord);
9671
+ const hasExtractedData = populated.length > 0;
9672
+ let status;
9673
+ let errorMessage;
9674
+ if (!uploaded) {
9675
+ status = "not_uploaded" /* NotUploaded */;
9676
+ } else if (hasJobRecords) {
9677
+ if (jobRecord) {
9678
+ if (jobRecord.status === "queued" /* Queued */) {
9679
+ status = "queued" /* Queued */;
9680
+ } else if (jobRecord.status === "processing" /* Processing */) {
9681
+ status = "processing" /* Processing */;
9682
+ } else if (jobRecord.status === "failed" /* Failed */) {
9683
+ if (hasExtractedData) {
9650
9684
  status = "extracted" /* Extracted */;
9685
+ errorMessage = jobRecord.errorMessage;
9651
9686
  } else {
9652
- status = "uploaded" /* Uploaded */;
9687
+ status = "failed" /* Failed */;
9688
+ errorMessage = jobRecord.errorMessage;
9653
9689
  }
9654
- } else if (hasExtractedData) {
9690
+ } else if (jobRecord.status === "succeeded" /* Succeeded */ || hasExtractedData) {
9655
9691
  status = "extracted" /* Extracted */;
9656
9692
  } else {
9657
9693
  status = "uploaded" /* Uploaded */;
@@ -9659,16 +9695,68 @@ function resolveExtractionStatus(ihsRecord, jobRecords) {
9659
9695
  } else if (hasExtractedData) {
9660
9696
  status = "extracted" /* Extracted */;
9661
9697
  } else {
9662
- status = "unknown" /* Unknown */;
9698
+ status = "uploaded" /* Uploaded */;
9699
+ }
9700
+ } else if (hasExtractedData) {
9701
+ status = "extracted" /* Extracted */;
9702
+ } else {
9703
+ status = "unknown" /* Unknown */;
9704
+ }
9705
+ return { status, errorMessage };
9706
+ }
9707
+ function resolveExtractionStatus(ihsRecord, jobRecords) {
9708
+ const documents = [];
9709
+ const hasJobRecords = jobRecords !== void 0;
9710
+ for (const fileType of Object.values(ExtractionFileType)) {
9711
+ const groupName = FILE_TYPE_TO_GROUP[fileType];
9712
+ const fields = _grouped[groupName] ?? [];
9713
+ const displayName = _groupDisplayNames[groupName] ?? fileType;
9714
+ const uploadedCount = countUploadedFiles(ihsRecord, fileType);
9715
+ const isUploaded = uploadedCount > 0 || fields.some((f) => f.name && isPopulated(ihsRecord[f.name]));
9716
+ const matchingJobs = jobRecords?.filter((j) => j.fileType === fileType) ?? [];
9717
+ if (fields.length <= 1) {
9718
+ const field = fields[0];
9719
+ const columns = field?.ihs_column_names ?? [];
9720
+ const populated = getPopulatedColumns(columns, ihsRecord);
9721
+ const { status, errorMessage } = resolveDocStatus(
9722
+ columns,
9723
+ ihsRecord,
9724
+ isUploaded,
9725
+ matchingJobs[0],
9726
+ hasJobRecords
9727
+ );
9728
+ documents.push({
9729
+ fileType,
9730
+ status,
9731
+ displayName,
9732
+ populatedColumns: populated,
9733
+ totalColumns: columns.length,
9734
+ errorMessage
9735
+ });
9736
+ } else {
9737
+ const count = Math.max(uploadedCount, 1);
9738
+ for (let idx = 0; idx < count; idx++) {
9739
+ const field = fields[idx];
9740
+ const columns = field?.ihs_column_names ?? [];
9741
+ const populated = getPopulatedColumns(columns, ihsRecord);
9742
+ const fileUploaded = idx < uploadedCount;
9743
+ const { status, errorMessage } = resolveDocStatus(
9744
+ columns,
9745
+ ihsRecord,
9746
+ fileUploaded,
9747
+ matchingJobs[idx],
9748
+ hasJobRecords
9749
+ );
9750
+ documents.push({
9751
+ fileType,
9752
+ status,
9753
+ displayName,
9754
+ populatedColumns: populated,
9755
+ totalColumns: columns.length,
9756
+ errorMessage
9757
+ });
9758
+ }
9663
9759
  }
9664
- documents.push({
9665
- fileType,
9666
- status,
9667
- displayName,
9668
- populatedColumns: populated,
9669
- totalColumns: allColumns.length,
9670
- errorMessage
9671
- });
9672
9760
  }
9673
9761
  const summary = documents.reduce(
9674
9762
  (acc, d) => {
@@ -9697,6 +9785,10 @@ export {
9697
9785
  FormFieldCategory,
9698
9786
  FormFieldValidator,
9699
9787
  FormSpec,
9788
+ IHS_FAILURE_STATUSES,
9789
+ IHS_TERMINAL_STATUSES,
9790
+ IHS_VALID_STATUSES,
9791
+ IhsStatus,
9700
9792
  IhsValueFormat,
9701
9793
  Role,
9702
9794
  applyDynamicTitles,
@@ -9721,6 +9813,9 @@ export {
9721
9813
  groupDetailsByCategory,
9722
9814
  groupFieldsByCategory,
9723
9815
  groupFieldsByPattern,
9816
+ isFailureIhsStatus,
9817
+ isTerminalIhsStatus,
9818
+ isValidIhsStatus,
9724
9819
  processIhsDetails,
9725
9820
  resolveExtractionStatus,
9726
9821
  resolvePageFields,