@zeniai/client-epic-state 5.1.37 → 5.1.38

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.
Files changed (25) hide show
  1. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  2. package/lib/entity/snackbar/snackbarTypes.js +2 -0
  3. package/lib/esm/entity/snackbar/snackbarTypes.js +2 -0
  4. package/lib/esm/index.js +3 -2
  5. package/lib/esm/view/expenseAutomationView/epics/accountRecon/updateStatementInfoEpic.js +5 -3
  6. package/lib/esm/view/expenseAutomationView/helpers/mapStatementUpdateLocalDataToRequestBody.js +32 -0
  7. package/lib/esm/view/expenseAutomationView/payload/reconciliationPayload.js +1 -0
  8. package/lib/esm/view/expenseAutomationView/reducers/reconciliationViewReducer.js +163 -21
  9. package/lib/esm/view/expenseAutomationView/selectors/reconciliationViewSelector.js +30 -26
  10. package/lib/esm/view/expenseAutomationView/selectors/statementParseViewSelector.js +295 -0
  11. package/lib/index.d.ts +4 -3
  12. package/lib/index.js +44 -38
  13. package/lib/view/expenseAutomationView/epics/accountRecon/updateStatementInfoEpic.js +4 -2
  14. package/lib/view/expenseAutomationView/helpers/mapStatementUpdateLocalDataToRequestBody.d.ts +2 -0
  15. package/lib/view/expenseAutomationView/helpers/mapStatementUpdateLocalDataToRequestBody.js +36 -0
  16. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  17. package/lib/view/expenseAutomationView/payload/reconciliationPayload.js +1 -0
  18. package/lib/view/expenseAutomationView/reducers/reconciliationViewReducer.d.ts +14 -1
  19. package/lib/view/expenseAutomationView/reducers/reconciliationViewReducer.js +165 -23
  20. package/lib/view/expenseAutomationView/selectorTypes/reconciliationViewSelectorTypes.d.ts +10 -2
  21. package/lib/view/expenseAutomationView/selectors/reconciliationViewSelector.js +30 -26
  22. package/lib/view/expenseAutomationView/selectors/statementParseViewSelector.d.ts +25 -0
  23. package/lib/view/expenseAutomationView/selectors/statementParseViewSelector.js +313 -0
  24. package/lib/view/expenseAutomationView/types/reconciliationViewState.d.ts +52 -2
  25. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- export declare const toSnackbarMessageSection: (v: string) => "common" | "transactionDetails_updatingPastTransactions" | "transactionDetails_transactionUpdated" | "cockpit_month_end_email_sent" | "cockpit_month_end_email_save" | "cockpit_month_end_email_attachment_upload" | "je_bill_link" | "je_posted" | "audit_score_updated" | "people_invite_people" | "people_invite_multiple_people" | "people_delete_person" | "people_update_person" | "reimbursement_updated" | "reimbursement_deleted" | "reimbursement_cancelled_deleted" | "reimbursement_cancelled" | "reimbursement_approved" | "reimbursement_rejected" | "reimbursement_sent_for_approval" | "account_added" | "business_verification_save" | "business_verification_submit" | "business_verification_from_bills_submit" | "plaid_connection" | "unlink_deposit_account" | "reimbursement_setup" | "reimbursement_approval_create" | "reimbursement_duplicate_approval_create" | "reimbursement_approval_update" | "reimbursement_duplicate_approval_update" | "reimbursement_approval_delete" | "reimbursement_approval_reorder" | "reimbursement_create_mileage" | "reimbursement_update_mileage" | "reimbursement_accept_term" | "reimbursement_accept_employee_term" | "reimbursement_bulk_submit" | "reimbursement_bulk_processed" | "bill_pay_setup" | "zeni_accounts_setup" | "bill_pay_approval_create" | "bill_pay_duplicate_approval_create" | "bill_pay_approval_update" | "bill_pay_duplicate_approval_update" | "bill_pay_approval_delete" | "bill_pay_approval_reorder" | "bill_pay_updated" | "bill_pay_deleted" | "bill_pay_cancelled_deleted" | "bill_pay_cancelled" | "bill_pay_approved" | "real_time_approver_added" | "bill_pay_rejected" | "bill_pay_sent_for_approval" | "bill_pay_accept_term" | "bill_pay_bulk_submit" | "bill_pay_bulk_processed" | "bill_pay_refund" | "bill_pay_retry" | "bill_pay_marked_as_paid" | "zeni_account_accept_term" | "update_vendor" | "data_refresh_update" | "delete_bank_account" | "create_bank_account" | "create_bank_account_ach" | "create_bank_account_wire" | "create_bank_account_international" | "transfer_money" | "update_zeni_account_nickname" | "create_checking_account" | "deposit_check" | "create_vendor" | "onboarding_customer_view" | "onboarding_customer_view_complete" | "onboarding_customer_identity_verification_save" | "onboarding_customer_identity_verification_submit" | "onboarding_customer_business_verification_save" | "onboarding_customer_business_verification_submit" | "retry_bank_account_connection" | "dashboard_invite_sent" | "onboarding_info_saved" | "approve_original_merchant" | "approve_global_merchant" | "reject_global_merchant" | "create_global_merchant" | "fetch_global_merchant_no_recommendation" | "save_vendor_renamed" | "save_vendor_sent_for_review" | "save_vendor_marked_as_employee" | "save_vendor_marked_as_local_contractor" | "charge_card_setup" | "charge_card_accept_term" | "charge_card_receipt_upload" | "charge_card_resend_invite" | "charge_card_revoke_invite" | "card_policy_created" | "card_policy_updated" | "card_policy_deleted" | "charge_card_update_limit" | "charge_cards_update_limit" | "close_charge_card" | "lock_charge_card" | "lock_charge_card_card_user" | "lock_charge_cards" | "unlock_charge_card" | "unlock_charge_card_card_user" | "unlock_charge_cards" | "close_charge_cards" | "revoke_invite_charge_cards" | "charge_card_express_interest" | "create_schedule" | "save_schedule" | "delete_schedule" | "ignore_schedule" | "save_task_detail" | "fetch_task_detail" | "delete_task" | "archive_task" | "snooze_task" | "unsnooze_task" | "create_tag" | "delete_tag" | "update_charge_card" | "update_charge_card_name" | "update_charge_card_name_card_user" | "issue_charge_card" | "issue_charge_cards" | "notification_settings_saved" | "referral_invite_sent" | "notification_mark_as_read" | "mark_as_complete_schedule" | "cancel_journal_entry" | "settings_accounting_accounts_updated" | "create_card_setup" | "confirm_card_setup" | "add_card_payment_source" | "fetch_payment_sources" | "task_assigned_toast_notification" | "task_due_tomorrow_toast_notification" | "task_deleted_toast_notification" | "task_archived_toast_notification" | "task_overdue_toast_notification" | "task_overdue_toast_notification_creator" | "task_notification_count" | "task_activities_toast_notification" | "task_created_toast_notification" | "task_group_creation_success" | "task_group_deletion_success" | "task_group_update_success" | "primary_funding_account_updated" | "task_time_spent_validation" | "missing_receipts_attachment" | "flux_analysis_unreviewed" | "flux_analysis_reviewed" | "receipt_match" | "receipts_upload" | "receipts_bulk_match" | "billing_address_view" | "express_pay_submit" | "exclude_transaction" | "reconcile" | "save_reconcile_for_later" | "recon_transaction_categorize" | "recon_transaction_match" | "update_debit_card_pin_attempt" | "set_debit_card_pin" | "ai_cfo_create_session_and_submit" | "ai_cfo_chat_session_deleted" | "charge_card_auto_pay_enable" | "charge_card_auto_pay_disable" | "treasury_setup" | "treasury_accept_term" | "treasury_transfer_money" | "treasury_transfer_money_failed" | "auto_transfer_rule_create" | "auto_transfer_rule_update" | "auto_transfer_rule_delete" | "auto_transfer_rule_pause" | "auto_transfer_rule_resume" | "treasury_update_portfolio_allocation" | "send_email_magic_link_to_user" | "complete_profile_done" | "create_transfer_entry" | "reports_resync" | "invalid_phone_number" | "transactions_categorized_updated_failed" | "transaction_categorized_updated_failed" | "transactionsCategorized_transactionsUpdated" | "transactionsCategorized_transactionUpdated" | "transactionCategorized_transactionsUpdated" | "transactionCategorized_transactionUpdated" | "transactionsCategorized_transactionsFailed" | "transactionsCategorized_transactionFailed" | "transactionCategorized_transactionsFailed" | "transactionCategorized_transactionFailed" | "transactionsUpdated_transactionsFailed" | "transactionsUpdated_transactionFailed" | "transactionUpdated_transactionsFailed" | "transactionUpdated_transactionFailed" | "transactionsCategorized_transactionsUpdated_transactionsFailed" | "transactionsCategorized_transactionsUpdated_transactionFailed" | "transactionsCategorized_transactionUpdated_transactionsFailed" | "transactionCategorized_transactionsUpdated_transactionsFailed" | "transactionsCategorized_transactionUpdated_transactionFailed" | "transactionCategorized_transactionsUpdated_transactionFailed" | "transactionCategorized_transactionUpdated_transactionsFailed" | "transactionCategorized_transactionUpdated_transactionFailed" | "accounting_classes_enabled_update" | "capitalization_threshold_update" | "account_excluded_from_reconciliation" | "account_included_in_reconciliation" | "ai_accountant_trigger_job" | "oauth_consent_approve" | "oauth_consent_invalid_request" | "credit_agent_update_profile" | "credit_agent_save_macro" | "credit_agent_schedule_cron";
1
+ export declare const toSnackbarMessageSection: (v: string) => "common" | "transactionDetails_updatingPastTransactions" | "transactionDetails_transactionUpdated" | "cockpit_month_end_email_sent" | "cockpit_month_end_email_save" | "cockpit_month_end_email_attachment_upload" | "je_bill_link" | "je_posted" | "audit_score_updated" | "people_invite_people" | "people_invite_multiple_people" | "people_delete_person" | "people_update_person" | "reimbursement_updated" | "reimbursement_deleted" | "reimbursement_cancelled_deleted" | "reimbursement_cancelled" | "reimbursement_approved" | "reimbursement_rejected" | "reimbursement_sent_for_approval" | "account_added" | "business_verification_save" | "business_verification_submit" | "business_verification_from_bills_submit" | "plaid_connection" | "unlink_deposit_account" | "reimbursement_setup" | "reimbursement_approval_create" | "reimbursement_duplicate_approval_create" | "reimbursement_approval_update" | "reimbursement_duplicate_approval_update" | "reimbursement_approval_delete" | "reimbursement_approval_reorder" | "reimbursement_create_mileage" | "reimbursement_update_mileage" | "reimbursement_accept_term" | "reimbursement_accept_employee_term" | "reimbursement_bulk_submit" | "reimbursement_bulk_processed" | "bill_pay_setup" | "zeni_accounts_setup" | "bill_pay_approval_create" | "bill_pay_duplicate_approval_create" | "bill_pay_approval_update" | "bill_pay_duplicate_approval_update" | "bill_pay_approval_delete" | "bill_pay_approval_reorder" | "bill_pay_updated" | "bill_pay_deleted" | "bill_pay_cancelled_deleted" | "bill_pay_cancelled" | "bill_pay_approved" | "real_time_approver_added" | "bill_pay_rejected" | "bill_pay_sent_for_approval" | "bill_pay_accept_term" | "bill_pay_bulk_submit" | "bill_pay_bulk_processed" | "bill_pay_refund" | "bill_pay_retry" | "bill_pay_marked_as_paid" | "zeni_account_accept_term" | "update_vendor" | "data_refresh_update" | "delete_bank_account" | "create_bank_account" | "create_bank_account_ach" | "create_bank_account_wire" | "create_bank_account_international" | "transfer_money" | "update_zeni_account_nickname" | "create_checking_account" | "deposit_check" | "create_vendor" | "onboarding_customer_view" | "onboarding_customer_view_complete" | "onboarding_customer_identity_verification_save" | "onboarding_customer_identity_verification_submit" | "onboarding_customer_business_verification_save" | "onboarding_customer_business_verification_submit" | "retry_bank_account_connection" | "dashboard_invite_sent" | "onboarding_info_saved" | "approve_original_merchant" | "approve_global_merchant" | "reject_global_merchant" | "create_global_merchant" | "fetch_global_merchant_no_recommendation" | "save_vendor_renamed" | "save_vendor_sent_for_review" | "save_vendor_marked_as_employee" | "save_vendor_marked_as_local_contractor" | "charge_card_setup" | "charge_card_accept_term" | "charge_card_receipt_upload" | "charge_card_resend_invite" | "charge_card_revoke_invite" | "card_policy_created" | "card_policy_updated" | "card_policy_deleted" | "charge_card_update_limit" | "charge_cards_update_limit" | "close_charge_card" | "lock_charge_card" | "lock_charge_card_card_user" | "lock_charge_cards" | "unlock_charge_card" | "unlock_charge_card_card_user" | "unlock_charge_cards" | "close_charge_cards" | "revoke_invite_charge_cards" | "charge_card_express_interest" | "create_schedule" | "save_schedule" | "delete_schedule" | "ignore_schedule" | "save_task_detail" | "fetch_task_detail" | "delete_task" | "archive_task" | "snooze_task" | "unsnooze_task" | "create_tag" | "delete_tag" | "update_charge_card" | "update_charge_card_name" | "update_charge_card_name_card_user" | "issue_charge_card" | "issue_charge_cards" | "notification_settings_saved" | "referral_invite_sent" | "notification_mark_as_read" | "mark_as_complete_schedule" | "cancel_journal_entry" | "settings_accounting_accounts_updated" | "create_card_setup" | "confirm_card_setup" | "add_card_payment_source" | "fetch_payment_sources" | "task_assigned_toast_notification" | "task_due_tomorrow_toast_notification" | "task_deleted_toast_notification" | "task_archived_toast_notification" | "task_overdue_toast_notification" | "task_overdue_toast_notification_creator" | "task_notification_count" | "task_activities_toast_notification" | "task_created_toast_notification" | "task_group_creation_success" | "task_group_deletion_success" | "task_group_update_success" | "primary_funding_account_updated" | "task_time_spent_validation" | "missing_receipts_attachment" | "flux_analysis_unreviewed" | "flux_analysis_reviewed" | "receipt_match" | "receipts_upload" | "receipts_bulk_match" | "billing_address_view" | "express_pay_submit" | "exclude_transaction" | "reconcile" | "save_reconcile_for_later" | "recon_transaction_categorize" | "recon_transaction_match" | "update_debit_card_pin_attempt" | "set_debit_card_pin" | "ai_cfo_create_session_and_submit" | "ai_cfo_chat_session_deleted" | "charge_card_auto_pay_enable" | "charge_card_auto_pay_disable" | "treasury_setup" | "treasury_accept_term" | "treasury_transfer_money" | "treasury_transfer_money_failed" | "auto_transfer_rule_create" | "auto_transfer_rule_update" | "auto_transfer_rule_delete" | "auto_transfer_rule_pause" | "auto_transfer_rule_resume" | "treasury_update_portfolio_allocation" | "send_email_magic_link_to_user" | "complete_profile_done" | "create_transfer_entry" | "reports_resync" | "invalid_phone_number" | "transactions_categorized_updated_failed" | "transaction_categorized_updated_failed" | "transactionsCategorized_transactionsUpdated" | "transactionsCategorized_transactionUpdated" | "transactionCategorized_transactionsUpdated" | "transactionCategorized_transactionUpdated" | "transactionsCategorized_transactionsFailed" | "transactionsCategorized_transactionFailed" | "transactionCategorized_transactionsFailed" | "transactionCategorized_transactionFailed" | "transactionsUpdated_transactionsFailed" | "transactionsUpdated_transactionFailed" | "transactionUpdated_transactionsFailed" | "transactionUpdated_transactionFailed" | "transactionsCategorized_transactionsUpdated_transactionsFailed" | "transactionsCategorized_transactionsUpdated_transactionFailed" | "transactionsCategorized_transactionUpdated_transactionsFailed" | "transactionCategorized_transactionsUpdated_transactionsFailed" | "transactionsCategorized_transactionUpdated_transactionFailed" | "transactionCategorized_transactionsUpdated_transactionFailed" | "transactionCategorized_transactionUpdated_transactionsFailed" | "transactionCategorized_transactionUpdated_transactionFailed" | "accounting_classes_enabled_update" | "capitalization_threshold_update" | "account_excluded_from_reconciliation" | "account_included_in_reconciliation" | "statement_parsing_in_progress" | "statement_parsing_complete" | "ai_accountant_trigger_job" | "oauth_consent_approve" | "oauth_consent_invalid_request" | "credit_agent_update_profile" | "credit_agent_save_macro" | "credit_agent_schedule_cron";
2
2
  export type SnackbarMessageSections = ReturnType<typeof toSnackbarMessageSection>;
3
3
  export declare const toSnackbarMessageSectionTexts: (v: string) => "notification" | "failed" | "success";
4
4
  export type SnackbarMessageSectionTexts = ReturnType<typeof toSnackbarMessageSectionTexts>;
@@ -216,6 +216,8 @@ const ALL_SNACKBAR_MESSAGE_SECTIONS = [
216
216
  'capitalization_threshold_update',
217
217
  'account_excluded_from_reconciliation',
218
218
  'account_included_in_reconciliation',
219
+ 'statement_parsing_in_progress',
220
+ 'statement_parsing_complete',
219
221
  'ai_accountant_trigger_job',
220
222
  'oauth_consent_approve',
221
223
  'oauth_consent_invalid_request',
@@ -213,6 +213,8 @@ const ALL_SNACKBAR_MESSAGE_SECTIONS = [
213
213
  'capitalization_threshold_update',
214
214
  'account_excluded_from_reconciliation',
215
215
  'account_included_in_reconciliation',
216
+ 'statement_parsing_in_progress',
217
+ 'statement_parsing_complete',
216
218
  'ai_accountant_trigger_job',
217
219
  'oauth_consent_approve',
218
220
  'oauth_consent_invalid_request',
package/lib/esm/index.js CHANGED
@@ -173,10 +173,11 @@ import { MAX_SELECTION_LIMIT, checkIfAllLineItemsAreCategoryClassFilled, getLine
173
173
  import { clearExpenseAutomationFluxAnalysisView, fetchFluxAnalysisView, reviewFluxAnalysisView, updateFluxAnalysisViewPageMetaData, updateFluxAnalysisViewUIState, updateOperatingExpensesIdsForReview, updateSelectedSectionIdsForReview, } from './view/expenseAutomationView/reducers/fluxAnalysisViewReducer';
174
174
  import { clearJeScheduleLocalData as clearExpenseAutomationJEScheduleLocalData, clearExpenseAutomationJESchedulesView, fetchJeSchedulesPage as fetchExpenseAutomationJESchedulesPage, ignoreRecommendedJeSchedule as ignoreExpenseAutomationJESchedule, initializeAccountSettingsView as initializeJeAccountSettingsView, initializeJeScheduleLocalData, removeJeScheduleTransactionKey, retryJeSchedule as retryExpenseAutomationJESchedule, saveAccountSettings as saveJeAccountSettings, saveAccountSettingsLocalData as saveJeAccountSettingsLocalData, updateJESchedulesUIState as updateExpenseAutomationJESchedulesUIState, updateJeScheduleLocalDataById, updateJeScheduleTransactionKeys, } from './view/expenseAutomationView/reducers/jeSchedulesViewReducer';
175
175
  import { acknowledgeBulkUploadConfirmMatchComplete, bulkUploadAutomatchingTimedOut, bulkUploadReceipts, bulkUploadReceiptsFailure, bulkUploadReceiptsSuccess, clearBulkUpload, clearBulkUploadBatchDetailsForScopeChange, clearManualSearchResults, clearMissingReceiptsTabNavigation, confirmBulkUploadMatch, confirmBulkUploadMatchFailure, confirmBulkUploadMatchSuccess, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsFailure, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatches, fetchBulkUploadBatchesFailure, fetchBulkUploadBatchesSuccess, fetchCompletedTransactions, fetchCompletedTransactionsFailure, fetchCompletedTransactionsSuccess, fetchMissingReceipts as fetchExpenseAutomationMissingReceipts, fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, markBatchDetailRefreshAttempted, markMissingReceiptAsDone as markExpenseAutomationMissingReceiptAsDone, pusherBatchStatusUpdate, refreshBatchDetailsForBatchId, requestMissingReceiptsTabNavigation, restoreBulkUploadAutomatchingOnMount, restoreBulkUploadMatchingState, searchTransactionsForManualMatch, searchTransactionsForManualMatchFailure, searchTransactionsForManualMatchSuccess, setBulkUploadCompletedSubTab, setBulkUploadResultsTab, setBulkUploadSortConfig, setBulkUploadUploadedFileCount, storeBatchDetails, updateBulkUploadProgress, updateMissingReceiptUploadState as updateExpenseAutomationMissingReceiptUploadState, updateMissingReceiptsUIState as updateExpenseAutomationMissingReceiptsUIState, uploadMissingReceiptSuccess as uploadExpenseAutomationMissingReceiptSuccess, } from './view/expenseAutomationView/reducers/missingReceiptsViewReducer';
176
- import { clearStatementDateConflict, deleteAccountStatement, excludeAccountFromReconciliation, fetchReconciliation as fetchReconciliationView, includeAccountInReconciliation, parseStatement, parseStatementFailure, parseStatementSuccess, reparseStatement, reparseStatementFailure, reparseStatementSuccess, resetReparseStatementStatus, saveReconciliationDetail as saveExpenseAutomationReconciliationDetail, saveReconciliationReview as saveExpenseAutomationReconciliationReview, setConnectionInProgressForAccount as setConnectionInProgressForAccountReconciliation, setStatementParseInProgress, submitStatementUpdate, updateAccountReconciliationLocalData as updateExpenseAutomationAccountReconciliationLocalData, updateSelectedAccountId as updateExpenseAutomationAccountReconciliationSelectedAccountId, updateSelectedTab as updateExpenseAutomationAccountReconciliationSelectedTab, updateReconListScrollPosition as updateExpenseAutomationReconListScrollPosition, updateReviewTabSortState as updateExpenseAutomationReconReviewTabListSortState, updateReviewTabLocalData as updateExpenseAutomationReconReviewTabLocalData, updateReconcileTabListScrollState as updateExpenseAutomationReconcileTabListScrollState, updateReconcileTabListSortState as updateExpenseAutomationReconcileTabListSortState, updateReconcileTabLocalData as updateExpenseAutomationReconcileTabLocalData, updateSelectedDrawerAccountId as updateExpenseAutomationSelectedDrawerAccountId, updateNodeCollapseState, updateParsedStatementData, updateStatementProcessingFailed, updateStatementUpdateLocalData, updateStatementUploadChosen, uploadAccountStatement, } from './view/expenseAutomationView/reducers/reconciliationViewReducer';
176
+ import { clearStatementDateConflict, deleteAccountStatement, excludeAccountFromReconciliation, fetchReconciliation as fetchReconciliationView, includeAccountInReconciliation, parseStatement, parseStatementFailure, parseStatementSuccess, reparseStatement, reparseStatementFailure, reparseStatementSuccess, resetReparseStatementStatus, saveReconciliationDetail as saveExpenseAutomationReconciliationDetail, saveReconciliationReview as saveExpenseAutomationReconciliationReview, setConnectionInProgressForAccount as setConnectionInProgressForAccountReconciliation, setStatementParseInProgress, setStatementProcessingDismissedToBackground, submitStatementUpdate, updateAccountReconciliationLocalData as updateExpenseAutomationAccountReconciliationLocalData, updateSelectedAccountId as updateExpenseAutomationAccountReconciliationSelectedAccountId, updateSelectedTab as updateExpenseAutomationAccountReconciliationSelectedTab, updateReconListScrollPosition as updateExpenseAutomationReconListScrollPosition, updateReviewTabSortState as updateExpenseAutomationReconReviewTabListSortState, updateReviewTabLocalData as updateExpenseAutomationReconReviewTabLocalData, updateReconcileTabListScrollState as updateExpenseAutomationReconcileTabListScrollState, updateReconcileTabListSortState as updateExpenseAutomationReconcileTabListSortState, updateReconcileTabLocalData as updateExpenseAutomationReconcileTabLocalData, updateSelectedDrawerAccountId as updateExpenseAutomationSelectedDrawerAccountId, updateNodeCollapseState, updateParsedStatementData, updateStatementProcessingBackgroundCompletedSteps, updateStatementProcessingFailed, updateStatementUpdateLocalData, updateStatementUploadChosen, uploadAccountStatement, } from './view/expenseAutomationView/reducers/reconciliationViewReducer';
177
177
  import { backgroundRefetchReviewTab, clearExpenseAutomationTransactionsView, fetchTransactionCategorization, fetchTransactionCategorizationFailure, fetchTransactionCategorizationView, initializeTransactionCategorizationViewLocalData, markCategoryClassRecommendationsFailureForCategorization, markTransactionAsNotMiscategorized, removeTransactionFromAllTabs, saveTransactionCategorization, saveTransactionCategorizationLocalData, setAllItemsToCategoryClassInLocalDataForCategorization, setEntityRecommendationForLineIdsForCategorization, syncTransactionCategorizationFromDetailSave, updateCurrentSelectedTransactionCategorizationTab, updateSelectedCheckboxTransactionIds, updateSelectedCustomerForTransaction, updateSelectedTransactionId, updateSelectedVendorForTransaction, updateTransactionCategorization, updateTransactionCategorizationCompletedSubTab, updateTransactionCategorizationSaveStatus, updateTransactionCategorizationUIState, updateTransactionCategorizationUploadReceiptState, updateTransactionFilters, uploadTransactionCategorizationReceiptSuccess, } from './view/expenseAutomationView/reducers/transactionsViewReducer';
178
178
  import { getExpenseAutomationFluxAnalysisView } from './view/expenseAutomationView/selectors/fluxAnalysisViewSelector';
179
179
  import { getExpenseAutomationReconciliationView, getReparseStatementStatusByAccountId, isAccountReconReport, } from './view/expenseAutomationView/selectors/reconciliationViewSelector';
180
+ import { applyStatementParseFormChange, areStatementParseBalancesConsistent, getStatementParseFormView, } from './view/expenseAutomationView/selectors/statementParseViewSelector';
180
181
  import { getExpenseAutomationTransactionView, getLastTransferEntryReplacement, } from './view/expenseAutomationView/selectors/transactionCategorizationSelector';
181
182
  import { TRANSACTION_FILTER_CATEGORIES, applyTransactionFilters, } from './view/expenseAutomationView/transactionFilterHelpers';
182
183
  import { DEFAULT_COMPLETED_SUB_TAB, toCompletedSubTab, } from './view/expenseAutomationView/types/completedSubTab';
@@ -461,7 +462,7 @@ export {
461
462
  // Bulk Upload Types
462
463
  BATCH_FILE_STATUSES, isUnmatchedTabFileStatus, DEFAULT_COMPLETED_SUB_TAB, toBatchFileStatus, toBatchStatusValue, toBulkUploadPhase, toBulkUploadResultsTab, toBulkUploadSortKey, toCompletedSubTab, toMatchSource, toMissingReceiptsTab, toExpenseAutomationJEScheduleMainTab, toExpenseAutomationJEScheduleSortKey, getExpenseAutomationView, toExpenseAutomationMissingReceiptsSortKey, toExpenseAutomationTransactionsTabKey, toExpenseAutomationViewType, uploadExpenseAutomationMissingReceiptSuccess, markExpenseAutomationMissingReceiptAsDone, fetchAllExpenseAutomationTabs, refreshExpenseAutomationCurrentTab, updateCurrentSelectedTransactionCategorizationTab, updateTransactionCategorizationCompletedSubTab, fetchExpenseAutomationMissingReceipts,
463
464
  // Bulk Upload Actions
464
- bulkUploadReceipts, bulkUploadAutomatchingTimedOut, bulkUploadReceiptsSuccess, bulkUploadReceiptsFailure, restoreBulkUploadAutomatchingOnMount, restoreBulkUploadMatchingState, updateBulkUploadProgress, pusherBatchStatusUpdate, requestMissingReceiptsTabNavigation, clearMissingReceiptsTabNavigation, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatchDetailsFailure, storeBatchDetails, fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, fetchBulkUploadBatches, fetchBulkUploadBatchesSuccess, fetchBulkUploadBatchesFailure, clearBulkUploadBatchDetailsForScopeChange, markBatchDetailRefreshAttempted, refreshBatchDetailsForBatchId, confirmBulkUploadMatch, confirmBulkUploadMatchSuccess, confirmBulkUploadMatchFailure, setBulkUploadResultsTab, setBulkUploadCompletedSubTab, setBulkUploadSortConfig, setBulkUploadUploadedFileCount, clearBulkUpload, searchTransactionsForManualMatch, searchTransactionsForManualMatchSuccess, searchTransactionsForManualMatchFailure, clearManualSearchResults, acknowledgeBulkUploadConfirmMatchComplete, fetchCompletedTransactions, fetchCompletedTransactionsSuccess, fetchCompletedTransactionsFailure, fetchFluxAnalysisView, clearExpenseAutomationFluxAnalysisView, updateOperatingExpensesIdsForReview as updateFluxOperatingExpensesIdsForReview, updateSelectedSectionIdsForReview as updateFluxAnalysisSelectedSectionIdsForReview, reviewFluxAnalysisView, updateExpenseAutomationMissingReceiptUploadState, updateExpenseAutomationMissingReceiptsUIState, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess, getExpenseAutomationFluxAnalysisView, updateCurrentSelectedView, updateCurrentSelectedPeriod, getExpenseAutomationTransactionView, getLastTransferEntryReplacement, updateFluxAnalysisViewUIState, updateFluxAnalysisViewPageMetaData, MAX_SELECTION_LIMIT, checkIfAllLineItemsAreCategoryClassFilled, getLineItemsByTransactionIdsFromLocalData, isAnyItemWithUncategorizedExpenseAccount, excludeAccountFromReconciliation, includeAccountInReconciliation, saveExpenseAutomationReconciliationDetail, updateExpenseAutomationReconcileTabListScrollState, updateExpenseAutomationReconReviewTabListSortState, updateExpenseAutomationReconcileTabListSortState, updateExpenseAutomationReconcileTabLocalData, updateExpenseAutomationAccountReconciliationSelectedTab, updateExpenseAutomationAccountReconciliationSelectedAccountId, getExpenseAutomationReconciliationView, getReparseStatementStatusByAccountId, fetchReconciliationView, uploadAccountStatementIntoDocumentAI, updateExpenseAutomationReconListScrollPosition, setConnectionInProgressForAccountReconciliation, getAccountReconByAccountIdAndSelectedPeriod, toReconciliationTabsType, isAccountReconReport, updateExpenseAutomationReconReviewTabLocalData, updateExpenseAutomationSelectedDrawerAccountId, updateStatementProcessingFailed, saveExpenseAutomationReconciliationReview, updateExpenseAutomationAccountReconciliationLocalData, toReconciliationAccountSource, deleteAccountStatement, uploadAccountStatement, parseStatement, parseStatementSuccess, parseStatementFailure, reparseStatement, reparseStatementSuccess, reparseStatementFailure, resetReparseStatementStatus, updateParsedStatementData, updateNodeCollapseState, updateStatementUploadChosen, submitStatementUpdate, clearStatementDateConflict, updateStatementUpdateLocalData, isReviewTransactionBankTransferType, isReviewTransactionBillPaymentType, isReviewTransactionCreditCardPaymentType, isReviewTransactionDepositType, isReviewTransactionExpenseType, isReviewTransactionCreditCardCreditType, setStatementParseInProgress, };
465
+ bulkUploadReceipts, bulkUploadAutomatchingTimedOut, bulkUploadReceiptsSuccess, bulkUploadReceiptsFailure, restoreBulkUploadAutomatchingOnMount, restoreBulkUploadMatchingState, updateBulkUploadProgress, pusherBatchStatusUpdate, requestMissingReceiptsTabNavigation, clearMissingReceiptsTabNavigation, fetchBulkUploadBatchDetails, fetchBulkUploadBatchDetailsSuccess, fetchBulkUploadBatchDetailsFailure, storeBatchDetails, fetchMoreBatchDetails, fetchMoreBatchDetailsComplete, fetchMoreBatchDetailsFailure, fetchBulkUploadBatches, fetchBulkUploadBatchesSuccess, fetchBulkUploadBatchesFailure, clearBulkUploadBatchDetailsForScopeChange, markBatchDetailRefreshAttempted, refreshBatchDetailsForBatchId, confirmBulkUploadMatch, confirmBulkUploadMatchSuccess, confirmBulkUploadMatchFailure, setBulkUploadResultsTab, setBulkUploadCompletedSubTab, setBulkUploadSortConfig, setBulkUploadUploadedFileCount, clearBulkUpload, searchTransactionsForManualMatch, searchTransactionsForManualMatchSuccess, searchTransactionsForManualMatchFailure, clearManualSearchResults, acknowledgeBulkUploadConfirmMatchComplete, fetchCompletedTransactions, fetchCompletedTransactionsSuccess, fetchCompletedTransactionsFailure, fetchFluxAnalysisView, clearExpenseAutomationFluxAnalysisView, updateOperatingExpensesIdsForReview as updateFluxOperatingExpensesIdsForReview, updateSelectedSectionIdsForReview as updateFluxAnalysisSelectedSectionIdsForReview, reviewFluxAnalysisView, updateExpenseAutomationMissingReceiptUploadState, updateExpenseAutomationMissingReceiptsUIState, updateTransactionCategorizationUploadReceiptState, uploadTransactionCategorizationReceiptSuccess, getExpenseAutomationFluxAnalysisView, updateCurrentSelectedView, updateCurrentSelectedPeriod, getExpenseAutomationTransactionView, getLastTransferEntryReplacement, updateFluxAnalysisViewUIState, updateFluxAnalysisViewPageMetaData, MAX_SELECTION_LIMIT, checkIfAllLineItemsAreCategoryClassFilled, getLineItemsByTransactionIdsFromLocalData, isAnyItemWithUncategorizedExpenseAccount, excludeAccountFromReconciliation, includeAccountInReconciliation, saveExpenseAutomationReconciliationDetail, updateExpenseAutomationReconcileTabListScrollState, updateExpenseAutomationReconReviewTabListSortState, updateExpenseAutomationReconcileTabListSortState, updateExpenseAutomationReconcileTabLocalData, updateExpenseAutomationAccountReconciliationSelectedTab, updateExpenseAutomationAccountReconciliationSelectedAccountId, getExpenseAutomationReconciliationView, getReparseStatementStatusByAccountId, applyStatementParseFormChange, areStatementParseBalancesConsistent, getStatementParseFormView, fetchReconciliationView, uploadAccountStatementIntoDocumentAI, updateExpenseAutomationReconListScrollPosition, setConnectionInProgressForAccountReconciliation, getAccountReconByAccountIdAndSelectedPeriod, toReconciliationTabsType, isAccountReconReport, updateExpenseAutomationReconReviewTabLocalData, updateExpenseAutomationSelectedDrawerAccountId, updateStatementProcessingFailed, saveExpenseAutomationReconciliationReview, updateExpenseAutomationAccountReconciliationLocalData, toReconciliationAccountSource, deleteAccountStatement, uploadAccountStatement, parseStatement, parseStatementSuccess, parseStatementFailure, reparseStatement, reparseStatementSuccess, reparseStatementFailure, resetReparseStatementStatus, updateParsedStatementData, updateNodeCollapseState, updateStatementUploadChosen, submitStatementUpdate, clearStatementDateConflict, updateStatementUpdateLocalData, isReviewTransactionBankTransferType, isReviewTransactionBillPaymentType, isReviewTransactionCreditCardPaymentType, isReviewTransactionDepositType, isReviewTransactionExpenseType, isReviewTransactionCreditCardCreditType, setStatementParseInProgress, setStatementProcessingDismissedToBackground, updateStatementProcessingBackgroundCompletedSteps, };
465
466
  export { fetchTransactionCategorization, fetchTransactionCategorizationView, updateTransactionCategorizationUIState, updateTransactionFilters, updateSelectedCheckboxTransactionIds, markCategoryClassRecommendationsFailureForCategorization, setEntityRecommendationForLineIdsForCategorization, initializeTransactionCategorizationViewLocalData, setAllItemsToCategoryClassInLocalDataForCategorization, saveTransactionCategorizationLocalData, fetchTransactionCategorizationFailure, saveTransactionCategorization, updateTransactionCategorization, updateTransactionCategorizationSaveStatus, markTransactionAsNotMiscategorized, updateSelectedVendorForTransaction, updateSelectedCustomerForTransaction, updateSelectedTransactionId, syncTransactionCategorizationFromDetailSave, backgroundRefetchReviewTab, createTransferEntry, createTransferEntryFailure, createTransferEntryReplacedTransaction, createTransferEntrySuccess, resetCreateTransferEntryStatus, clearTransferEntryRouteReplacement, fetchAccountsForTransferFlow, removeTransactionFromAllTabs, clearExpenseAutomationTransactionsView, toTransactionsSortKey, };
466
467
  export { TOP_EX_TIME_PERIODS };
467
468
  export { toTimeframeTick, mapTimePeriodtoTimeframeTick, toTimePeriod, toAbsoluteDay, toMonthYearPeriodId, convertToPeriod, };
@@ -2,7 +2,8 @@ import { of } from 'rxjs';
2
2
  import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
3
  import { toMonthYearPeriodId } from '../../../../commonStateTypes/timePeriod';
4
4
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
5
- import { isStatementDateConflictResponse, toStatementUpdateRequestBody, transformStatementDateConflictToState, transformUpdateStatementInfoPayloadToState, } from '../../payload/reconciliationPayload';
5
+ import { mapStatementUpdateLocalDataToRequestBody } from '../../helpers/mapStatementUpdateLocalDataToRequestBody';
6
+ import { isStatementDateConflictResponse, transformStatementDateConflictToState, transformUpdateStatementInfoPayloadToState, } from '../../payload/reconciliationPayload';
6
7
  import { submitStatementUpdate, submitStatementUpdateFailure, submitStatementUpdateSuccess, updateParsedStatementData, } from '../../reducers/reconciliationViewReducer';
7
8
  export const updateStatementInfoEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(submitStatementUpdate.match), switchMap((action) => {
8
9
  const { accountId, selectedPeriod, statementUploadId } = action.payload;
@@ -10,7 +11,8 @@ export const updateStatementInfoEpic = (actions$, state$, zeniAPI) => actions$.p
10
11
  const reconciliationView = state.expenseAutomationReconciliationViewState;
11
12
  const accountRecon = reconciliationView.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
12
13
  const localData = accountRecon?.localData?.statementUpdateLocalData;
13
- if (!localData) {
14
+ const parsedStatementData = accountRecon?.parsedStatementData;
15
+ if (localData == null || parsedStatementData == null) {
14
16
  return of(submitStatementUpdateFailure({
15
17
  accountId,
16
18
  selectedPeriod,
@@ -25,7 +27,7 @@ export const updateStatementInfoEpic = (actions$, state$, zeniAPI) => actions$.p
25
27
  error: createZeniAPIStatus('Invalid Data', 'One or more edited transactions are missing statement transaction ids'),
26
28
  }));
27
29
  }
28
- const body = toStatementUpdateRequestBody(localData);
30
+ const body = mapStatementUpdateLocalDataToRequestBody(localData, parsedStatementData);
29
31
  const updateStatementApi$ = zeniAPI.putAndGetJSON(`${zeniAPI.apiEndPoints.reconciliationMicroServiceBaseUrl}/2.0/statement-uploads/${statementUploadId}`, body);
30
32
  return updateStatementApi$.pipe(mergeMap((response) => {
31
33
  if (isSuccessResponse(response) === true && response.data != null) {
@@ -0,0 +1,32 @@
1
+ import { toStatementUpdateRequestBody } from '../payload/reconciliationPayload';
2
+ import { getEffectiveStatementPeriod, mergeStatementParseDeletedTransactionIds, } from '../selectors/statementParseViewSelector';
3
+ const prepareStatementUpdateLocalData = (localData, parsedStatementData) => {
4
+ const { statementMeta, statementTransactions } = parsedStatementData.statementUpload;
5
+ const statementPeriod = getEffectiveStatementPeriod(statementMeta, localData.statementMeta);
6
+ const localDeletedIds = localData.statementTransactions?.deletedIds ?? [];
7
+ const currentTransactionIds = new Set(statementTransactions
8
+ .filter((transaction) => transaction.statementTransactionId !== '' &&
9
+ !localDeletedIds.includes(transaction.statementTransactionId))
10
+ .map((transaction) => transaction.statementTransactionId));
11
+ const mergedDeletedIds = mergeStatementParseDeletedTransactionIds(statementTransactions, statementPeriod, currentTransactionIds);
12
+ const deletedIds = [
13
+ ...new Set([...localDeletedIds, ...mergedDeletedIds]),
14
+ ];
15
+ const localUpdated = localData.statementTransactions?.updated ?? [];
16
+ if (deletedIds.length === localDeletedIds.length &&
17
+ deletedIds.every((id) => localDeletedIds.includes(id))) {
18
+ return localData;
19
+ }
20
+ const deletedIdSet = new Set(deletedIds);
21
+ return {
22
+ ...localData,
23
+ statementTransactions: {
24
+ ...localData.statementTransactions,
25
+ deletedIds,
26
+ added: localData.statementTransactions?.added ?? [],
27
+ updated: localUpdated.filter((transaction) => transaction.statementTransactionId != null &&
28
+ !deletedIdSet.has(transaction.statementTransactionId)),
29
+ },
30
+ };
31
+ };
32
+ export const mapStatementUpdateLocalDataToRequestBody = (localData, parsedStatementData) => toStatementUpdateRequestBody(prepareStatementUpdateLocalData(localData, parsedStatementData));
@@ -190,6 +190,7 @@ export function transformStatementUpdateStateToPayload(localData) {
190
190
  const { statementMeta, statementTransactions } = localData;
191
191
  return {
192
192
  statement_meta: {
193
+ closing_balance: statementMeta.closingBalance,
193
194
  opening_balance: statementMeta.openingBalance,
194
195
  statement_end_date: statementMeta.statementEndDate,
195
196
  statement_start_date: statementMeta.statementStartDate,
@@ -68,7 +68,6 @@ export const initialState = {
68
68
  refreshStatus: { fetchState: 'Not-Started', error: undefined },
69
69
  actionFetchState: initialActionFetchState,
70
70
  selectedAccountId: undefined,
71
- statementProcessingFailed: false,
72
71
  statementUploadChosen: false,
73
72
  reconListUIState: {
74
73
  nodeCollapseState: {},
@@ -291,8 +290,19 @@ const expenseAutomationReconciliationView = createSlice({
291
290
  draft.selectedDrawerAccountId = action.payload.selectedDrawerAccountId;
292
291
  },
293
292
  updateStatementProcessingFailed: (draft, action) => {
294
- draft.statementProcessingFailed =
295
- action.payload.statementProcessingFailed;
293
+ const { accountId, selectedPeriod, statementProcessingFailed } = action.payload;
294
+ const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
295
+ if (accountRecon == null) {
296
+ return;
297
+ }
298
+ accountRecon.statementProcessingFailed = statementProcessingFailed;
299
+ if (statementProcessingFailed === true) {
300
+ accountRecon.statementProcessingDismissedToBackground = false;
301
+ accountRecon.statementProcessingBackgroundCompletedSteps = 0;
302
+ accountRecon.statementProcessingStartedAtMs = undefined;
303
+ accountRecon.statementProcessingTotalDurationSeconds = undefined;
304
+ accountRecon.statementParseInProgress = false;
305
+ }
296
306
  },
297
307
  initialiseLocalDataForSelectedAccountId: () => { },
298
308
  updateSelectedTab: (draft, action) => {
@@ -383,6 +393,41 @@ const expenseAutomationReconciliationView = createSlice({
383
393
  draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].statementParseInProgress =
384
394
  statementParseInProgress;
385
395
  },
396
+ setStatementProcessingDismissedToBackground: (draft, action) => {
397
+ const { accountId, selectedPeriod, dismissedToBackground } = action.payload;
398
+ const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
399
+ if (accountRecon != null) {
400
+ accountRecon.statementProcessingDismissedToBackground =
401
+ dismissedToBackground;
402
+ if (dismissedToBackground === true) {
403
+ accountRecon.statementProcessingBackgroundCompletedSteps =
404
+ action.payload.backgroundCompletedSteps ??
405
+ accountRecon.statementProcessingBackgroundCompletedSteps;
406
+ if (action.payload.startedAtMs != null) {
407
+ accountRecon.statementProcessingStartedAtMs =
408
+ action.payload.startedAtMs;
409
+ }
410
+ if (action.payload.totalDurationSeconds != null) {
411
+ accountRecon.statementProcessingTotalDurationSeconds =
412
+ action.payload.totalDurationSeconds;
413
+ }
414
+ }
415
+ }
416
+ },
417
+ updateStatementProcessingBackgroundCompletedSteps: (draft, action) => {
418
+ const { accountId, selectedPeriod, backgroundCompletedSteps } = action.payload;
419
+ const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
420
+ if (accountRecon != null) {
421
+ if (backgroundCompletedSteps === 0) {
422
+ accountRecon.statementProcessingBackgroundCompletedSteps = 0;
423
+ accountRecon.statementProcessingStartedAtMs = undefined;
424
+ accountRecon.statementProcessingTotalDurationSeconds = undefined;
425
+ }
426
+ else {
427
+ accountRecon.statementProcessingBackgroundCompletedSteps = Math.max(accountRecon.statementProcessingBackgroundCompletedSteps ?? 0, backgroundCompletedSteps);
428
+ }
429
+ }
430
+ },
386
431
  uploadAccountStatement: (draft, action) => {
387
432
  const { accountId, selectedPeriod } = action.payload;
388
433
  const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
@@ -396,6 +441,15 @@ const expenseAutomationReconciliationView = createSlice({
396
441
  fetchState: 'Not-Started',
397
442
  error: undefined,
398
443
  };
444
+ accountRecon.statementUpdateStatus = {
445
+ fetchState: 'Not-Started',
446
+ error: undefined,
447
+ };
448
+ accountRecon.statementProcessingDismissedToBackground = false;
449
+ accountRecon.statementProcessingBackgroundCompletedSteps = 0;
450
+ accountRecon.statementProcessingStartedAtMs = undefined;
451
+ accountRecon.statementProcessingTotalDurationSeconds = undefined;
452
+ accountRecon.statementProcessingFailed = false;
399
453
  accountRecon.reparseStatementStatus = {
400
454
  fetchState: 'Not-Started',
401
455
  error: undefined,
@@ -426,10 +480,22 @@ const expenseAutomationReconciliationView = createSlice({
426
480
  },
427
481
  deleteAccountStatementSuccess: (draft, action) => {
428
482
  const { accountId, selectedPeriod } = action.payload;
429
- draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].statementDeleteStatus = {
430
- fetchState: 'Completed',
431
- error: undefined,
432
- };
483
+ const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
484
+ if (accountRecon != null) {
485
+ accountRecon.statementDeleteStatus = {
486
+ fetchState: 'Completed',
487
+ error: undefined,
488
+ };
489
+ accountRecon.parsedStatementData = undefined;
490
+ accountRecon.statementProcessingDismissedToBackground = false;
491
+ accountRecon.statementProcessingBackgroundCompletedSteps = 0;
492
+ accountRecon.statementProcessingStartedAtMs = undefined;
493
+ accountRecon.statementProcessingTotalDurationSeconds = undefined;
494
+ accountRecon.statementUpdateStatus = {
495
+ fetchState: 'Not-Started',
496
+ error: undefined,
497
+ };
498
+ }
433
499
  },
434
500
  deleteAccountStatementFailure: (draft, action) => {
435
501
  const { accountId, selectedPeriod } = action.payload;
@@ -456,7 +522,7 @@ const expenseAutomationReconciliationView = createSlice({
456
522
  error: undefined,
457
523
  };
458
524
  draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].statementParseInProgress = false;
459
- draft.statementProcessingFailed = false;
525
+ draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].statementProcessingFailed = false;
460
526
  },
461
527
  parseStatementFailure: (draft, action) => {
462
528
  const { accountId, selectedPeriod } = action.payload;
@@ -465,7 +531,7 @@ const expenseAutomationReconciliationView = createSlice({
465
531
  error: action.payload.error,
466
532
  };
467
533
  draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].statementParseInProgress = false;
468
- draft.statementProcessingFailed = true;
534
+ draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].statementProcessingFailed = true;
469
535
  },
470
536
  reparseStatement: (draft, action) => {
471
537
  const { accountId, selectedPeriod } = action.payload;
@@ -480,21 +546,38 @@ const expenseAutomationReconciliationView = createSlice({
480
546
  fetchState: 'Not-Started',
481
547
  error: undefined,
482
548
  };
483
- accountRecon.statementParseInProgress = false;
549
+ accountRecon.statementUpdateStatus = {
550
+ fetchState: 'Not-Started',
551
+ error: undefined,
552
+ };
553
+ accountRecon.statementProcessingDismissedToBackground = false;
554
+ accountRecon.statementProcessingBackgroundCompletedSteps = 0;
555
+ accountRecon.statementProcessingStartedAtMs = undefined;
556
+ accountRecon.statementProcessingTotalDurationSeconds = undefined;
557
+ accountRecon.statementProcessingFailed = false;
558
+ accountRecon.statementParseInProgress = true;
484
559
  }
485
560
  },
486
561
  reparseStatementSuccess: (draft, action) => {
487
562
  const { accountId, selectedPeriod } = action.payload;
488
- draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].reparseStatementStatus = {
563
+ const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
564
+ if (accountRecon == null) {
565
+ return;
566
+ }
567
+ accountRecon.reparseStatementStatus = {
489
568
  fetchState: 'Completed',
490
569
  error: undefined,
491
570
  };
492
- draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].statementParseStatus = {
493
- fetchState: 'Not-Started',
494
- error: undefined,
495
- };
496
- draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].statementParseInProgress = false;
497
- draft.statementProcessingFailed = false;
571
+ const parseFetchState = accountRecon.statementParseStatus.fetchState;
572
+ if (parseFetchState !== 'In-Progress' &&
573
+ parseFetchState !== 'Completed') {
574
+ accountRecon.statementParseStatus = {
575
+ fetchState: 'Not-Started',
576
+ error: undefined,
577
+ };
578
+ }
579
+ accountRecon.statementParseInProgress = true;
580
+ accountRecon.statementProcessingFailed = false;
498
581
  },
499
582
  reparseStatementFailure: (draft, action) => {
500
583
  const { accountId, selectedPeriod } = action.payload;
@@ -502,7 +585,7 @@ const expenseAutomationReconciliationView = createSlice({
502
585
  fetchState: 'Error',
503
586
  error: action.payload.error,
504
587
  };
505
- draft.statementProcessingFailed = true;
588
+ draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)].reconciliationByAccountID[accountId].statementProcessingFailed = true;
506
589
  },
507
590
  resetReparseStatementStatus: (draft, action) => {
508
591
  const { accountId, selectedPeriod } = action.payload;
@@ -552,6 +635,10 @@ const expenseAutomationReconciliationView = createSlice({
552
635
  error: undefined,
553
636
  };
554
637
  accountRecon.statementDateConflict = null;
638
+ accountRecon.statementProcessingDismissedToBackground = false;
639
+ accountRecon.statementProcessingBackgroundCompletedSteps = 0;
640
+ accountRecon.statementProcessingStartedAtMs = undefined;
641
+ accountRecon.statementProcessingTotalDurationSeconds = undefined;
555
642
  if (accountRecon.localData != null) {
556
643
  accountRecon.localData.statementUpdateLocalData = undefined;
557
644
  }
@@ -680,9 +767,56 @@ const expenseAutomationReconciliationView = createSlice({
680
767
  },
681
768
  });
682
769
  // Export actions
683
- export const { fetchReconciliation, fetchReconciliationFailure, fetchReconciliationSuccess, saveReconciliationDetail, saveReconciliationDetailFailure, updateSelectedAccountId, setConnectionInProgressForAccount, setStatementParseInProgress, updateReconcileTabLocalData, updateReconcileTabListSortState, updateReconcileTabListScrollState, saveReconciliationDetailSuccess, updateSelectedTab, updateBalancesLocalData, initialiseLocalDataForSelectedAccountId, clearExpenseAutomationReconciliationView, updateSelectedDrawerAccountId, updateStatementProcessingFailed, updateReviewTabSortState, initializeReconciliationReviewTabLocalData, updateReviewTabLocalData, saveReconciliationReview, updateSaveReconciliationReviewFetchStatus, updateReconListScrollPosition, updateAccountReconciliationLocalData, deleteAccountStatement, deleteAccountStatementSuccess, deleteAccountStatementFailure, excludeAccountFromReconciliation, excludeAccountFromReconciliationSuccess, excludeAccountFromReconciliationFailure, includeAccountInReconciliation, includeAccountInReconciliationSuccess, includeAccountInReconciliationFailure, uploadAccountStatement, uploadAccountStatementSuccess, uploadAccountStatementFailure, parseStatement, parseStatementSuccess, parseStatementFailure, reparseStatement, reparseStatementSuccess, reparseStatementFailure, resetReparseStatementStatus, updateParsedStatementData, updateStatementUpdateLocalData, submitStatementUpdate, submitStatementUpdateSuccess, submitStatementUpdateFailure, clearStatementDateConflict, updateStatementUploadChosen, updateNodeCollapseState, } = expenseAutomationReconciliationView.actions;
770
+ export const { fetchReconciliation, fetchReconciliationFailure, fetchReconciliationSuccess, saveReconciliationDetail, saveReconciliationDetailFailure, updateSelectedAccountId, setConnectionInProgressForAccount, setStatementParseInProgress, setStatementProcessingDismissedToBackground, updateStatementProcessingBackgroundCompletedSteps, updateReconcileTabLocalData, updateReconcileTabListSortState, updateReconcileTabListScrollState, saveReconciliationDetailSuccess, updateSelectedTab, updateBalancesLocalData, initialiseLocalDataForSelectedAccountId, clearExpenseAutomationReconciliationView, updateSelectedDrawerAccountId, updateStatementProcessingFailed, updateReviewTabSortState, initializeReconciliationReviewTabLocalData, updateReviewTabLocalData, saveReconciliationReview, updateSaveReconciliationReviewFetchStatus, updateReconListScrollPosition, updateAccountReconciliationLocalData, deleteAccountStatement, deleteAccountStatementSuccess, deleteAccountStatementFailure, excludeAccountFromReconciliation, excludeAccountFromReconciliationSuccess, excludeAccountFromReconciliationFailure, includeAccountInReconciliation, includeAccountInReconciliationSuccess, includeAccountInReconciliationFailure, uploadAccountStatement, uploadAccountStatementSuccess, uploadAccountStatementFailure, parseStatement, parseStatementSuccess, parseStatementFailure, reparseStatement, reparseStatementSuccess, reparseStatementFailure, resetReparseStatementStatus, updateParsedStatementData, updateStatementUpdateLocalData, submitStatementUpdate, submitStatementUpdateSuccess, submitStatementUpdateFailure, clearStatementDateConflict, updateStatementUploadChosen, updateNodeCollapseState, } = expenseAutomationReconciliationView.actions;
684
771
  // Export reducer
685
772
  export default expenseAutomationReconciliationView.reducer;
773
+ function isEntityStatementParsing(reconciliation) {
774
+ return reconciliation.statement_data_status?.code === 'parsing';
775
+ }
776
+ function resolveStatementParseInProgressAfterFetch(oldRecord, reconciliation) {
777
+ if (isEntityStatementParsing(reconciliation)) {
778
+ return true;
779
+ }
780
+ if (oldRecord?.statementParseInProgress === true) {
781
+ return true;
782
+ }
783
+ if (oldRecord?.statementParseStatus.fetchState === 'In-Progress') {
784
+ return true;
785
+ }
786
+ if (oldRecord?.reparseStatementStatus.fetchState === 'In-Progress') {
787
+ return true;
788
+ }
789
+ if (oldRecord?.statementUploadStatus.fetchState === 'In-Progress') {
790
+ return true;
791
+ }
792
+ // Reparse POST returned; async parse may not be on entity yet.
793
+ if (oldRecord?.reparseStatementStatus.fetchState === 'Completed' &&
794
+ oldRecord.statementParseStatus.fetchState !== 'Completed' &&
795
+ oldRecord.statementParseStatus.fetchState !== 'Error') {
796
+ return true;
797
+ }
798
+ // Continue-in-background while parse flow has not finished.
799
+ if (oldRecord?.statementProcessingDismissedToBackground === true &&
800
+ oldRecord.statementParseStatus.fetchState !== 'Completed' &&
801
+ oldRecord.statementParseStatus.fetchState !== 'Error' &&
802
+ (oldRecord.statementProcessingBackgroundCompletedSteps ?? 0) > 0) {
803
+ return true;
804
+ }
805
+ return false;
806
+ }
807
+ function preserveStatementUploadStatusAfterFetch(oldRecord) {
808
+ const oldUploadStatus = oldRecord?.statementUploadStatus;
809
+ if (oldUploadStatus?.fetchState === 'In-Progress') {
810
+ return oldUploadStatus;
811
+ }
812
+ const parseFetchState = oldRecord?.statementParseStatus.fetchState;
813
+ if (oldUploadStatus?.fetchState === 'Completed' &&
814
+ parseFetchState !== 'Completed' &&
815
+ parseFetchState !== 'Error') {
816
+ return oldUploadStatus;
817
+ }
818
+ return { fetchState: 'Not-Started', error: undefined };
819
+ }
686
820
  function updateAllReconciliation(draft, accounts, selectedPeriod, reconciliationData, refreshViewInBackground, excludedAccounts, summary) {
687
821
  draft.excludedAccountIDs = excludedAccounts.map((ea) => ea.account_id);
688
822
  draft.excludedAccountExclusionInfo = {};
@@ -743,12 +877,17 @@ function updateAllReconciliation(draft, accounts, selectedPeriod, reconciliation
743
877
  transactionFetchState: { fetchState: 'Not-Started', error: undefined },
744
878
  refreshStatus: { fetchState: 'Not-Started', error: undefined },
745
879
  statementDeleteStatus: { fetchState: 'Not-Started', error: undefined },
746
- statementUploadStatus: { fetchState: 'Not-Started', error: undefined },
880
+ statementUploadStatus: preserveStatementUploadStatusAfterFetch(oldRecord),
747
881
  statementUpdateStatus: oldRecord?.statementUpdateStatus ?? {
748
882
  fetchState: 'Not-Started',
749
883
  error: undefined,
750
884
  },
751
- statementParseInProgress: reconciliation.bank_status.code === 'parsing',
885
+ statementParseInProgress: resolveStatementParseInProgressAfterFetch(oldRecord, reconciliation),
886
+ statementProcessingDismissedToBackground: oldRecord?.statementProcessingDismissedToBackground ?? false,
887
+ statementProcessingBackgroundCompletedSteps: oldRecord?.statementProcessingBackgroundCompletedSteps ?? 0,
888
+ statementProcessingStartedAtMs: oldRecord?.statementProcessingStartedAtMs,
889
+ statementProcessingTotalDurationSeconds: oldRecord?.statementProcessingTotalDurationSeconds,
890
+ statementProcessingFailed: oldRecord?.statementProcessingFailed ?? false,
752
891
  statementParseStatus: oldRecord?.statementParseStatus ?? {
753
892
  fetchState: 'Not-Started',
754
893
  error: undefined,
@@ -852,6 +991,9 @@ const updateReconciliationByAccountIDOnIncludeAccount = (draft, accountId, recon
852
991
  statementUploadStatus: { fetchState: 'Not-Started', error: undefined },
853
992
  statementUpdateStatus: { fetchState: 'Not-Started', error: undefined },
854
993
  statementParseInProgress: reconciliation.bank_status.code === 'parsing',
994
+ statementProcessingDismissedToBackground: false,
995
+ statementProcessingBackgroundCompletedSteps: 0,
996
+ statementProcessingFailed: false,
855
997
  statementParseStatus: { fetchState: 'Not-Started', error: undefined },
856
998
  reparseStatementStatus: { fetchState: 'Not-Started', error: undefined },
857
999
  statementDateConflict: null,
@@ -13,6 +13,7 @@ import { date } from '../../../zeniDayJS';
13
13
  import { getAccountList, getNestedAccountListHierarchy, } from '../../accountList/accountListSelector';
14
14
  import { getClassList, getNestedClassListHierarchy, } from '../../classList/classListSelector';
15
15
  import { initialActionFetchState, initialReconciliationTabsState, initialState, } from '../reducers/reconciliationViewReducer';
16
+ import { getParsedStatementDataForView, getStatementTransactionPeriodById, } from './statementParseViewSelector';
16
17
  import { getAllSteps } from '../selectorTypes/expenseAutomationViewSelectorTypes';
17
18
  export const getExpenseAutomationReconciliationView = createSelector((state) => state, (state) => getCurrentTenant(state), (state) => state.expenseAutomationReconciliationViewState, (state) => state.expenseAutomationViewState, (state) => state.plaidAccountViewState, (state) => state.accountListState, (state) => state.accountState, (state) => state.classListState, (state) => state.classState, (state) => state.monthEndCloseChecksState, (state) => state.vendorState, (state) => state.monthEndCloseChecksViewState, (state, currentTenant, reconciliationViewState, expenseAutomationViewState, plaidAccountViewState, accountListState, accountState, classListState, classState, monthEndCloseChecksState, vendorState, monthEndCloseChecksViewState) => {
18
19
  const { selectedPeriodByTenantId } = expenseAutomationViewState;
@@ -77,7 +78,6 @@ export const getExpenseAutomationReconciliationView = createSelector((state) =>
77
78
  selectedTab: reconciliationViewState.selectedTab,
78
79
  monthYearPeriod: selectedPeriod,
79
80
  selectedAccountId: reconciliationViewState.selectedAccountId,
80
- statementProcessingFailed: reconciliationViewState.statementProcessingFailed,
81
81
  statementUploadChosen: reconciliationViewState.statementUploadChosen,
82
82
  accountReconciliationsByAccountID: {},
83
83
  reconciliationTabsState: initialReconciliationTabsState,
@@ -243,38 +243,40 @@ export const getExpenseAutomationReconciliationView = createSelector((state) =>
243
243
  liabilityTotals.varianceTotal = total.varianceTotal;
244
244
  liabilityTotals.variancePercentage = total.variancePercentage;
245
245
  }
246
+ const accountReconState = accountReconForMonthYearPeriod.reconciliationByAccountID[accountID];
247
+ const rawParsedStatementData = accountReconState.parsedStatementData;
248
+ const statementUpdateLocalData = accountReconState.localData?.statementUpdateLocalData;
249
+ const parsedStatementDataForView = getParsedStatementDataForView(rawParsedStatementData, statementUpdateLocalData);
246
250
  reconList[accountID] = {
247
251
  accountConnectionStatus: accountConnectionStatus.status,
248
252
  paymentAccountId: accountConnectionStatus.paymentAccountId,
249
253
  connectionInProgress: accountConnectionStatus.connectionInProgress,
250
254
  accountReconEntity: accountRecon,
251
- refreshStatus: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
252
- .refreshStatus,
253
- statementDeleteStatus: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
254
- .statementDeleteStatus,
255
- statementParseStatus: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
256
- .statementParseStatus,
257
- reparseStatementStatus: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
258
- .reparseStatementStatus,
259
- statementDateConflict: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
260
- .statementDateConflict,
261
- statementUpdateStatus: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
262
- .statementUpdateStatus,
263
- statementUploadStatus: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
264
- .statementUploadStatus,
265
- localData: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
266
- .localData,
267
- statementParseInProgress: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
268
- .statementParseInProgress,
269
- parsedStatementData: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
270
- .parsedStatementData,
255
+ refreshStatus: accountReconState.refreshStatus,
256
+ statementDeleteStatus: accountReconState.statementDeleteStatus,
257
+ statementParseStatus: accountReconState.statementParseStatus,
258
+ reparseStatementStatus: accountReconState.reparseStatementStatus,
259
+ statementDateConflict: accountReconState.statementDateConflict,
260
+ statementUpdateStatus: accountReconState.statementUpdateStatus,
261
+ statementUploadStatus: accountReconState.statementUploadStatus,
262
+ localData: accountReconState.localData,
263
+ statementParseInProgress: accountReconState.statementParseInProgress,
264
+ statementProcessingDismissedToBackground: accountReconState.statementProcessingDismissedToBackground,
265
+ statementProcessingBackgroundCompletedSteps: accountReconState.statementProcessingBackgroundCompletedSteps,
266
+ statementProcessingStartedAtMs: accountReconState.statementProcessingStartedAtMs,
267
+ statementProcessingTotalDurationSeconds: accountReconState.statementProcessingTotalDurationSeconds,
268
+ statementProcessingFailed: accountReconState.statementProcessingFailed,
269
+ parsedStatementData: parsedStatementDataForView,
270
+ allCitationTransactions: rawParsedStatementData?.statementUpload?.statementTransactions,
271
+ statementTransactionPeriodById: rawParsedStatementData?.statementUpload != null
272
+ ? getStatementTransactionPeriodById(rawParsedStatementData.statementUpload.statementTransactions, rawParsedStatementData.statementUpload.statementMeta, accountReconState.localData?.statementUpdateLocalData
273
+ ?.statementMeta)
274
+ : undefined,
271
275
  transactionFetchState: reduceAnyFetchState([
272
- accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
273
- .transactionFetchState,
276
+ accountReconState.transactionFetchState,
274
277
  accountListState.byReportId.accountList,
275
278
  ]),
276
- accountDetectionReason: accountReconForMonthYearPeriod.reconciliationByAccountID[accountID]
277
- .accountDetectionReason,
279
+ accountDetectionReason: accountReconState.accountDetectionReason,
278
280
  };
279
281
  });
280
282
  const accountReconciliationBySectionID = {
@@ -342,6 +344,9 @@ export const getExpenseAutomationReconciliationView = createSelector((state) =>
342
344
  localData: {},
343
345
  connectionInProgress: false,
344
346
  statementParseInProgress: false,
347
+ statementProcessingDismissedToBackground: false,
348
+ statementProcessingBackgroundCompletedSteps: 0,
349
+ statementProcessingFailed: false,
345
350
  accountConnectionStatus: 'not_connected',
346
351
  transactionFetchState: { fetchState: 'Not-Started' },
347
352
  accountReconEntity: {
@@ -427,7 +432,6 @@ export const getExpenseAutomationReconciliationView = createSelector((state) =>
427
432
  refreshStatus: reconciliationViewState.refreshStatus,
428
433
  selectedTab: reconciliationViewState.selectedTab,
429
434
  selectedDrawerAccountId: reconciliationViewState.selectedDrawerAccountId,
430
- statementProcessingFailed: reconciliationViewState.statementProcessingFailed,
431
435
  statementUploadChosen: reconciliationViewState.statementUploadChosen,
432
436
  monthYearPeriod: selectedPeriod,
433
437
  accountReconciliationBySectionID,