@zeniai/client-epic-state 5.2.41-beta6MM → 5.2.41-beta8MM
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/lib/entity/billPay/billTransaction/billTransactionReducer.d.ts +5 -1
- package/lib/entity/billPay/billTransaction/billTransactionReducer.js +17 -2
- package/lib/entity/invoicing/coupon/couponPayload.d.ts +8 -0
- package/lib/entity/invoicing/coupon/couponPayload.js +8 -0
- package/lib/entity/invoicing/coupon/couponState.d.ts +6 -0
- package/lib/entity/invoicing/dunningCase/dunningCaseTypes.d.ts +1 -1
- package/lib/entity/invoicing/plan/planSelector.d.ts +20 -0
- package/lib/entity/invoicing/plan/planSelector.js +42 -0
- package/lib/entity/invoicing/product/productPayload.d.ts +8 -1
- package/lib/entity/invoicing/product/productPayload.js +4 -1
- package/lib/entity/invoicing/product/productState.d.ts +16 -0
- package/lib/entity/jeSchedules/jeSchedulesReducer.d.ts +4 -1
- package/lib/entity/jeSchedules/jeSchedulesReducer.js +79 -5
- package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
- package/lib/entity/snackbar/snackbarTypes.js +1 -0
- package/lib/entity/task/taskState.d.ts +1 -1
- package/lib/epic.d.ts +4 -1
- package/lib/epic.js +10 -4
- package/lib/esm/entity/billPay/billTransaction/billTransactionReducer.js +16 -1
- package/lib/esm/entity/invoicing/coupon/couponPayload.js +8 -0
- package/lib/esm/entity/invoicing/plan/planSelector.js +39 -0
- package/lib/esm/entity/invoicing/product/productPayload.js +4 -1
- package/lib/esm/entity/jeSchedules/jeSchedulesReducer.js +78 -4
- package/lib/esm/entity/snackbar/snackbarTypes.js +1 -0
- package/lib/esm/epic.js +10 -4
- package/lib/esm/index.js +8 -7
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/actualMatching/ignoreActualMatchingSuggestionEpic.js +3 -2
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchFailedJePostingsEpic.js +39 -0
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +14 -1
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulesEpic.js +10 -13
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/initializeJeScheduleLocalDataEpic.js +38 -17
- package/lib/esm/view/expenseAutomationView/epics/jeSchedule/retryJeSchedulesEpic.js +17 -1
- package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +15 -4
- package/lib/esm/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +6 -0
- package/lib/esm/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +74 -1
- package/lib/esm/view/expenseAutomationView/reducers/reconciliationViewReducer.js +80 -53
- package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +10 -0
- package/lib/esm/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +6 -2
- package/lib/esm/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +49 -14
- package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +28 -11
- package/lib/esm/view/invoicing/createInvoice/createInvoiceSelector.js +28 -1
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +67 -35
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewReducer.js +22 -11
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +23 -4
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/invoicingCatalogItemFormHelper.js +1 -7
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +19 -5
- package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/seedInvoicingCatalogItemFormDraftEpic.js +30 -7
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewPayload.js +16 -1
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewReducer.js +22 -11
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewSelector.js +69 -6
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewState.js +22 -0
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/fetchInvoicingCouponDetailPageEpic.js +22 -4
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.js +18 -4
- package/lib/esm/view/invoicing/editInvoicingCouponDetailView/seedInvoicingCouponFormDraftEpic.js +29 -6
- package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.js +28 -12
- package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.js +37 -2
- package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/initializeInvoicingCustomerAddressEpic.js +43 -17
- package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.js +1 -1
- package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/seedInvoicingCustomerFormDraftEpic.js +29 -6
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.js +30 -14
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.js +143 -8
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionFormPageEpic.js +4 -1
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/initializeInvoicingSubscriptionAddressEpic.js +44 -18
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +1 -1
- package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/seedInvoicingSubscriptionFormDraftEpic.js +29 -8
- package/lib/esm/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewSelector.js +12 -0
- package/lib/esm/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewState.js +6 -6
- package/lib/esm/view/invoicing/invoicingFormDraft.js +78 -0
- package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +57 -9
- package/lib/esm/view/recommendation/recommendationPayload.js +3 -1
- package/lib/esm/view/recommendation/recommendationReducer.js +27 -7
- package/lib/esm/view/recommendation/recommendationSelector.js +1 -0
- package/lib/esm/view/recommendation/submitRecommendationFeedbackEpic.js +20 -8
- package/lib/esm/view/spendManagement/billPay/billDetailView/billDetailViewReducer.js +87 -1
- package/lib/esm/view/spendManagement/billPay/billDetailView/billDetailViewSelector.js +1 -0
- package/lib/esm/view/spendManagement/billPay/billDetailView/billDetailViewState.js +4 -0
- package/lib/esm/view/spendManagement/billPay/billDetailView/epics/fetchBillRecommendationsEpic.js +97 -0
- package/lib/esm/view/spendManagement/billPay/billDetailView/epics/fetchRecommendationsForBill.js +85 -0
- package/lib/esm/view/spendManagement/billPay/editBillView/editBillViewReducer.js +18 -1
- package/lib/esm/view/spendManagement/billPay/editBillView/editBillViewSelector.js +3 -0
- package/lib/esm/view/spendManagement/billPay/editBillView/epics/applyBillRecommendationsToLocalStoreEpic.js +95 -0
- package/lib/index.d.ts +9 -8
- package/lib/index.js +54 -48
- package/lib/view/expenseAutomationView/epics/jeSchedule/actualMatching/ignoreActualMatchingSuggestionEpic.js +3 -2
- package/lib/view/expenseAutomationView/epics/jeSchedule/fetchFailedJePostingsEpic.d.ts +20 -0
- package/lib/view/expenseAutomationView/epics/jeSchedule/fetchFailedJePostingsEpic.js +43 -0
- package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.d.ts +2 -2
- package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +13 -0
- package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulesEpic.js +8 -11
- package/lib/view/expenseAutomationView/epics/jeSchedule/initializeJeScheduleLocalDataEpic.js +38 -17
- package/lib/view/expenseAutomationView/epics/jeSchedule/retryJeSchedulesEpic.d.ts +2 -2
- package/lib/view/expenseAutomationView/epics/jeSchedule/retryJeSchedulesEpic.js +17 -1
- package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +15 -4
- package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
- package/lib/view/expenseAutomationView/payload/jeSchedulesPayload.d.ts +18 -0
- package/lib/view/expenseAutomationView/payload/transactionCategorizationPayload.d.ts +18 -7
- package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +6 -0
- package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.d.ts +10 -3
- package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +76 -3
- package/lib/view/expenseAutomationView/reducers/reconciliationViewReducer.js +80 -53
- package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +10 -0
- package/lib/view/expenseAutomationView/selectorTypes/jeSchedulesSelectorTypes.d.ts +3 -0
- package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +8 -4
- package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.d.ts +1 -0
- package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +7 -2
- package/lib/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +49 -14
- package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +27 -10
- package/lib/view/expenseAutomationView/types/jeSchedulesViewState.d.ts +16 -0
- package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +8 -5
- package/lib/view/invoicing/createInvoice/createInvoiceSelector.d.ts +7 -2
- package/lib/view/invoicing/createInvoice/createInvoiceSelector.js +28 -1
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.d.ts +46 -5
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +69 -36
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewReducer.d.ts +3 -1
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewReducer.js +22 -11
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.d.ts +17 -0
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +22 -3
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewState.d.ts +2 -1
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/invoicingCatalogItemFormHelper.d.ts +2 -2
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/invoicingCatalogItemFormHelper.js +1 -7
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +17 -3
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/seedInvoicingCatalogItemFormDraftEpic.d.ts +10 -6
- package/lib/view/invoicing/editInvoicingCatalogItemDetailView/seedInvoicingCatalogItemFormDraftEpic.js +29 -6
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewPayload.d.ts +5 -1
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewPayload.js +16 -1
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewReducer.d.ts +3 -1
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewReducer.js +22 -11
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewSelector.d.ts +46 -0
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewSelector.js +68 -5
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewState.d.ts +21 -1
- package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewState.js +24 -1
- package/lib/view/invoicing/editInvoicingCouponDetailView/fetchInvoicingCouponDetailPageEpic.d.ts +9 -5
- package/lib/view/invoicing/editInvoicingCouponDetailView/fetchInvoicingCouponDetailPageEpic.js +22 -4
- package/lib/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.js +18 -4
- package/lib/view/invoicing/editInvoicingCouponDetailView/seedInvoicingCouponFormDraftEpic.d.ts +10 -6
- package/lib/view/invoicing/editInvoicingCouponDetailView/seedInvoicingCouponFormDraftEpic.js +28 -5
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.d.ts +5 -1
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.js +29 -13
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.d.ts +16 -7
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.js +37 -2
- package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewState.d.ts +8 -1
- package/lib/view/invoicing/editInvoicingCustomerDetailView/initializeInvoicingCustomerAddressEpic.d.ts +15 -10
- package/lib/view/invoicing/editInvoicingCustomerDetailView/initializeInvoicingCustomerAddressEpic.js +42 -16
- package/lib/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.js +1 -1
- package/lib/view/invoicing/editInvoicingCustomerDetailView/seedInvoicingCustomerFormDraftEpic.d.ts +10 -6
- package/lib/view/invoicing/editInvoicingCustomerDetailView/seedInvoicingCustomerFormDraftEpic.js +28 -5
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.d.ts +5 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.js +31 -15
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.d.ts +79 -12
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.js +145 -9
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewState.d.ts +8 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionFormPageEpic.js +4 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/initializeInvoicingSubscriptionAddressEpic.d.ts +15 -11
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/initializeInvoicingSubscriptionAddressEpic.js +42 -16
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +1 -1
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/seedInvoicingSubscriptionFormDraftEpic.d.ts +10 -6
- package/lib/view/invoicing/editInvoicingSubscriptionDetailView/seedInvoicingSubscriptionFormDraftEpic.js +28 -7
- package/lib/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewSelector.d.ts +10 -0
- package/lib/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewSelector.js +12 -0
- package/lib/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewReducer.d.ts +1 -1
- package/lib/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewState.d.ts +2 -2
- package/lib/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewState.js +6 -6
- package/lib/view/invoicing/invoicingFormDraft.d.ts +63 -0
- package/lib/view/invoicing/invoicingFormDraft.js +86 -0
- package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.d.ts +49 -0
- package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +59 -10
- package/lib/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewReducer.d.ts +1 -1
- package/lib/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewState.d.ts +1 -1
- package/lib/view/recommendation/recommendationPayload.d.ts +1 -0
- package/lib/view/recommendation/recommendationPayload.js +5 -0
- package/lib/view/recommendation/recommendationReducer.d.ts +7 -1
- package/lib/view/recommendation/recommendationReducer.js +28 -8
- package/lib/view/recommendation/recommendationSelector.d.ts +2 -0
- package/lib/view/recommendation/recommendationSelector.js +3 -1
- package/lib/view/recommendation/recommendationState.d.ts +2 -1
- package/lib/view/recommendation/submitRecommendationFeedbackEpic.d.ts +2 -2
- package/lib/view/recommendation/submitRecommendationFeedbackEpic.js +18 -6
- package/lib/view/spendManagement/billPay/billDetailView/billDetailViewPayload.d.ts +19 -0
- package/lib/view/spendManagement/billPay/billDetailView/billDetailViewReducer.d.ts +10 -1
- package/lib/view/spendManagement/billPay/billDetailView/billDetailViewReducer.js +88 -2
- package/lib/view/spendManagement/billPay/billDetailView/billDetailViewSelector.d.ts +1 -0
- package/lib/view/spendManagement/billPay/billDetailView/billDetailViewSelector.js +1 -0
- package/lib/view/spendManagement/billPay/billDetailView/billDetailViewState.d.ts +7 -0
- package/lib/view/spendManagement/billPay/billDetailView/billDetailViewState.js +4 -0
- package/lib/view/spendManagement/billPay/billDetailView/epics/fetchBillRecommendationsEpic.d.ts +37 -0
- package/lib/view/spendManagement/billPay/billDetailView/epics/fetchBillRecommendationsEpic.js +104 -0
- package/lib/view/spendManagement/billPay/billDetailView/epics/fetchRecommendationsForBill.d.ts +24 -0
- package/lib/view/spendManagement/billPay/billDetailView/epics/fetchRecommendationsForBill.js +92 -0
- package/lib/view/spendManagement/billPay/editBillView/editBillViewReducer.d.ts +4 -1
- package/lib/view/spendManagement/billPay/editBillView/editBillViewReducer.js +19 -2
- package/lib/view/spendManagement/billPay/editBillView/editBillViewSelector.d.ts +1 -0
- package/lib/view/spendManagement/billPay/editBillView/editBillViewSelector.js +3 -0
- package/lib/view/spendManagement/billPay/editBillView/epics/applyBillRecommendationsToLocalStoreEpic.d.ts +18 -0
- package/lib/view/spendManagement/billPay/editBillView/epics/applyBillRecommendationsToLocalStoreEpic.js +102 -0
- package/package.json +2 -2
|
@@ -102,32 +102,27 @@ const expenseAutomationReconciliationView = createSlice({
|
|
|
102
102
|
draft.selectedTab = 'review';
|
|
103
103
|
draft.selectedAccountId = action.payload.accountId;
|
|
104
104
|
}
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(action.payload.selectedPeriod)]?.reconciliationByAccountID[action.payload.accountId];
|
|
106
|
+
if (accountRecon == null) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (accountRecon.transactionFetchState.fetchState !== 'Completed' ||
|
|
108
110
|
action.payload.cacheOverride === true) {
|
|
109
|
-
const data = accountReconciliationRecords.reconciliationByAccountID[action.payload.accountId];
|
|
110
111
|
if (action.payload.refreshViewInBackground === true) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
fetchState: 'In-Progress',
|
|
115
|
-
error: undefined,
|
|
116
|
-
},
|
|
117
|
-
accountId: action.payload.accountId,
|
|
118
|
-
reconciliationId: undefined,
|
|
112
|
+
accountRecon.refreshStatus = {
|
|
113
|
+
fetchState: 'In-Progress',
|
|
114
|
+
error: undefined,
|
|
119
115
|
};
|
|
116
|
+
accountRecon.accountId = action.payload.accountId;
|
|
117
|
+
accountRecon.reconciliationId = undefined;
|
|
120
118
|
}
|
|
121
119
|
else {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
fetchState: 'In-Progress',
|
|
126
|
-
error: undefined,
|
|
127
|
-
},
|
|
128
|
-
accountId: action.payload.accountId,
|
|
129
|
-
reconciliationId: undefined,
|
|
120
|
+
accountRecon.transactionFetchState = {
|
|
121
|
+
fetchState: 'In-Progress',
|
|
122
|
+
error: undefined,
|
|
130
123
|
};
|
|
124
|
+
accountRecon.accountId = action.payload.accountId;
|
|
125
|
+
accountRecon.reconciliationId = undefined;
|
|
131
126
|
}
|
|
132
127
|
}
|
|
133
128
|
}
|
|
@@ -179,14 +174,17 @@ const expenseAutomationReconciliationView = createSlice({
|
|
|
179
174
|
}
|
|
180
175
|
}
|
|
181
176
|
else {
|
|
182
|
-
const
|
|
177
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(action.payload.selectedPeriod)]?.reconciliationByAccountID[action.payload.accountId];
|
|
178
|
+
if (accountRecon == null) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
183
181
|
if (action.payload.refreshViewInBackground === true) {
|
|
184
|
-
|
|
185
|
-
|
|
182
|
+
accountRecon.refreshStatus.error = action.payload.error;
|
|
183
|
+
accountRecon.refreshStatus.fetchState = 'Error';
|
|
186
184
|
}
|
|
187
185
|
else {
|
|
188
|
-
|
|
189
|
-
|
|
186
|
+
accountRecon.transactionFetchState.error = action.payload.error;
|
|
187
|
+
accountRecon.transactionFetchState.fetchState = 'Error';
|
|
190
188
|
}
|
|
191
189
|
}
|
|
192
190
|
},
|
|
@@ -391,8 +389,11 @@ const expenseAutomationReconciliationView = createSlice({
|
|
|
391
389
|
},
|
|
392
390
|
setStatementParseInProgress: (draft, action) => {
|
|
393
391
|
const { accountId, selectedPeriod, statementParseInProgress } = action.payload;
|
|
394
|
-
draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]
|
|
395
|
-
|
|
392
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
393
|
+
if (accountRecon == null) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
accountRecon.statementParseInProgress = statementParseInProgress;
|
|
396
397
|
},
|
|
397
398
|
setStatementProcessingDismissedToBackground: (draft, action) => {
|
|
398
399
|
const { accountId, selectedPeriod, dismissedToBackground } = action.payload;
|
|
@@ -476,15 +477,23 @@ const expenseAutomationReconciliationView = createSlice({
|
|
|
476
477
|
},
|
|
477
478
|
uploadAccountStatementSuccess: (draft, action) => {
|
|
478
479
|
const { accountId, selectedPeriod } = action.payload;
|
|
479
|
-
draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]
|
|
480
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
481
|
+
if (accountRecon == null) {
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
accountRecon.statementUploadStatus = {
|
|
480
485
|
fetchState: 'Completed',
|
|
481
486
|
error: undefined,
|
|
482
487
|
};
|
|
483
|
-
|
|
488
|
+
accountRecon.statementParseInProgress = true;
|
|
484
489
|
},
|
|
485
490
|
uploadAccountStatementFailure: (draft, action) => {
|
|
486
491
|
const { accountId, selectedPeriod } = action.payload;
|
|
487
|
-
draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]
|
|
492
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
493
|
+
if (accountRecon == null) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
accountRecon.statementUploadStatus = {
|
|
488
497
|
fetchState: 'Error',
|
|
489
498
|
error: action.payload.error,
|
|
490
499
|
};
|
|
@@ -677,23 +686,34 @@ const expenseAutomationReconciliationView = createSlice({
|
|
|
677
686
|
},
|
|
678
687
|
reparseStatementFailure: (draft, action) => {
|
|
679
688
|
const { accountId, selectedPeriod } = action.payload;
|
|
680
|
-
draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]
|
|
689
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
690
|
+
if (accountRecon == null) {
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
accountRecon.reparseStatementStatus = {
|
|
681
694
|
fetchState: 'Error',
|
|
682
695
|
error: action.payload.error,
|
|
683
696
|
};
|
|
684
|
-
|
|
697
|
+
accountRecon.statementProcessingFailed = true;
|
|
685
698
|
},
|
|
686
699
|
resetReparseStatementStatus: (draft, action) => {
|
|
687
700
|
const { accountId, selectedPeriod } = action.payload;
|
|
688
|
-
draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]
|
|
701
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
702
|
+
if (accountRecon == null) {
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
accountRecon.reparseStatementStatus = {
|
|
689
706
|
fetchState: 'Not-Started',
|
|
690
707
|
error: undefined,
|
|
691
708
|
};
|
|
692
709
|
},
|
|
693
710
|
updateParsedStatementData: (draft, action) => {
|
|
694
711
|
const { accountId, selectedPeriod, parsedStatementData } = action.payload;
|
|
695
|
-
draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]
|
|
696
|
-
|
|
712
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
713
|
+
if (accountRecon == null) {
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
accountRecon.parsedStatementData = parsedStatementData;
|
|
697
717
|
},
|
|
698
718
|
updateStatementUpdateLocalData: (draft, action) => {
|
|
699
719
|
const { accountId, selectedPeriod, statementUpdateLocalData, statementUploadId, } = action.payload;
|
|
@@ -819,17 +839,21 @@ const expenseAutomationReconciliationView = createSlice({
|
|
|
819
839
|
},
|
|
820
840
|
updateAccountReconciliationLocalData: (draft, action) => {
|
|
821
841
|
const { accountId, selectedPeriod, localData } = action.payload;
|
|
822
|
-
const
|
|
842
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
843
|
+
if (accountRecon == null) {
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
const oldLocalData = accountRecon.localData;
|
|
823
847
|
if (oldLocalData == null) {
|
|
824
|
-
|
|
848
|
+
accountRecon.localData = localData;
|
|
825
849
|
}
|
|
826
850
|
else {
|
|
827
|
-
|
|
851
|
+
accountRecon.localData = {
|
|
828
852
|
...oldLocalData,
|
|
829
853
|
...localData,
|
|
830
854
|
};
|
|
831
855
|
}
|
|
832
|
-
|
|
856
|
+
accountRecon.localData = {
|
|
833
857
|
...oldLocalData,
|
|
834
858
|
...localData,
|
|
835
859
|
};
|
|
@@ -1119,7 +1143,10 @@ function updateAllReconciliation(draft, accounts, selectedPeriod, reconciliation
|
|
|
1119
1143
|
}
|
|
1120
1144
|
}
|
|
1121
1145
|
function updateReconciliationByAccountID(draft, accounts, selectedPeriod, accountId, reconciliationData, refreshViewInBackground) {
|
|
1122
|
-
const
|
|
1146
|
+
const accountRecon = draft.accountReconciliationRecordsBySelectedPeriod[toMonthYearPeriodId(selectedPeriod)]?.reconciliationByAccountID[accountId];
|
|
1147
|
+
if (accountRecon == null) {
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1123
1150
|
const oldConnectionStatus = draft.accountConnectionStatusByAccountID[accountId];
|
|
1124
1151
|
draft.accountConnectionStatusByAccountID[accountId] = {
|
|
1125
1152
|
status: toBankStatusCodeType(reconciliationData.bank_status.code),
|
|
@@ -1129,21 +1156,17 @@ function updateReconciliationByAccountID(draft, accounts, selectedPeriod, accoun
|
|
|
1129
1156
|
: false,
|
|
1130
1157
|
paymentAccountId: accounts.payment_account_id ?? undefined,
|
|
1131
1158
|
};
|
|
1132
|
-
const statementParseInProgress = resolveStatementParseInProgressAfterFetch(
|
|
1159
|
+
const statementParseInProgress = resolveStatementParseInProgressAfterFetch(accountRecon, reconciliationData);
|
|
1160
|
+
accountRecon.reconciliationId =
|
|
1161
|
+
reconciliationData.reconciliation_id ?? undefined;
|
|
1162
|
+
accountRecon.statementParseInProgress = statementParseInProgress;
|
|
1133
1163
|
if (refreshViewInBackground === true) {
|
|
1134
|
-
|
|
1135
|
-
...oldRecord,
|
|
1136
|
-
reconciliationId: reconciliationData.reconciliation_id ?? undefined,
|
|
1137
|
-
refreshStatus: { fetchState: 'Completed', error: undefined },
|
|
1138
|
-
statementParseInProgress,
|
|
1139
|
-
};
|
|
1164
|
+
accountRecon.refreshStatus = { fetchState: 'Completed', error: undefined };
|
|
1140
1165
|
}
|
|
1141
1166
|
else {
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
reconciliationId: reconciliationData.reconciliation_id ?? undefined,
|
|
1146
|
-
transactionFetchState: { fetchState: 'Completed', error: undefined },
|
|
1167
|
+
accountRecon.transactionFetchState = {
|
|
1168
|
+
fetchState: 'Completed',
|
|
1169
|
+
error: undefined,
|
|
1147
1170
|
};
|
|
1148
1171
|
}
|
|
1149
1172
|
}
|
|
@@ -1173,7 +1196,11 @@ const updateExcludedAccountExclusionInfo = (draft, account) => {
|
|
|
1173
1196
|
};
|
|
1174
1197
|
};
|
|
1175
1198
|
const updateReconciliationByAccountIDOnIncludeAccount = (draft, accountId, reconciliation, monthYearPeriodId) => {
|
|
1176
|
-
draft.accountReconciliationRecordsBySelectedPeriod[monthYearPeriodId]
|
|
1199
|
+
const accountReconciliationRecords = draft.accountReconciliationRecordsBySelectedPeriod[monthYearPeriodId];
|
|
1200
|
+
if (accountReconciliationRecords == null) {
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
accountReconciliationRecords.reconciliationByAccountID[accountId] = {
|
|
1177
1204
|
accountId,
|
|
1178
1205
|
reconciliationId: reconciliation.reconciliation_id ?? undefined,
|
|
1179
1206
|
transactionFetchState: { fetchState: 'Not-Started', error: undefined },
|
|
@@ -373,6 +373,16 @@ const expenseAutomationTransactionsView = createSlice({
|
|
|
373
373
|
if (cached != null) {
|
|
374
374
|
autoCat.uiState.pageToken = cached.pageToken;
|
|
375
375
|
autoCat.uiState.totalCount = { ...cached.totalCount };
|
|
376
|
+
// Keep the stored badge in step with the restored sub-tab: the selector
|
|
377
|
+
// falls back to it for any period this sub-tab has not loaded. Merged,
|
|
378
|
+
// not replaced -- the snapshot only holds periods this sub-tab actually
|
|
379
|
+
// loaded, while the badge map also carries periods filled from a Review
|
|
380
|
+
// response's `other_tab_total_count`. Overwriting would drop those and
|
|
381
|
+
// leave the badge at 0 for any period this sub-tab never visited.
|
|
382
|
+
draft.parentTotalCountByTab.autoCategorized = {
|
|
383
|
+
...draft.parentTotalCountByTab.autoCategorized,
|
|
384
|
+
...cached.totalCount,
|
|
385
|
+
};
|
|
376
386
|
autoCat.uiState.scrollPosition = cached.scrollPosition;
|
|
377
387
|
autoCat.transactionIdsBySelectedPeriod = {
|
|
378
388
|
...cached.transactionIdsBySelectedPeriod,
|
|
@@ -333,6 +333,7 @@ function toActualMatchingMatchView(match) {
|
|
|
333
333
|
: match.leftovers.map(toMatchModalAccrual),
|
|
334
334
|
};
|
|
335
335
|
}
|
|
336
|
+
const EMPTY_ACTUAL_MATCHING_ROWS = [];
|
|
336
337
|
export function getExpenseAutomationActualMatchingMatchView(state, matchId) {
|
|
337
338
|
const view = state.expenseAutomationActualMatchingViewState;
|
|
338
339
|
const match = getActualMatchingMatchById(state.actualMatchingState, matchId);
|
|
@@ -353,6 +354,9 @@ export function getCloseMatchAfterIgnoreMatchId(state) {
|
|
|
353
354
|
return state.expenseAutomationActualMatchingViewState
|
|
354
355
|
.closeMatchAfterIgnoreMatchId;
|
|
355
356
|
}
|
|
357
|
+
export function getActualMatchingMatchModalConfirmStatus(state) {
|
|
358
|
+
return state.expenseAutomationActualMatchingViewState.matchModal.confirmStatus;
|
|
359
|
+
}
|
|
356
360
|
export function getExpenseAutomationActualMatchingView(state) {
|
|
357
361
|
const { expenseAutomationActualMatchingViewState } = state;
|
|
358
362
|
const { matchIdsByPeriod, fetchState, error, refreshStatus, uiState, matchModal, ignoreStatusByMatchId, undoReversalStatusByMatchId, } = expenseAutomationActualMatchingViewState;
|
|
@@ -369,10 +373,10 @@ export function getExpenseAutomationActualMatchingView(state) {
|
|
|
369
373
|
const includeReversed = uiState.statusFilter !== 'pending_review';
|
|
370
374
|
const reversed = includeReversed
|
|
371
375
|
? sortActualMatchingRows(rows.filter((row) => isReversedMatchKind(row.kind)), uiState)
|
|
372
|
-
:
|
|
376
|
+
: EMPTY_ACTUAL_MATCHING_ROWS;
|
|
373
377
|
const pendingReview = includePendingReview
|
|
374
378
|
? sortActualMatchingRows(rows.filter((row) => row.kind === 'pending_review'), uiState)
|
|
375
|
-
:
|
|
379
|
+
: EMPTY_ACTUAL_MATCHING_ROWS;
|
|
376
380
|
const kpiSummary = buildActualMatchingKpiSummary(allRows, getTenantDisplayCurrency(state)?.currencyCode);
|
|
377
381
|
const searchedAccruals = matchModal.accrualSearch.results
|
|
378
382
|
.map((key) => getJEOneTimeAccrualByKey(key, state.jeSchedulesState))
|
|
@@ -210,7 +210,7 @@ export function getExpenseAutomationJESchedulesView(state) {
|
|
|
210
210
|
const accruedSchedules = [];
|
|
211
211
|
const oneTimeAccruals = [];
|
|
212
212
|
const { accountState, accountListState, classState, classListState, expenseAutomationJESchedulesViewState, expenseAutomationViewState, monthEndCloseChecksState, } = state;
|
|
213
|
-
const { jeScheduleTransactionKeysByPeriodAndTab, jeScheduleAccruedKeysByPeriodAndTab, jeScheduleOneTimeAccrualKeysByPeriodAndTab,
|
|
213
|
+
const { jeScheduleTransactionKeysByPeriodAndTab, jeScheduleAccruedKeysByPeriodAndTab, jeScheduleOneTimeAccrualKeysByPeriodAndTab, failedJePostings, jeScheduleLocalDataById, postStatusById, ignoreStatusById, revertStatusById, oneTimeAccrualActionStatusById, uiState, error, fetchState, refreshStatus, newSchedule, } = expenseAutomationJESchedulesViewState;
|
|
214
214
|
const accountSettingsView = getJEAccountSettingsView(state);
|
|
215
215
|
const newScheduleView = newSchedule.selectedDetailScheduleType === 'fixed_assets' ||
|
|
216
216
|
newSchedule.selectedDetailScheduleType === 'prepaid_expenses'
|
|
@@ -249,15 +249,11 @@ export function getExpenseAutomationJESchedulesView(state) {
|
|
|
249
249
|
const jeScheduleTransactionKeysForSelectedTab = selectedPeriodTabKey != null
|
|
250
250
|
? jeScheduleTransactionKeysByPeriodAndTab[selectedPeriodTabKey]
|
|
251
251
|
: undefined;
|
|
252
|
-
const failedJeScheduleTransactionKeysForSelectedTab = selectedPeriodTabKey != null
|
|
253
|
-
? failedJeScheduleTransactionKeysByPeriodAndTab[selectedPeriodTabKey]
|
|
254
|
-
: undefined;
|
|
255
252
|
// Distinguishes "this tab has never been fetched for this period" from "fetched and genuinely
|
|
256
253
|
// empty". Without it the page cannot tell a first visit from a real empty state, and renders an
|
|
257
254
|
// empty table where it should render a skeleton.
|
|
258
255
|
const hasLoadedForSelectedTab = jeScheduleTransactionKeysForSelectedTab != null;
|
|
259
256
|
const jeScheduleTransactionKeys = jeScheduleTransactionKeysForSelectedTab ?? [];
|
|
260
|
-
const failedJeScheduleTransactionKeys = failedJeScheduleTransactionKeysForSelectedTab ?? [];
|
|
261
257
|
const jeScheduleAccruedKeys = (selectedPeriodTabKey != null
|
|
262
258
|
? jeScheduleAccruedKeysByPeriodAndTab[selectedPeriodTabKey]
|
|
263
259
|
: undefined) ?? [];
|
|
@@ -280,24 +276,57 @@ export function getExpenseAutomationJESchedulesView(state) {
|
|
|
280
276
|
accruedSchedules.push(accruedScheduleDetails);
|
|
281
277
|
}
|
|
282
278
|
});
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
279
|
+
// Sourced from the tenant-wide bucket, not the per-period one: a failed posting belongs to no
|
|
280
|
+
// close month, and windowing it is what kept older failures off the only tab that can resolve
|
|
281
|
+
// them.
|
|
282
|
+
failedJePostings.jeScheduleTransactionKeys.forEach((jeScheduleKey) => {
|
|
283
|
+
// getJEScheduledTransactionByJEScheduleTransactionKey throws three different ways: it
|
|
284
|
+
// destructures the result of recordGet without a null check, and it raises explicitly for a
|
|
285
|
+
// missing vendor and for a missing base transaction. Catching rather than pre-checking each
|
|
286
|
+
// one keeps this correct if a fourth is added, and none of the three is recoverable here --
|
|
287
|
+
// the row cannot be drawn either way.
|
|
288
|
+
//
|
|
289
|
+
// This matters more for this bucket than anywhere else on the page. It is tenant-wide, so it
|
|
290
|
+
// names schedules the close-month list never loaded, and the group is defined by deleted
|
|
291
|
+
// vendors and deleted categories -- precisely the rows whose entities are most likely to be
|
|
292
|
+
// missing. Unguarded, one such key takes down the whole JE tab instead of dropping one row.
|
|
293
|
+
try {
|
|
294
|
+
failedJeScheduledTransaction.push(getJEScheduledTransactionByJEScheduleTransactionKey(jeScheduleKey, state));
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
// Intentionally swallowed: a failed posting we cannot resolve is one missing row, and the
|
|
298
|
+
// alternative is a blank page for every other row too.
|
|
299
|
+
}
|
|
286
300
|
});
|
|
301
|
+
// What the endpoint last reported as failing. The entity store keeps every month a schedule
|
|
302
|
+
// has and an entry resolved by someone else keeps its old `error` array, so `error` alone would
|
|
303
|
+
// leave a Retry button on a posting that is no longer failing.
|
|
304
|
+
const failedScheduledJournalEntryIDs = new Set(failedJePostings.scheduledJournalEntryIDs);
|
|
287
305
|
const { sortKey, sortOrder, selectedJETypeFilter } = uiState;
|
|
306
|
+
const matchesTypeFilter = (schedule) => selectedJETypeFilter == null ||
|
|
307
|
+
getJEScheduleRowType(schedule) === selectedJETypeFilter;
|
|
288
308
|
const unsortedResolveSchedules = [];
|
|
289
309
|
failedJeScheduledTransaction.forEach((schedule) => {
|
|
290
|
-
|
|
310
|
+
// Only the entries that actually carry an error. The schedule's entry array is shared state:
|
|
311
|
+
// opening a schedule's detail page or Quick View replaces it with every month the schedule
|
|
312
|
+
// has, posted ones included, so fanning out over all of them turned one failed posting into a
|
|
313
|
+
// row per month -- and a month that never failed has nothing to resolve, so it rendered with
|
|
314
|
+
// an empty action column.
|
|
315
|
+
//
|
|
316
|
+
// Intersected with the ids the endpoint actually returned, not just `error`: the entry array
|
|
317
|
+
// is a cache of every month, and an entry whose failure someone else resolved keeps its stale
|
|
318
|
+
// error array until something overwrites it. Only what the last fetch named is still failing.
|
|
319
|
+
schedule.scheduledJournalEntry
|
|
320
|
+
.filter((journalEntry) => journalEntry.error.length > 0 &&
|
|
321
|
+
failedScheduledJournalEntryIDs.has(journalEntry.scheduledJournalEntryID))
|
|
322
|
+
.forEach((journalEntry) => {
|
|
291
323
|
unsortedResolveSchedules.push({
|
|
292
324
|
...schedule,
|
|
293
325
|
scheduledJournalEntry: journalEntry,
|
|
294
326
|
});
|
|
295
327
|
});
|
|
296
328
|
});
|
|
297
|
-
const resolveSchedules = sortJEScheduledTransactions(unsortedResolveSchedules, sortKey, sortOrder);
|
|
298
|
-
const validResolveForPending = unsortedResolveSchedules.filter((r) => jeScheduleLocalDataById[r.scheduledJournalEntry.scheduledJournalEntryID] != null);
|
|
299
|
-
const matchesTypeFilter = (schedule) => selectedJETypeFilter == null ||
|
|
300
|
-
getJEScheduleRowType(schedule) === selectedJETypeFilter;
|
|
329
|
+
const resolveSchedules = sortJEScheduledTransactions(unsortedResolveSchedules.filter(matchesTypeFilter), sortKey, sortOrder);
|
|
301
330
|
const isCreatedStatus = (schedule) => {
|
|
302
331
|
// An accrual is "created" once it is not review work: posted, reversing, reversed, or in a
|
|
303
332
|
// transient posting claim.
|
|
@@ -315,7 +344,6 @@ export function getExpenseAutomationJESchedulesView(state) {
|
|
|
315
344
|
];
|
|
316
345
|
const pendingReviewSchedules = sortJEScheduledTransactions([
|
|
317
346
|
...allScheduleRows.filter((schedule) => isPendingReviewRow(schedule) && matchesTypeFilter(schedule)),
|
|
318
|
-
...validResolveForPending,
|
|
319
347
|
], sortKey, sortOrder);
|
|
320
348
|
const createdSchedules = sortJEScheduledTransactions(allScheduleRows.filter((schedule) => isCreatedStatus(schedule) && matchesTypeFilter(schedule)), sortKey, sortOrder);
|
|
321
349
|
// KPI summary — derived from unfiltered schedules so counts are always accurate
|
|
@@ -434,6 +462,13 @@ export function getExpenseAutomationJESchedulesView(state) {
|
|
|
434
462
|
kpiSummary,
|
|
435
463
|
pendingReviewSchedules,
|
|
436
464
|
pendingReviewTotal,
|
|
465
|
+
failedPostings: resolveSchedules,
|
|
466
|
+
// Exposed so a consumer can tell "still loading" and "the lookup failed" from "nothing has
|
|
467
|
+
// failed". The bucket keeps its rows on failure precisely so an error can be shown beside
|
|
468
|
+
// them, which is only possible if the state travels with them.
|
|
469
|
+
failedPostingsFetchState: failedJePostings.fetchState,
|
|
470
|
+
// Deprecated alias of failedPostings. Still read by the section's footer-form effect and by
|
|
471
|
+
// app-ui analytics; remove once both are repointed.
|
|
437
472
|
resolveSchedules: resolveSchedules,
|
|
438
473
|
accountSettingsView,
|
|
439
474
|
newScheduleView,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createSelector } from '@reduxjs/toolkit';
|
|
2
2
|
import omit from 'lodash/omit';
|
|
3
|
-
import { toMonthYearPeriodId } from '../../../commonStateTypes/timePeriod';
|
|
3
|
+
import { toMonthYearPeriodId, } from '../../../commonStateTypes/timePeriod';
|
|
4
4
|
import { getCurrentTenant, getIsAccountingProjectsEnabled, } from '../../../entity/tenant/tenantSelector';
|
|
5
5
|
import { getTransactionWithCOT } from '../../../entity/transaction/transactionHelper';
|
|
6
6
|
import { getSupportedTransactionsByIds } from '../../../entity/transaction/transactionSelector';
|
|
@@ -172,18 +172,35 @@ export function getExpenseAutomationTransactionView(state) {
|
|
|
172
172
|
review: monthYearPeriodId != null ? totalCountByReview[monthYearPeriodId] : 0,
|
|
173
173
|
autoCategorized: monthYearPeriodId != null ? totalCountByAutoCat[monthYearPeriodId] : 0,
|
|
174
174
|
};
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
//
|
|
175
|
+
// Tab badge counts, used exclusively by the navbar / tab strip badges. Both
|
|
176
|
+
// tabs are counted under the filters applied to the list, so a badge always
|
|
177
|
+
// equals the number of rows that tab shows.
|
|
178
|
+
//
|
|
179
|
+
// The ACTIVE tab prefers its own list count: the two are the same number (the
|
|
180
|
+
// backend's parent_tab_total_count mirrors total_count), but `uiState.totalCount`
|
|
181
|
+
// is restored by every cache path that skips a fetch -- switching back to an
|
|
182
|
+
// already-loaded period, or to a cached Completed sub-tab -- whereas the stored
|
|
183
|
+
// badge is only written when a response lands, so on those paths it would keep
|
|
184
|
+
// showing whatever was fetched last.
|
|
185
|
+
//
|
|
186
|
+
// The `in` check is load-bearing, not defensive: `totalCount` is a per-period
|
|
187
|
+
// map typed as `Record<MonthYearPeriodId, number>`, so indexing it is typed
|
|
188
|
+
// `number` but is `undefined` for any period the active (sub-)tab has not
|
|
189
|
+
// loaded -- the window between switching to a fresh sub-tab and its response
|
|
190
|
+
// landing. Falling back to the stored badge there keeps a real number in the
|
|
191
|
+
// label instead of blanking it.
|
|
179
192
|
const parentTotalCountByTab = expenseAutomationTransactionsViewState.parentTotalCountByTab;
|
|
193
|
+
const storedBadgeByTab = (tab) => monthYearPeriodId != null
|
|
194
|
+
? (parentTotalCountByTab[tab][monthYearPeriodId] ?? 0)
|
|
195
|
+
: 0;
|
|
196
|
+
const badgeForTab = (tab, listCountByPeriod) => selectedTransactionCategorizationTab === tab &&
|
|
197
|
+
monthYearPeriodId != null &&
|
|
198
|
+
monthYearPeriodId in listCountByPeriod
|
|
199
|
+
? listCountByPeriod[monthYearPeriodId]
|
|
200
|
+
: storedBadgeByTab(tab);
|
|
180
201
|
const parentTabTotalCountByTab = {
|
|
181
|
-
review:
|
|
182
|
-
|
|
183
|
-
: 0,
|
|
184
|
-
autoCategorized: monthYearPeriodId != null
|
|
185
|
-
? (parentTotalCountByTab.autoCategorized[monthYearPeriodId] ?? 0)
|
|
186
|
-
: 0,
|
|
202
|
+
review: badgeForTab('review', totalCountByReview),
|
|
203
|
+
autoCategorized: badgeForTab('autoCategorized', totalCountByAutoCat),
|
|
187
204
|
};
|
|
188
205
|
const fetchStateByTab = {
|
|
189
206
|
review: {
|
|
@@ -1,8 +1,35 @@
|
|
|
1
1
|
import { getAddressByAddressId } from '../../../entity/address/addressSelector';
|
|
2
|
+
import { invoicingPricePointDisplayAmount } from '../../../entity/invoicing/plan/planSelector';
|
|
2
3
|
import { getInvoicingCatalogListView, getInvoicingCatalogPrices, } from '../invoicingCatalogListView/invoicingCatalogListViewSelector';
|
|
3
4
|
import { invoicingFieldHelpForGroup } from '../invoicingConfigView/invoicingConfigViewSelector';
|
|
4
5
|
import { getInvoicingCustomerListView } from '../invoicingCustomerListView/invoicingCustomerListViewSelector';
|
|
5
6
|
import { getInvoicingSettings } from '../settingsView/settingsViewSelector';
|
|
7
|
+
/**
|
|
8
|
+
* Pricing models whose single-unit amount is a true per-unit rate — the only
|
|
9
|
+
* ones a Create Invoice line may be prefilled from. Typed against the entity
|
|
10
|
+
* union so a misspelt member fails to compile; a model added to the union
|
|
11
|
+
* still has to be classified here by hand (it is left out, so it fails closed:
|
|
12
|
+
* no prefill). The complement is `isTieredCatalogPricingModel`.
|
|
13
|
+
*/
|
|
14
|
+
const PER_UNIT_RATE_PRICING_MODELS = [
|
|
15
|
+
'per_unit',
|
|
16
|
+
'flat_fee',
|
|
17
|
+
];
|
|
18
|
+
/**
|
|
19
|
+
* Whether a price point's single-unit amount is a per-unit rate. A missing
|
|
20
|
+
* model means per-unit: the service defaults `pricing_model` to `per_unit`
|
|
21
|
+
* and older records may omit it.
|
|
22
|
+
*/
|
|
23
|
+
const hasPerUnitRate = (pricingModel) => pricingModel == null ||
|
|
24
|
+
pricingModel === '' ||
|
|
25
|
+
PER_UNIT_RATE_PRICING_MODELS.some((model) => model === pricingModel);
|
|
26
|
+
/**
|
|
27
|
+
* The amount to prefill on a Create Invoice line for a price point, or
|
|
28
|
+
* `undefined` when there is no safe per-unit rate to prefill.
|
|
29
|
+
*/
|
|
30
|
+
const planNamePrefillPrice = (pricePoint) => hasPerUnitRate(pricePoint.pricingModel)
|
|
31
|
+
? invoicingPricePointDisplayAmount(pricePoint)?.amount
|
|
32
|
+
: undefined;
|
|
6
33
|
/**
|
|
7
34
|
* Display name for a customer option: company, else the full name, else the
|
|
8
35
|
* email, else the raw id.
|
|
@@ -47,7 +74,7 @@ export const getCreateInvoiceFormView = (state) => {
|
|
|
47
74
|
.filter((subFamily) => (subFamily.name ?? '') !== '')
|
|
48
75
|
.map((subFamily) => ({
|
|
49
76
|
planName: plan.name ?? '',
|
|
50
|
-
price: subFamily
|
|
77
|
+
price: planNamePrefillPrice(subFamily),
|
|
51
78
|
subFamilyName: subFamily.name ?? '',
|
|
52
79
|
}))),
|
|
53
80
|
products: getInvoicingCatalogPrices(catalogListView.products),
|
|
@@ -3,7 +3,6 @@ import { toClassBasePayload } from '../../../entity/class/classPayload';
|
|
|
3
3
|
import { blankInvoicingCatalogSubFamily, isTieredCatalogPricingModel, } from './invoicingCatalogItemFormHelper';
|
|
4
4
|
const CURRENCY_CODE = 'USD';
|
|
5
5
|
const trimOrUndefined = (value) => value.trim() !== '' ? value.trim() : undefined;
|
|
6
|
-
const trimOrNull = (value) => value.trim() !== '' ? value.trim() : null;
|
|
7
6
|
const toProductCode = (value) => value
|
|
8
7
|
.trim()
|
|
9
8
|
.toLowerCase()
|
|
@@ -91,13 +90,24 @@ const buildSubFamilyPayload = (localData, subFamily, clearEmptyValues, lookup) =
|
|
|
91
90
|
/** `unit` is the item's unit of measure, not a currency; `CURRENCY_CODE` above prices Price points. */
|
|
92
91
|
const DEFAULT_CATALOG_ITEM_UNIT = 'USD';
|
|
93
92
|
/**
|
|
94
|
-
* Build the parent catalog-item request body from the form draft.
|
|
95
|
-
*
|
|
93
|
+
* Build the parent catalog-item request body from the form draft. Every field
|
|
94
|
+
* the form holds is sent, so a value cleared on the form is cleared on the
|
|
95
|
+
* item: the service reads an absent key as "leave it alone", which is why
|
|
96
|
+
* `description` travels as an empty string and `applicable_items` as an empty
|
|
97
|
+
* list rather than being dropped. Fields the form does not collect
|
|
98
|
+
* (`external_name`, `item_family_id`, `metadata`, …) stay off the body so an
|
|
99
|
+
* edit cannot overwrite them with a value nobody chose.
|
|
96
100
|
*/
|
|
97
101
|
export const localDataToSaveInvoicingCatalogItemPayload = (localData, isCreate, lookup) => {
|
|
98
|
-
const description = trimOrUndefined(localData.description);
|
|
99
102
|
const productCode = trimOrUndefined(localData.productCode) ?? toProductCode(localData.name);
|
|
100
103
|
const itemPayload = {
|
|
104
|
+
// `applicable_items` only means anything while the item is restricted;
|
|
105
|
+
// sending the empty list otherwise is what clears a restriction that was
|
|
106
|
+
// just lifted.
|
|
107
|
+
applicable_items: localData.itemApplicability === 'restricted'
|
|
108
|
+
? localData.applicableItems
|
|
109
|
+
: [],
|
|
110
|
+
description: trimOrUndefined(localData.description) ?? '',
|
|
101
111
|
enabled_for_checkout: true,
|
|
102
112
|
enabled_in_portal: true,
|
|
103
113
|
is_giftable: false,
|
|
@@ -107,41 +117,63 @@ export const localDataToSaveInvoicingCatalogItemPayload = (localData, isCreate,
|
|
|
107
117
|
name: localData.name.trim(),
|
|
108
118
|
product_code: productCode,
|
|
109
119
|
type: localData.catalogType,
|
|
120
|
+
// The form no longer collects a unit; an item that already has one keeps it.
|
|
121
|
+
unit: trimOrUndefined(localData.unit) ?? DEFAULT_CATALOG_ITEM_UNIT,
|
|
110
122
|
};
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (localData.itemApplicability === 'restricted' &&
|
|
118
|
-
localData.applicableItems.length > 0) {
|
|
119
|
-
itemPayload.applicable_items = localData.applicableItems;
|
|
123
|
+
// The external id is assigned by the billing service and shown read-only, so
|
|
124
|
+
// it is echoed back only when the item already has one: sending the blank a
|
|
125
|
+
// create starts with would clear an id assigned in the meantime.
|
|
126
|
+
const externalId = trimOrUndefined(localData.externalId);
|
|
127
|
+
if (externalId != null) {
|
|
128
|
+
itemPayload.external_id = externalId;
|
|
120
129
|
}
|
|
121
130
|
const subFamilies = localData.subFamilies.length > 0
|
|
122
131
|
? localData.subFamilies
|
|
123
132
|
: [blankInvoicingCatalogSubFamily()];
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
133
|
+
if (isCreate) {
|
|
134
|
+
itemPayload.sub_families = subFamilies.map((subFamily) => buildSubFamilyPayload(localData, subFamily, false, lookup));
|
|
135
|
+
return itemPayload;
|
|
136
|
+
}
|
|
137
|
+
// Only price points the service has already stored travel in an update: it
|
|
138
|
+
// addresses them by id, and a blank one has nothing to address. A price point
|
|
139
|
+
// added on the form is created by its own request once the item is saved,
|
|
140
|
+
// rather than riding along as a row the update cannot place.
|
|
141
|
+
itemPayload.sub_families = subFamilies
|
|
142
|
+
.filter((subFamily) => subFamily.id !== '')
|
|
143
|
+
.map((subFamily) => {
|
|
144
|
+
// Edits send the same body a create does. The narrower hand-built one
|
|
145
|
+
// this replaces had to be kept in step by hand and was not: it dropped
|
|
146
|
+
// `accounting_detail` once, and `billing_cycles`, `price`,
|
|
147
|
+
// `pricing_model`, `period`, the trial fields, `proration_type`,
|
|
148
|
+
// `free_quantity`, `is_taxable` and `tiers` were never added at all, so
|
|
149
|
+
// editing any of them looked saved and changed nothing.
|
|
150
|
+
const detail = buildSubFamilyPayload(localData, subFamily, true, lookup);
|
|
151
|
+
// `null` rather than omitted when empty: an absent key on an update
|
|
152
|
+
// reads as "leave it alone", which would make a cleared mapping
|
|
153
|
+
// impossible to save — the same reason `description` and
|
|
154
|
+
// `invoice_notes` are cleared rather than dropped above.
|
|
155
|
+
detail.accounting_detail = detail.accounting_detail ?? null;
|
|
156
|
+
// Never blank: the filter above leaves only price points the service has
|
|
157
|
+
// already stored, which is the only kind an update can address.
|
|
158
|
+
return { ...detail, id: subFamily.id };
|
|
159
|
+
});
|
|
146
160
|
return itemPayload;
|
|
147
161
|
};
|
|
162
|
+
/**
|
|
163
|
+
* The bodies that create the price points added on an edit form, one per
|
|
164
|
+
* request to `/catalog/products`.
|
|
165
|
+
*
|
|
166
|
+
* An update cannot carry them: it addresses price points by id, and these have
|
|
167
|
+
* none until the service assigns one. They are bound to their item by
|
|
168
|
+
* `item_id`, so this needs the saved item's id and runs after the item itself
|
|
169
|
+
* is saved.
|
|
170
|
+
*
|
|
171
|
+
* Empty values are left off rather than sent as `null`: there is nothing on a
|
|
172
|
+
* price point that does not exist yet to clear.
|
|
173
|
+
*/
|
|
174
|
+
export const localDataToCreateInvoicingCatalogProductPayloads = (localData, itemId, lookup) => localData.subFamilies
|
|
175
|
+
.filter((subFamily) => subFamily.id === '')
|
|
176
|
+
.map((subFamily) => ({
|
|
177
|
+
...buildSubFamilyPayload(localData, subFamily, false, lookup),
|
|
178
|
+
item_id: itemId,
|
|
179
|
+
}));
|