@zeniai/client-epic-state 5.2.13 → 5.2.14-betaRD1

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 (135) hide show
  1. package/lib/entity/approvalRule/approvalRuleConflict.d.ts +13 -0
  2. package/lib/entity/approvalRule/approvalRuleConflict.js +16 -1
  3. package/lib/entity/jeSchedules/jeScheduleHelper.d.ts +1 -0
  4. package/lib/entity/jeSchedules/jeScheduleHelper.js +7 -0
  5. package/lib/entity/jeSchedules/jeSchedulesPayload.d.ts +78 -1
  6. package/lib/entity/jeSchedules/jeSchedulesPayload.js +183 -4
  7. package/lib/entity/jeSchedules/jeSchedulesReducer.d.ts +4 -2
  8. package/lib/entity/jeSchedules/jeSchedulesReducer.js +30 -2
  9. package/lib/entity/jeSchedules/jeSchedulesSelector.d.ts +11 -2
  10. package/lib/entity/jeSchedules/jeSchedulesSelector.js +38 -3
  11. package/lib/entity/jeSchedules/jeSchedulesState.d.ts +123 -1
  12. package/lib/entity/jeSchedules/jeSchedulesTypes.d.ts +24 -1
  13. package/lib/entity/jeSchedules/jeSchedulesTypes.js +96 -1
  14. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  15. package/lib/entity/snackbar/snackbarTypes.js +3 -0
  16. package/lib/entity/transaction/payloadTypes/scheduleTransactionPayload.d.ts +2 -0
  17. package/lib/entity/transaction/payloadTypes/scheduleTransactionPayload.js +2 -0
  18. package/lib/entity/transaction/stateTypes/scheduleTransaction.d.ts +2 -0
  19. package/lib/epic.d.ts +9 -1
  20. package/lib/epic.js +16 -0
  21. package/lib/esm/entity/approvalRule/approvalRuleConflict.js +16 -1
  22. package/lib/esm/entity/jeSchedules/jeScheduleHelper.js +6 -0
  23. package/lib/esm/entity/jeSchedules/jeSchedulesPayload.js +183 -5
  24. package/lib/esm/entity/jeSchedules/jeSchedulesReducer.js +30 -2
  25. package/lib/esm/entity/jeSchedules/jeSchedulesSelector.js +36 -2
  26. package/lib/esm/entity/jeSchedules/jeSchedulesTypes.js +86 -0
  27. package/lib/esm/entity/snackbar/snackbarTypes.js +3 -0
  28. package/lib/esm/entity/transaction/payloadTypes/scheduleTransactionPayload.js +2 -0
  29. package/lib/esm/epic.js +16 -0
  30. package/lib/esm/index.js +17 -11
  31. package/lib/esm/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +6 -3
  32. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +9 -1
  33. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulesEpic.js +41 -5
  34. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchOneTimeAccrualEpic.js +45 -0
  35. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/ignoreRecommendedJeScheduleEpic.js +14 -2
  36. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/newSchedule/createOneTimeAccrualEpic.js +104 -0
  37. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/newSchedule/initializeNewScheduleEpic.js +25 -0
  38. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.js +138 -0
  39. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/newSchedule/searchTransactionsForNewScheduleEpic.js +79 -0
  40. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/oneTimeAccrualActionsEpic.js +180 -0
  41. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/revertJeScheduleEpic.js +111 -0
  42. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +28 -0
  43. package/lib/esm/view/expenseAutomationView/helpers/newScheduleLocalDataHelper.js +140 -0
  44. package/lib/esm/view/expenseAutomationView/payload/jeSchedulesPayload.js +55 -0
  45. package/lib/esm/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +319 -29
  46. package/lib/esm/view/expenseAutomationView/selectorTypes/jeNewScheduleViewSelectorTypes.js +1 -0
  47. package/lib/esm/view/expenseAutomationView/selectors/jeAccountSettingsViewSelector.js +1 -0
  48. package/lib/esm/view/expenseAutomationView/selectors/jeNewScheduleViewSelector.js +106 -0
  49. package/lib/esm/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +327 -22
  50. package/lib/esm/view/expenseAutomationView/types/jeSchedulesViewState.js +5 -6
  51. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/catalogDetailEditPayload.js +24 -7
  52. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +1 -1
  53. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewReducer.js +8 -1
  54. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +42 -8
  55. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/invoicingCatalogSubFamilyEpics.js +8 -2
  56. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +19 -10
  57. package/lib/esm/view/invoicing/invoicingCatalogItemDetailView/invoicingCatalogItemDetailViewSelector.js +2 -0
  58. package/lib/esm/view/invoicing/invoicingQboView/epics/fetchInvoicingAccountingPickersEpic.js +0 -1
  59. package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +15 -36
  60. package/lib/esm/view/scheduleView/scheduleListView/epics/fetchAccruedScheduleListEpic.js +1 -1
  61. package/lib/esm/view/scheduleView/scheduleListView/epics/fetchDownloadSchedulesEpic.js +7 -2
  62. package/lib/esm/view/scheduleView/scheduleListView/scheduleListReducer.js +5 -3
  63. package/lib/esm/view/spendManagement/billPay/billPaySetupApproverView/billPaySetupApproverViewSelector.js +20 -1
  64. package/lib/esm/view/spendManagement/reimbursement/remiSetupApproverView/remiSetupApproverViewSelector.js +20 -1
  65. package/lib/index.d.ts +24 -18
  66. package/lib/index.js +93 -48
  67. package/lib/view/expenseAutomationView/epics/common/fetchAllExpenseAutomationTabsEpic.js +6 -3
  68. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +9 -1
  69. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulesEpic.d.ts +3 -2
  70. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulesEpic.js +39 -3
  71. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchOneTimeAccrualEpic.d.ts +21 -0
  72. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchOneTimeAccrualEpic.js +49 -0
  73. package/lib/view/expenseAutomationView/epics/jeSchedule/ignoreRecommendedJeScheduleEpic.js +14 -2
  74. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/createOneTimeAccrualEpic.d.ts +9 -0
  75. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/createOneTimeAccrualEpic.js +108 -0
  76. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/initializeNewScheduleEpic.d.ts +8 -0
  77. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/initializeNewScheduleEpic.js +29 -0
  78. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.d.ts +9 -0
  79. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/saveNewScheduleEpic.js +142 -0
  80. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/searchTransactionsForNewScheduleEpic.d.ts +8 -0
  81. package/lib/view/expenseAutomationView/epics/jeSchedule/newSchedule/searchTransactionsForNewScheduleEpic.js +83 -0
  82. package/lib/view/expenseAutomationView/epics/jeSchedule/oneTimeAccrualActionsEpic.d.ts +13 -0
  83. package/lib/view/expenseAutomationView/epics/jeSchedule/oneTimeAccrualActionsEpic.js +187 -0
  84. package/lib/view/expenseAutomationView/epics/jeSchedule/revertJeScheduleEpic.d.ts +65 -0
  85. package/lib/view/expenseAutomationView/epics/jeSchedule/revertJeScheduleEpic.js +115 -0
  86. package/lib/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.d.ts +6 -0
  87. package/lib/view/expenseAutomationView/epics/jeSchedule/switchJESchedulesTabEpic.js +32 -0
  88. package/lib/view/expenseAutomationView/helpers/newScheduleLocalDataHelper.d.ts +36 -0
  89. package/lib/view/expenseAutomationView/helpers/newScheduleLocalDataHelper.js +145 -0
  90. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  91. package/lib/view/expenseAutomationView/payload/jeSchedulesPayload.d.ts +86 -3
  92. package/lib/view/expenseAutomationView/payload/jeSchedulesPayload.js +58 -1
  93. package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.d.ts +93 -12
  94. package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +321 -29
  95. package/lib/view/expenseAutomationView/reducers/missingReceiptsViewReducer.d.ts +1 -1
  96. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.d.ts +2 -2
  97. package/lib/view/expenseAutomationView/selectorTypes/jeAccountSettingsViewSelectorTypes.d.ts +1 -0
  98. package/lib/view/expenseAutomationView/selectorTypes/jeNewScheduleViewSelectorTypes.d.ts +42 -0
  99. package/lib/view/expenseAutomationView/selectorTypes/jeNewScheduleViewSelectorTypes.js +2 -0
  100. package/lib/view/expenseAutomationView/selectorTypes/jeSchedulesSelectorTypes.d.ts +41 -7
  101. package/lib/view/expenseAutomationView/selectors/jeAccountSettingsViewSelector.js +1 -0
  102. package/lib/view/expenseAutomationView/selectors/jeNewScheduleViewSelector.d.ts +21 -0
  103. package/lib/view/expenseAutomationView/selectors/jeNewScheduleViewSelector.js +112 -0
  104. package/lib/view/expenseAutomationView/selectors/jeSchedulesViewSelector.d.ts +34 -0
  105. package/lib/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +335 -21
  106. package/lib/view/expenseAutomationView/types/completedSubTab.d.ts +1 -1
  107. package/lib/view/expenseAutomationView/types/jeSchedulesViewState.d.ts +83 -8
  108. package/lib/view/expenseAutomationView/types/jeSchedulesViewState.js +8 -8
  109. package/lib/view/expenseAutomationView/types/reconciliationViewState.d.ts +1 -1
  110. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/catalogDetailEditPayload.d.ts +13 -21
  111. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/catalogDetailEditPayload.js +24 -7
  112. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.d.ts +7 -0
  113. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +4 -3
  114. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewReducer.d.ts +18 -9
  115. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewReducer.js +9 -2
  116. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.d.ts +15 -5
  117. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +43 -8
  118. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewState.d.ts +24 -0
  119. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/invoicingCatalogSubFamilyEpics.d.ts +1 -1
  120. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/invoicingCatalogSubFamilyEpics.js +8 -2
  121. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +18 -9
  122. package/lib/view/invoicing/invoicingCatalogItemDetailView/invoicingCatalogItemDetailViewSelector.d.ts +6 -0
  123. package/lib/view/invoicing/invoicingCatalogItemDetailView/invoicingCatalogItemDetailViewSelector.js +2 -0
  124. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingAccountingPickersEpic.js +0 -1
  125. package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.d.ts +13 -13
  126. package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +17 -38
  127. package/lib/view/scheduleView/scheduleListView/epics/fetchAccruedScheduleListEpic.js +1 -1
  128. package/lib/view/scheduleView/scheduleListView/epics/fetchDownloadSchedulesEpic.js +7 -2
  129. package/lib/view/scheduleView/scheduleListView/scheduleListReducer.d.ts +4 -2
  130. package/lib/view/scheduleView/scheduleListView/scheduleListReducer.js +5 -3
  131. package/lib/view/spendManagement/billPay/billPaySetupApproverView/billPaySetupApproverViewSelector.js +20 -1
  132. package/lib/view/spendManagement/billPay/billPaySetupApproverView/billPaySetupApproverViewSelectorTypes.d.ts +3 -0
  133. package/lib/view/spendManagement/reimbursement/remiSetupApproverView/remiSetupApproverViewSelector.js +20 -1
  134. package/lib/view/spendManagement/reimbursement/remiSetupApproverView/remiSetupApproverViewSelectorTypes.d.ts +3 -0
  135. package/package.json +1 -1
@@ -1,8 +1,44 @@
1
1
  import { createSelector } from '@reduxjs/toolkit';
2
2
  import { getInvoicingPlanById, getInvoicingPlansByIds, } from '../../../entity/invoicing/plan/planSelector';
3
3
  import { getInvoicingConfigView, invoicingFieldHelpForGroup, } from '../invoicingConfigView/invoicingConfigViewSelector';
4
- import { INVOICING_INCOME_ACCOUNT_TYPES, getInvoicingQboView, invoicingAccountHierarchyForTypes, isInvoicingAccountingPickerDataLoading, isInvoicingQboConnected, } from '../invoicingQboView/invoicingQboViewSelector';
4
+ import { getInvoicingQboView, isInvoicingAccountingPickerDataLoading, isInvoicingQboConnected, } from '../invoicingQboView/invoicingQboViewSelector';
5
+ import { planToCatalogDetailEditValues } from './catalogDetailEditPayload';
5
6
  import { INVOICING_CATALOG_APPLICABILITY_VALUES, INVOICING_CATALOG_PRICING_MODEL_VALUES, INVOICING_CATALOG_PRORATION_VALUES, INVOICING_CATALOG_TIER_TYPE_VALUES, entityToInvoicingCatalogItemFormLocalData, } from './invoicingCatalogItemFormConfig';
7
+ /** An unsaved item's inline-edit draft, before the record has loaded. */
8
+ export const initialCatalogDetailEditValues = () => ({
9
+ applicableItems: [],
10
+ description: '',
11
+ enabledForCheckout: true,
12
+ enabledInPortal: true,
13
+ giftClaimRedirectUrl: '',
14
+ includedInMrr: true,
15
+ isGiftable: false,
16
+ isShippable: false,
17
+ itemApplicability: 'all',
18
+ metered: false,
19
+ name: '',
20
+ productCode: '',
21
+ subFamilies: [],
22
+ unit: '',
23
+ usageCalculation: 'sum_of_usages',
24
+ });
25
+ /**
26
+ * The detail page's inline-edit form data: the stored draft once the user has
27
+ * touched the form, seeded from the loaded record until then. Memoized so the
28
+ * page receives a stable value — recomputing it per store change would reseed
29
+ * the form mid-edit and drop what the user had typed.
30
+ */
31
+ export const getInvoicingCatalogItemDetailEditLocalData = createSelector([
32
+ (state, id) => id != null
33
+ ? state.editInvoicingCatalogItemDetailViewState.detailEditDraftById[id]
34
+ : undefined,
35
+ (state, id) => id != null
36
+ ? getInvoicingPlanById(state.invoicingPlanState, id)
37
+ : undefined,
38
+ ], (storedDraft, plan) => storedDraft ??
39
+ (plan != null
40
+ ? planToCatalogDetailEditValues(plan)
41
+ : initialCatalogDetailEditValues()));
6
42
  export const getEditInvoicingCatalogItemDetailViewState = (state) => ({
7
43
  fetchState: state.editInvoicingCatalogItemDetailViewState.fetchState,
8
44
  error: state.editInvoicingCatalogItemDetailViewState.error,
@@ -49,17 +85,15 @@ export const getInvoicingCatalogItemFormView = (state, id) => {
49
85
  const applicableItemOptions = id != null
50
86
  ? allApplicableItemOptions.filter((option) => option.id !== id)
51
87
  : allApplicableItemOptions;
52
- const isQboConnected = isInvoicingQboConnected(state);
88
+ const qboView = isInvoicingQboConnected(state)
89
+ ? getInvoicingQboView(state)
90
+ : undefined;
53
91
  const { billingPeriodUnitOptions, catalogItemTypeOptions, catalogTierTypeOptionsByPricingModel, catalogTrialEndActionOptions, catalogTrialUnitOptions, } = getInvoicingConfigView(state);
54
92
  return {
55
93
  fieldHelp: invoicingFieldHelpForGroup(state, 'catalog'),
56
94
  isQboPickerDataLoading: isInvoicingAccountingPickerDataLoading(state),
57
- qboClassHierarchy: isQboConnected
58
- ? getInvoicingQboView(state).nestedClassHierarchy
59
- : [],
60
- qboIncomeAccountHierarchy: isQboConnected
61
- ? invoicingAccountHierarchyForTypes(state, INVOICING_INCOME_ACCOUNT_TYPES)
62
- : [],
95
+ qboAccountHierarchy: qboView?.nestedAccountHierarchy ?? [],
96
+ qboClassHierarchy: qboView?.nestedClassHierarchy ?? [],
63
97
  applicabilityValues: INVOICING_CATALOG_APPLICABILITY_VALUES,
64
98
  applicableItemOptions,
65
99
  localData,
@@ -2,6 +2,8 @@ import { of } from 'rxjs';
2
2
  import { catchError, filter, mergeMap } from 'rxjs/operators';
3
3
  import { mergeInvoicingPlanSubFamily } from '../../../entity/invoicing/plan/planReducer';
4
4
  import { createZeniAPIStatus, isSuccessResponse, } from '../../../responsePayload';
5
+ import { getInvoicingCatalogAccountingLookup } from '../invoicingQboView/invoicingQboViewSelector';
6
+ import { createSubFamilyFormToPayload } from './catalogDetailEditPayload';
5
7
  import { archiveInvoicingCatalogSubFamily, createInvoicingCatalogSubFamily, submitInvoicingCatalogItemFormFailure, submitInvoicingCatalogItemFormSuccess, } from './editInvoicingCatalogItemDetailViewReducer';
6
8
  const subFamilySuccessActions = (parentItemId, subFamilyPayload) => [
7
9
  mergeInvoicingPlanSubFamily(parentItemId, subFamilyPayload),
@@ -10,8 +12,12 @@ const subFamilySuccessActions = (parentItemId, subFamilyPayload) => [
10
12
  kind: 'plan',
11
13
  }),
12
14
  ];
13
- export const createInvoicingCatalogSubFamilyEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(createInvoicingCatalogSubFamily.match), mergeMap((action) => {
14
- const { data, parentItemId } = action.payload;
15
+ export const createInvoicingCatalogSubFamilyEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(createInvoicingCatalogSubFamily.match), mergeMap((action) => {
16
+ const { itemType, parentItemId, values } = action.payload;
17
+ const state = state$.value;
18
+ // Built here so the dialog can stay in picked ids: resolving one to the
19
+ // account record the payload carries needs the shared account list.
20
+ const data = createSubFamilyFormToPayload(parentItemId, itemType, values, getInvoicingCatalogAccountingLookup(state));
15
21
  const baseUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0`;
16
22
  return zeniAPI
17
23
  .postAndGetJSON(`${baseUrl}/catalog/products`, data)
@@ -1,31 +1,40 @@
1
1
  import { forkJoin, from } from 'rxjs';
2
2
  import { catchError, filter, mergeMap } from 'rxjs/operators';
3
+ import { getInvoicingPlanById } from '../../../entity/invoicing/plan/planSelector';
3
4
  import { mergeInvoicingPlanSubFamily, updateInvoicingPlans, } from '../../../entity/invoicing/plan/planReducer';
4
5
  import { updateInvoicingProducts } from '../../../entity/invoicing/product/productReducer';
5
6
  import { apiErrorSnackbarVariables } from '../../../entity/snackbar/snackbarApiError';
6
7
  import { openSnackbar } from '../../../entity/snackbar/snackbarReducer';
7
8
  import { createZeniAPIStatus, isSuccessResponse } from '../../../responsePayload';
8
9
  import { addInvoicingCatalogItem } from '../invoicingCatalogListView/invoicingCatalogListViewReducer';
9
- import { invoicingQboAccountByQboId, invoicingQboClassByQboId, } from '../invoicingQboView/invoicingQboViewSelector';
10
+ import { getInvoicingCatalogAccountingLookup } from '../invoicingQboView/invoicingQboViewSelector';
11
+ import { catalogDetailEditValuesToSavePayload, } from './catalogDetailEditPayload';
10
12
  import { localDataToSaveInvoicingCatalogItemPayload, } from './editInvoicingCatalogItemDetailViewPayload';
13
+ import { getInvoicingCatalogItemDetailEditLocalData } from './editInvoicingCatalogItemDetailViewSelector';
11
14
  import { saveInvoicingCatalogItem, submitInvoicingCatalogItemForm, submitInvoicingCatalogItemFormFailure, submitInvoicingCatalogItemFormSuccess, } from './editInvoicingCatalogItemDetailViewReducer';
12
15
  export const saveInvoicingCatalogItemEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter((action) => submitInvoicingCatalogItemForm.match(action) ||
13
16
  saveInvoicingCatalogItem.match(action)), mergeMap((action) => {
14
17
  const { id } = action.payload;
15
- // Draft-based create/edit form vs. detail-page inline edit (raw data).
18
+ const state = state$.value;
19
+ const accountingLookup = getInvoicingCatalogAccountingLookup(state);
20
+ // Both sources are form values, turned into request bodies here so the
21
+ // picked account and class become the records the service stores: the
22
+ // detail page's edit form, or the create/edit form's draft.
16
23
  let data;
24
+ let editProductUpdates;
17
25
  if (saveInvoicingCatalogItem.match(action)) {
18
- data = action.payload.data;
26
+ const plan = id != null
27
+ ? getInvoicingPlanById(state.invoicingPlanState, id)
28
+ : undefined;
29
+ const savePayload = catalogDetailEditValuesToSavePayload(getInvoicingCatalogItemDetailEditLocalData(state, id), accountingLookup, plan?.type ?? plan?.itemType, plan?.subFamilies);
30
+ data = savePayload.planData;
31
+ editProductUpdates = savePayload.productUpdates;
19
32
  }
20
33
  else {
21
- const viewState = state$.value.editInvoicingCatalogItemDetailViewState;
34
+ const viewState = state.editInvoicingCatalogItemDetailViewState;
22
35
  const localData = (id != null ? viewState.editDraftById[id] : viewState.newItemDraft) ??
23
36
  viewState.newItemDraft;
24
- const state = state$.value;
25
- data = localDataToSaveInvoicingCatalogItemPayload(localData, id == null, {
26
- accountByQboId: (qboId) => invoicingQboAccountByQboId(state, qboId),
27
- classByQboId: (qboId) => invoicingQboClassByQboId(state, qboId),
28
- });
37
+ data = localDataToSaveInvoicingCatalogItemPayload(localData, id == null, accountingLookup);
29
38
  }
30
39
  const baseUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0`;
31
40
  const path = id != null
@@ -88,7 +97,7 @@ export const saveInvoicingCatalogItemEpic = (actions$, state$, zeniAPI) => actio
88
97
  return resultActions;
89
98
  };
90
99
  const productUpdates = saveInvoicingCatalogItem.match(action)
91
- ? (action.payload.productUpdates ?? [])
100
+ ? (editProductUpdates ?? [])
92
101
  : [];
93
102
  if (productUpdates.length === 0) {
94
103
  return completeSave([]);
@@ -1,5 +1,6 @@
1
1
  import { getInvoicingPlanById } from '../../../entity/invoicing/plan/planSelector';
2
2
  import { getInvoicingConfigView, invoicingFieldHelpForGroup, } from '../invoicingConfigView/invoicingConfigViewSelector';
3
+ import { getInvoicingCatalogItemDetailEditLocalData } from '../editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector';
3
4
  export const getInvoicingCatalogItemDetail = (state, _kind, id) => {
4
5
  const { error, fetchState } = state.invoicingCatalogItemDetailViewState;
5
6
  const { billingPeriodUnitOptions, catalogItemLifecycleActionOptions, catalogItemTypeOptions, catalogProductLifecycleActionOptions, catalogTierTypeOptionsByPricingModel, catalogTrialEndActionOptions, catalogTrialUnitOptions, fetchState: configFetchState, } = getInvoicingConfigView(state);
@@ -10,6 +11,7 @@ export const getInvoicingCatalogItemDetail = (state, _kind, id) => {
10
11
  catalogTierTypeOptionsByPricingModel,
11
12
  configFetchState,
12
13
  periodUnitOptions: billingPeriodUnitOptions,
14
+ editLocalData: getInvoicingCatalogItemDetailEditLocalData(state, id),
13
15
  plan: getInvoicingPlanById(state.invoicingPlanState, id),
14
16
  trialEndActionOptions: catalogTrialEndActionOptions,
15
17
  trialUnitOptions: catalogTrialUnitOptions,
@@ -18,7 +18,6 @@ export const fetchInvoicingAccountingPickersEpic = (actions$, state$) => actions
18
18
  accountList.fetchState !== 'In-Progress') {
19
19
  // Same arguments the vendor and categorization screens pass, so this
20
20
  // shares their cached list rather than replacing it with a narrower one.
21
- // Invoicing narrows to income accounts when it builds its options.
22
21
  actions.push(fetchAccountList(INVOICING_ACCOUNT_LIST_KEY, true));
23
22
  }
24
23
  const classList = state.classListState;
@@ -1,8 +1,8 @@
1
1
  import { getAccountList, getNestedAccountListHierarchy, } from '../../accountList/accountListSelector';
2
2
  import { getClassList, getNestedClassListHierarchy, } from '../../classList/classListSelector';
3
3
  // The shared bucket, fetched with the same arguments as the other consumers so
4
- // whichever screen loads first satisfies the rest. Invoicing narrows to income
5
- // accounts when it builds its options, so it does not need its own copy.
4
+ // whichever screen loads first satisfies the rest. Invoicing renders the same
5
+ // accounts they do, so it does not need its own copy.
6
6
  export const INVOICING_ACCOUNT_LIST_KEY = 'accountList';
7
7
  /** Whether QBO is connected, without building the option lists to find out. */
8
8
  export const isInvoicingQboConnected = (state) => state.invoicingQboViewState.connection?.connected === true;
@@ -29,14 +29,6 @@ export const isInvoicingAccountingPickerDataLoading = (state) => {
29
29
  };
30
30
  /** The QBO connection fetch on its own, without building the bundled view. */
31
31
  export const getInvoicingQboConnectionFetchState = (state) => state.invoicingQboViewState.connectionFetchState;
32
- /**
33
- * Account types that can back invoicing revenue. Named here so screens ask for
34
- * "income accounts" rather than repeating the accounting service's vocabulary.
35
- */
36
- export const INVOICING_INCOME_ACCOUNT_TYPES = [
37
- 'income',
38
- 'other_income',
39
- ];
40
32
  /**
41
33
  * The account behind a picker value. The pickers submit the QuickBooks id, so
42
34
  * that is what callers hold; the payload builders need the whole entity to send
@@ -77,6 +69,18 @@ export const invoicingQboClassByQboId = (state, qboId) => {
77
69
  return undefined;
78
70
  }
79
71
  };
72
+ /**
73
+ * The resolver every catalog and settings payload builder needs: a picked
74
+ * QuickBooks id turned back into the account or class record the request body
75
+ * carries. Deliberately not memoized — epics call it once per action, where a
76
+ * fresh pair of closures costs nothing. Keep it out of `mapStateToProps`: the
77
+ * object identity changes per call, so a component reading it would re-render on
78
+ * every unrelated action.
79
+ */
80
+ export const getInvoicingCatalogAccountingLookup = (state) => ({
81
+ accountByQboId: (qboId) => invoicingQboAccountByQboId(state, qboId),
82
+ classByQboId: (qboId) => invoicingQboClassByQboId(state, qboId),
83
+ });
80
84
  /**
81
85
  * Accounts and classes as the invoicing pickers need them. Both come from the
82
86
  * shared accounting-service lists, so whatever another screen already loaded
@@ -84,17 +88,12 @@ export const invoicingQboClassByQboId = (state, qboId) => {
84
88
  * API stores; an account without one cannot be posted, so it is dropped rather
85
89
  * than offered.
86
90
  */
87
- const invoicingAccountingOptions = (state, accountTypes) => {
91
+ const invoicingAccountingOptions = (state) => {
88
92
  const { accounts } = getAccountList(state.accountState, state.accountListState, INVOICING_ACCOUNT_LIST_KEY);
89
93
  return accounts.flatMap((account) => {
90
94
  if (account.qboId == null || account.isDeleted === true) {
91
95
  return [];
92
96
  }
93
- if (accountTypes != null &&
94
- (account.accountType == null ||
95
- !accountTypes.includes(account.accountType))) {
96
- return [];
97
- }
98
97
  return [
99
98
  {
100
99
  id: String(account.qboId),
@@ -151,26 +150,6 @@ const invoicingAccountingClassOptions = (state) => {
151
150
  },
152
151
  ]);
153
152
  };
154
- /**
155
- * The account tree narrowed to the given account types. The tree itself carries
156
- * no account type, so the flat account list supplies that and the matching
157
- * `qboId`s are kept — which is why callers that only need "income accounts, as a
158
- * tree" do not have to hold a second flat list to filter with.
159
- */
160
- export const invoicingAccountHierarchyForTypes = (state, accountTypes) => {
161
- const allowedQboIds = new Set(invoicingAccountingOptions(state, accountTypes).map((account) => account.id));
162
- const keepMatching = (nodes) => nodes.flatMap((node) => {
163
- const children = keepMatching(node.children);
164
- const isMatch = node.qboId != null && allowedQboIds.has(String(node.qboId));
165
- // A parent that is not itself a match is still kept when it holds one, so
166
- // the tree's shape survives the filter.
167
- if (!isMatch && children.length === 0) {
168
- return [];
169
- }
170
- return [{ ...node, children }];
171
- });
172
- return keepMatching(getNestedAccountListHierarchy(state.accountListState, INVOICING_ACCOUNT_LIST_KEY));
173
- };
174
153
  /**
175
154
  * Single bundled view for the QBO integration settings screen — the
176
155
  * connection, the account and class options, sync health, and the fetch/action
@@ -30,7 +30,7 @@ export const fetchAccruedScheduleListEpic = (actions$, state$, zeniAPI) => actio
30
30
  je_schedule_type: scheduleType,
31
31
  sort_by: 'transaction_date',
32
32
  sort_order: 'desc',
33
- filter: 'all',
33
+ filter: action.payload.statusFilter,
34
34
  };
35
35
  return zeniAPI
36
36
  .getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/schedules?query=${encodeURIComponent(JSON.stringify(queryParam))}`)
@@ -7,9 +7,14 @@ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePay
7
7
  import { fetchDownloadSchedules, getState, updateDownloadScheduleFailure, updateDownloadScheduleSuccess, } from '../scheduleListReducer';
8
8
  import { getScheduleListKey } from '../scheduleListTypes';
9
9
  export const fetchDownloadSchedulesEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchDownloadSchedules.match), mergeMap((action) => {
10
- const { scheduleType, downloadCategories } = action.payload;
10
+ const { scheduleType, downloadCategories, statusFilter } = action.payload;
11
11
  const scheduleListState = state$.value.scheduleListState;
12
12
  // if parameters are provided, use those. else go to localdata
13
+ // Accrued is deliberately not served here. Its rows come back in a different shape, this
14
+ // epic writes them through updateJESchedules (the prepaid / fixed mapper), and
15
+ // doUpdateScheduleList never flips downloadState for accrued -- so a request would store the
16
+ // wrong data and then never report completion. Accrued downloads go through
17
+ // fetchAccruedScheduleList, which owns that shape end to end.
13
18
  let scheduleState;
14
19
  if (scheduleType !== 'accrued_expenses') {
15
20
  scheduleState = getState(scheduleListState, scheduleType);
@@ -34,7 +39,7 @@ export const fetchDownloadSchedulesEpic = (actions$, state$, zeniAPI) => actions
34
39
  base_txn_account_ids: downloadCategories,
35
40
  sort_by: 'transaction_date',
36
41
  sort_order: 'desc',
37
- filter: 'all',
42
+ filter: statusFilter,
38
43
  };
39
44
  return zeniAPI
40
45
  .getJSON(`${zeniAPI.apiEndPoints.accountMicroServiceBaseUrl}/1.0/schedules/download?query=${encodeURIComponent(JSON.stringify(queryParam))}`)
@@ -84,8 +84,8 @@ const scheduleListView = createSlice({
84
84
  reducers: {
85
85
  fetchDownloadSchedules: {
86
86
  reducer(draft, action) {
87
+ // Accrued is not downloaded through this action -- see fetchDownloadSchedulesEpic for why.
87
88
  if (action.payload.scheduleType !== 'accrued_expenses') {
88
- //if condition added for type narrowing
89
89
  const state = getState(draft, action.payload.scheduleType);
90
90
  const selectedTimeframe = action.payload.timeframeAndCategory.selectedTimeframe;
91
91
  const selectedCategoryId = action.payload.timeframeAndCategory.selectedCategoryId;
@@ -109,13 +109,14 @@ const scheduleListView = createSlice({
109
109
  updateState(draft, action.payload.scheduleType, state);
110
110
  }
111
111
  },
112
- prepare(scheduleType, timeframeAndCategory, downloadCategories, cacheOverride = false) {
112
+ prepare(scheduleType, timeframeAndCategory, downloadCategories, statusFilter = 'all', cacheOverride = false) {
113
113
  return {
114
114
  payload: {
115
115
  timeframeAndCategory,
116
116
  scheduleType,
117
117
  cacheOverride,
118
118
  downloadCategories,
119
+ statusFilter,
119
120
  },
120
121
  };
121
122
  },
@@ -151,12 +152,13 @@ const scheduleListView = createSlice({
151
152
  updateState(draft, action.payload.scheduleType, state);
152
153
  }
153
154
  },
154
- prepare(scheduleType, timeframeAndCategory, cacheOverride = false) {
155
+ prepare(scheduleType, timeframeAndCategory, cacheOverride = false, statusFilter = 'all') {
155
156
  return {
156
157
  payload: {
157
158
  timeframeAndCategory,
158
159
  scheduleType,
159
160
  cacheOverride,
161
+ statusFilter,
160
162
  },
161
163
  };
162
164
  },
@@ -7,7 +7,7 @@ import { getIsAccountingClassesEnabled } from '../../../../entity/tenant/tenantS
7
7
  import { getUserByUserId } from '../../../../entity/user/userSelector';
8
8
  import { getUserRoleByUserId } from '../../../../entity/userRole/userRoleSelector';
9
9
  export const getBillPaySetupApproverView = createSelector((state) => state.billPaySetupApproverViewState, (state) => state.approvalRuleState, (state) => state.userState, (billPaySetupApproverViewState, approvalRuleState, userState) => {
10
- const { approvalRulesDeleteStatusById, approvalRuleIds, fetchState, error, listeningToPusherEvent, newApprovalRuleId, } = billPaySetupApproverViewState;
10
+ const { approvalRulesDeleteStatusById, approvalRuleIds, approverListState, fetchState, error, listeningToPusherEvent, newApprovalRuleId, } = billPaySetupApproverViewState;
11
11
  const deleteStateList = Object.values(approvalRulesDeleteStatusById);
12
12
  const approvalRules = getApprovalRulesByIds(approvalRuleState, approvalRuleIds);
13
13
  const allApprovalRules = approvalRules.map((approvalRule) => {
@@ -35,10 +35,15 @@ export const getBillPaySetupApproverView = createSelector((state) => state.billP
35
35
  name: 'Require Info',
36
36
  code: 'require_info',
37
37
  };
38
+ const activeApproverIds = new Set(approverListState.approverIds);
39
+ const referencedApproverIds = new Set(allApprovalRules.flatMap((approvalRule) => approvalRule.steps.flatMap((step) => step.actors.map((actor) => actor.userId))));
40
+ const inactiveApproverIds = Array.from(referencedApproverIds).filter((userId) => !activeApproverIds.has(userId));
38
41
  return {
39
42
  allApprovalRules,
40
43
  deleteStatus: reduceAnyFetchState(deleteStateList),
41
44
  approvalRulesStatus,
45
+ approverListFetchStatus: approverListState.fetchStatus,
46
+ inactiveApproverIds,
42
47
  fetchState,
43
48
  error,
44
49
  listeningToPusherEvent,
@@ -57,6 +62,19 @@ export const getBillPaySetupApproverUpdateDataView = createSelector((state) => s
57
62
  };
58
63
  })
59
64
  .filter((approver) => approver.user != null);
65
+ const activeApproverIds = new Set(approverIds);
66
+ const currentRuleSteps = billPaySetupApproverViewUpdateData?.data?.steps ?? [];
67
+ const inactiveActorUserIds = new Set(currentRuleSteps
68
+ .flatMap((step) => step.actors.map((actor) => actor.userId))
69
+ .filter((userId) => userId != null && !activeApproverIds.has(userId)));
70
+ const inactiveActorApprovers = Array.from(inactiveActorUserIds)
71
+ .map((userId) => {
72
+ return {
73
+ user: getUserByUserId(userState, userId),
74
+ role: getUserRoleByUserId(userRoleState, userId),
75
+ };
76
+ })
77
+ .filter((approver) => approver.user != null);
60
78
  const allAttributes = attributes;
61
79
  const filteredApprovers = cloneDeep(allApprovers);
62
80
  const filteredAttributes = cloneDeep(attributes);
@@ -90,6 +108,7 @@ export const getBillPaySetupApproverUpdateDataView = createSelector((state) => s
90
108
  allApprovalRules,
91
109
  allApprovers,
92
110
  filteredApprovers,
111
+ inactiveActorApprovers,
93
112
  allAttributes,
94
113
  filteredAttributes,
95
114
  billPaySetupApproverViewUpdateData,
@@ -7,7 +7,7 @@ import { getUserByUserId } from '../../../../entity/user/userSelector';
7
7
  import { getUserRoleByUserId } from '../../../../entity/userRole/userRoleSelector';
8
8
  export const getRemiSetupApproverView = (state, filterAdmins = true) => {
9
9
  const { remiSetupApproverViewState, approvalRuleState, userState } = state;
10
- const { approvalRulesDeleteStatusById, approvalRuleIds, fetchState, error, listeningToPusherEvent, newApprovalRuleId, } = remiSetupApproverViewState;
10
+ const { approvalRulesDeleteStatusById, approvalRuleIds, approverListState, fetchState, error, listeningToPusherEvent, newApprovalRuleId, } = remiSetupApproverViewState;
11
11
  const deleteStateList = Object.values(approvalRulesDeleteStatusById);
12
12
  const approvalRules = getApprovalRulesByIds(approvalRuleState, approvalRuleIds);
13
13
  const allApprovalRules = approvalRules.map((approvalRule) => {
@@ -36,10 +36,15 @@ export const getRemiSetupApproverView = (state, filterAdmins = true) => {
36
36
  name: 'Require Info',
37
37
  code: 'require_info',
38
38
  };
39
+ const activeApproverIds = new Set(approverListState.approverIds);
40
+ const referencedApproverIds = new Set(allApprovalRules.flatMap((approvalRule) => approvalRule.steps.flatMap((step) => step.actors.map((actor) => actor.userId))));
41
+ const inactiveApproverIds = Array.from(referencedApproverIds).filter((userId) => !activeApproverIds.has(userId));
39
42
  return {
40
43
  allApprovalRules,
41
44
  deleteStatus: reduceAnyFetchState(deleteStateList),
42
45
  approvalRulesStatus,
46
+ approverListFetchStatus: approverListState.fetchStatus,
47
+ inactiveApproverIds,
43
48
  fetchState,
44
49
  error,
45
50
  listeningToPusherEvent,
@@ -74,6 +79,19 @@ export const getRemiSetupApproverUpdateDataView = createSelector((state) => stat
74
79
  };
75
80
  })
76
81
  .filter((approver) => approver.user != null);
82
+ const activeApproverIds = new Set(approverIds);
83
+ const currentRuleSteps = remiSetupApproverViewUpdateData?.data?.steps ?? [];
84
+ const inactiveActorUserIds = new Set(currentRuleSteps
85
+ .flatMap((step) => step.actors.map((actor) => actor.userId))
86
+ .filter((userId) => userId != null && !activeApproverIds.has(userId)));
87
+ const inactiveActorApprovers = Array.from(inactiveActorUserIds)
88
+ .map((userId) => {
89
+ return {
90
+ user: getUserByUserId(userState, userId),
91
+ role: getUserRoleByUserId(userRoleState, userId),
92
+ };
93
+ })
94
+ .filter((approver) => approver.user != null);
77
95
  const allAttributes = attributes;
78
96
  const filteredApprovers = cloneDeep(allApprovers);
79
97
  const filteredAttributes = cloneDeep(attributes);
@@ -108,6 +126,7 @@ export const getRemiSetupApproverUpdateDataView = createSelector((state) => stat
108
126
  allAttributes,
109
127
  filteredApprovers,
110
128
  filteredAttributes,
129
+ inactiveActorApprovers,
111
130
  allAccountingClasses,
112
131
  remiSetupApproverViewUpdateData,
113
132
  approversDetailsFetchStatus: reduceAllFetchState([