@zeniai/client-epic-state 5.2.41-beta5MM → 5.2.41-beta7MM

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 (196) hide show
  1. package/lib/entity/billPay/billTransaction/billTransactionReducer.d.ts +5 -1
  2. package/lib/entity/billPay/billTransaction/billTransactionReducer.js +17 -2
  3. package/lib/entity/invoicing/coupon/couponPayload.d.ts +8 -0
  4. package/lib/entity/invoicing/coupon/couponPayload.js +8 -0
  5. package/lib/entity/invoicing/coupon/couponState.d.ts +6 -0
  6. package/lib/entity/invoicing/dunningCase/dunningCaseTypes.d.ts +1 -1
  7. package/lib/entity/invoicing/plan/planSelector.d.ts +20 -0
  8. package/lib/entity/invoicing/plan/planSelector.js +42 -0
  9. package/lib/entity/invoicing/product/productPayload.d.ts +8 -1
  10. package/lib/entity/invoicing/product/productPayload.js +4 -1
  11. package/lib/entity/invoicing/product/productState.d.ts +16 -0
  12. package/lib/entity/jeSchedules/jeSchedulesReducer.d.ts +4 -1
  13. package/lib/entity/jeSchedules/jeSchedulesReducer.js +79 -5
  14. package/lib/entity/snackbar/snackbarTypes.d.ts +1 -1
  15. package/lib/entity/snackbar/snackbarTypes.js +1 -0
  16. package/lib/entity/task/taskState.d.ts +1 -1
  17. package/lib/epic.d.ts +4 -1
  18. package/lib/epic.js +10 -4
  19. package/lib/esm/entity/billPay/billTransaction/billTransactionReducer.js +16 -1
  20. package/lib/esm/entity/invoicing/coupon/couponPayload.js +8 -0
  21. package/lib/esm/entity/invoicing/plan/planSelector.js +39 -0
  22. package/lib/esm/entity/invoicing/product/productPayload.js +4 -1
  23. package/lib/esm/entity/jeSchedules/jeSchedulesReducer.js +78 -4
  24. package/lib/esm/entity/snackbar/snackbarTypes.js +1 -0
  25. package/lib/esm/epic.js +10 -4
  26. package/lib/esm/index.js +8 -7
  27. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/actualMatching/ignoreActualMatchingSuggestionEpic.js +3 -2
  28. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchFailedJePostingsEpic.js +39 -0
  29. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +14 -1
  30. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulesEpic.js +10 -13
  31. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/initializeJeScheduleLocalDataEpic.js +38 -17
  32. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/retryJeSchedulesEpic.js +17 -1
  33. package/lib/esm/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +15 -4
  34. package/lib/esm/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +6 -0
  35. package/lib/esm/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +74 -1
  36. package/lib/esm/view/expenseAutomationView/reducers/reconciliationViewReducer.js +80 -53
  37. package/lib/esm/view/expenseAutomationView/reducers/transactionsViewReducer.js +10 -0
  38. package/lib/esm/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +11 -2
  39. package/lib/esm/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +49 -14
  40. package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +28 -11
  41. package/lib/esm/view/invoicing/createInvoice/createInvoiceSelector.js +28 -1
  42. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +67 -35
  43. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewReducer.js +22 -11
  44. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +23 -4
  45. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/invoicingCatalogItemFormHelper.js +1 -7
  46. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +19 -5
  47. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/seedInvoicingCatalogItemFormDraftEpic.js +30 -7
  48. package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewPayload.js +16 -1
  49. package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewReducer.js +22 -11
  50. package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewSelector.js +69 -6
  51. package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewState.js +22 -0
  52. package/lib/esm/view/invoicing/editInvoicingCouponDetailView/fetchInvoicingCouponDetailPageEpic.js +22 -4
  53. package/lib/esm/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.js +18 -4
  54. package/lib/esm/view/invoicing/editInvoicingCouponDetailView/seedInvoicingCouponFormDraftEpic.js +29 -6
  55. package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.js +28 -12
  56. package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.js +37 -2
  57. package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/initializeInvoicingCustomerAddressEpic.js +43 -17
  58. package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.js +1 -1
  59. package/lib/esm/view/invoicing/editInvoicingCustomerDetailView/seedInvoicingCustomerFormDraftEpic.js +29 -6
  60. package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.js +30 -14
  61. package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.js +143 -8
  62. package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionFormPageEpic.js +4 -1
  63. package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/initializeInvoicingSubscriptionAddressEpic.js +44 -18
  64. package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +1 -1
  65. package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/seedInvoicingSubscriptionFormDraftEpic.js +29 -8
  66. package/lib/esm/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewSelector.js +12 -0
  67. package/lib/esm/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewState.js +6 -6
  68. package/lib/esm/view/invoicing/invoicingFormDraft.js +78 -0
  69. package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +57 -9
  70. package/lib/esm/view/recommendation/recommendationPayload.js +3 -1
  71. package/lib/esm/view/recommendation/recommendationReducer.js +27 -7
  72. package/lib/esm/view/recommendation/recommendationSelector.js +1 -0
  73. package/lib/esm/view/recommendation/submitRecommendationFeedbackEpic.js +20 -8
  74. package/lib/esm/view/spendManagement/billPay/billDetailView/billDetailViewReducer.js +87 -1
  75. package/lib/esm/view/spendManagement/billPay/billDetailView/billDetailViewSelector.js +1 -0
  76. package/lib/esm/view/spendManagement/billPay/billDetailView/billDetailViewState.js +4 -0
  77. package/lib/esm/view/spendManagement/billPay/billDetailView/epics/fetchBillRecommendationsEpic.js +97 -0
  78. package/lib/esm/view/spendManagement/billPay/billDetailView/epics/fetchRecommendationsForBill.js +85 -0
  79. package/lib/esm/view/spendManagement/billPay/editBillView/editBillViewReducer.js +18 -1
  80. package/lib/esm/view/spendManagement/billPay/editBillView/editBillViewSelector.js +3 -0
  81. package/lib/esm/view/spendManagement/billPay/editBillView/epics/applyBillRecommendationsToLocalStoreEpic.js +95 -0
  82. package/lib/index.d.ts +9 -8
  83. package/lib/index.js +54 -48
  84. package/lib/view/expenseAutomationView/epics/jeSchedule/actualMatching/ignoreActualMatchingSuggestionEpic.js +3 -2
  85. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchFailedJePostingsEpic.d.ts +20 -0
  86. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchFailedJePostingsEpic.js +43 -0
  87. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.d.ts +2 -2
  88. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulePageEpic.js +13 -0
  89. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulesEpic.js +8 -11
  90. package/lib/view/expenseAutomationView/epics/jeSchedule/initializeJeScheduleLocalDataEpic.js +38 -17
  91. package/lib/view/expenseAutomationView/epics/jeSchedule/retryJeSchedulesEpic.d.ts +2 -2
  92. package/lib/view/expenseAutomationView/epics/jeSchedule/retryJeSchedulesEpic.js +17 -1
  93. package/lib/view/expenseAutomationView/epics/transactionCategorization/fetchTransactionCategorizationEpic.js +15 -4
  94. package/lib/view/expenseAutomationView/helpers/transactionCategorizationLocalDataHelper.d.ts +1 -1
  95. package/lib/view/expenseAutomationView/payload/jeSchedulesPayload.d.ts +18 -0
  96. package/lib/view/expenseAutomationView/payload/transactionCategorizationPayload.d.ts +18 -7
  97. package/lib/view/expenseAutomationView/reducers/actualMatchingViewReducer.js +6 -0
  98. package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.d.ts +10 -3
  99. package/lib/view/expenseAutomationView/reducers/jeSchedulesViewReducer.js +76 -3
  100. package/lib/view/expenseAutomationView/reducers/reconciliationViewReducer.js +80 -53
  101. package/lib/view/expenseAutomationView/reducers/transactionsViewReducer.js +10 -0
  102. package/lib/view/expenseAutomationView/selectorTypes/actualMatchingViewSelectorTypes.d.ts +3 -0
  103. package/lib/view/expenseAutomationView/selectorTypes/jeSchedulesSelectorTypes.d.ts +3 -0
  104. package/lib/view/expenseAutomationView/selectorTypes/transactionsViewSelectorTypes.d.ts +8 -4
  105. package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.d.ts +1 -0
  106. package/lib/view/expenseAutomationView/selectors/actualMatchingViewSelector.js +12 -2
  107. package/lib/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +49 -14
  108. package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +27 -10
  109. package/lib/view/expenseAutomationView/types/jeSchedulesViewState.d.ts +16 -0
  110. package/lib/view/expenseAutomationView/types/transactionsViewState.d.ts +8 -5
  111. package/lib/view/invoicing/createInvoice/createInvoiceSelector.d.ts +7 -2
  112. package/lib/view/invoicing/createInvoice/createInvoiceSelector.js +28 -1
  113. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.d.ts +46 -5
  114. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +69 -36
  115. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewReducer.d.ts +3 -1
  116. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewReducer.js +22 -11
  117. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.d.ts +17 -0
  118. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewSelector.js +22 -3
  119. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewState.d.ts +2 -1
  120. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/invoicingCatalogItemFormHelper.d.ts +2 -2
  121. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/invoicingCatalogItemFormHelper.js +1 -7
  122. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/saveInvoicingCatalogItemEpic.js +17 -3
  123. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/seedInvoicingCatalogItemFormDraftEpic.d.ts +10 -6
  124. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/seedInvoicingCatalogItemFormDraftEpic.js +29 -6
  125. package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewPayload.d.ts +5 -1
  126. package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewPayload.js +16 -1
  127. package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewReducer.d.ts +3 -1
  128. package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewReducer.js +22 -11
  129. package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewSelector.d.ts +46 -0
  130. package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewSelector.js +68 -5
  131. package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewState.d.ts +21 -1
  132. package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewState.js +24 -1
  133. package/lib/view/invoicing/editInvoicingCouponDetailView/fetchInvoicingCouponDetailPageEpic.d.ts +9 -5
  134. package/lib/view/invoicing/editInvoicingCouponDetailView/fetchInvoicingCouponDetailPageEpic.js +22 -4
  135. package/lib/view/invoicing/editInvoicingCouponDetailView/saveInvoicingCouponEpic.js +18 -4
  136. package/lib/view/invoicing/editInvoicingCouponDetailView/seedInvoicingCouponFormDraftEpic.d.ts +10 -6
  137. package/lib/view/invoicing/editInvoicingCouponDetailView/seedInvoicingCouponFormDraftEpic.js +28 -5
  138. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.d.ts +5 -1
  139. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewReducer.js +29 -13
  140. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.d.ts +16 -7
  141. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewSelector.js +37 -2
  142. package/lib/view/invoicing/editInvoicingCustomerDetailView/editInvoicingCustomerDetailViewState.d.ts +8 -1
  143. package/lib/view/invoicing/editInvoicingCustomerDetailView/initializeInvoicingCustomerAddressEpic.d.ts +15 -10
  144. package/lib/view/invoicing/editInvoicingCustomerDetailView/initializeInvoicingCustomerAddressEpic.js +42 -16
  145. package/lib/view/invoicing/editInvoicingCustomerDetailView/saveInvoicingCustomerEpic.js +1 -1
  146. package/lib/view/invoicing/editInvoicingCustomerDetailView/seedInvoicingCustomerFormDraftEpic.d.ts +10 -6
  147. package/lib/view/invoicing/editInvoicingCustomerDetailView/seedInvoicingCustomerFormDraftEpic.js +28 -5
  148. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.d.ts +5 -1
  149. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewReducer.js +31 -15
  150. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.d.ts +79 -12
  151. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewSelector.js +145 -9
  152. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewState.d.ts +8 -1
  153. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/fetchInvoicingSubscriptionFormPageEpic.js +4 -1
  154. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/initializeInvoicingSubscriptionAddressEpic.d.ts +15 -11
  155. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/initializeInvoicingSubscriptionAddressEpic.js +42 -16
  156. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/saveInvoicingSubscriptionEpic.js +1 -1
  157. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/seedInvoicingSubscriptionFormDraftEpic.d.ts +10 -6
  158. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/seedInvoicingSubscriptionFormDraftEpic.js +28 -7
  159. package/lib/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewSelector.d.ts +10 -0
  160. package/lib/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewSelector.js +12 -0
  161. package/lib/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewReducer.d.ts +1 -1
  162. package/lib/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewState.d.ts +2 -2
  163. package/lib/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewState.js +6 -6
  164. package/lib/view/invoicing/invoicingFormDraft.d.ts +63 -0
  165. package/lib/view/invoicing/invoicingFormDraft.js +86 -0
  166. package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.d.ts +49 -0
  167. package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +59 -10
  168. package/lib/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewReducer.d.ts +1 -1
  169. package/lib/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewState.d.ts +1 -1
  170. package/lib/view/recommendation/recommendationPayload.d.ts +1 -0
  171. package/lib/view/recommendation/recommendationPayload.js +5 -0
  172. package/lib/view/recommendation/recommendationReducer.d.ts +7 -1
  173. package/lib/view/recommendation/recommendationReducer.js +28 -8
  174. package/lib/view/recommendation/recommendationSelector.d.ts +2 -0
  175. package/lib/view/recommendation/recommendationSelector.js +3 -1
  176. package/lib/view/recommendation/recommendationState.d.ts +2 -1
  177. package/lib/view/recommendation/submitRecommendationFeedbackEpic.d.ts +2 -2
  178. package/lib/view/recommendation/submitRecommendationFeedbackEpic.js +18 -6
  179. package/lib/view/spendManagement/billPay/billDetailView/billDetailViewPayload.d.ts +19 -0
  180. package/lib/view/spendManagement/billPay/billDetailView/billDetailViewReducer.d.ts +10 -1
  181. package/lib/view/spendManagement/billPay/billDetailView/billDetailViewReducer.js +88 -2
  182. package/lib/view/spendManagement/billPay/billDetailView/billDetailViewSelector.d.ts +1 -0
  183. package/lib/view/spendManagement/billPay/billDetailView/billDetailViewSelector.js +1 -0
  184. package/lib/view/spendManagement/billPay/billDetailView/billDetailViewState.d.ts +7 -0
  185. package/lib/view/spendManagement/billPay/billDetailView/billDetailViewState.js +4 -0
  186. package/lib/view/spendManagement/billPay/billDetailView/epics/fetchBillRecommendationsEpic.d.ts +37 -0
  187. package/lib/view/spendManagement/billPay/billDetailView/epics/fetchBillRecommendationsEpic.js +104 -0
  188. package/lib/view/spendManagement/billPay/billDetailView/epics/fetchRecommendationsForBill.d.ts +24 -0
  189. package/lib/view/spendManagement/billPay/billDetailView/epics/fetchRecommendationsForBill.js +92 -0
  190. package/lib/view/spendManagement/billPay/editBillView/editBillViewReducer.d.ts +4 -1
  191. package/lib/view/spendManagement/billPay/editBillView/editBillViewReducer.js +19 -2
  192. package/lib/view/spendManagement/billPay/editBillView/editBillViewSelector.d.ts +1 -0
  193. package/lib/view/spendManagement/billPay/editBillView/editBillViewSelector.js +3 -0
  194. package/lib/view/spendManagement/billPay/editBillView/epics/applyBillRecommendationsToLocalStoreEpic.d.ts +18 -0
  195. package/lib/view/spendManagement/billPay/editBillView/epics/applyBillRecommendationsToLocalStoreEpic.js +102 -0
  196. package/package.json +2 -2
@@ -7,13 +7,36 @@ const invoicingCustomerSelector_1 = require("../../../entity/invoicing/invoicing
7
7
  const editInvoicingCustomerDetailViewReducer_1 = require("./editInvoicingCustomerDetailViewReducer");
8
8
  const invoicingCustomerFormConfig_1 = require("./invoicingCustomerFormConfig");
9
9
  /**
10
- * Seeds the edit draft from the record the moment the detail fetch lands, so
11
- * the form has its values before it mounts. The reducer ignores the seed when a
12
- * draft already exists, which is what stops a background refetch from
13
- * overwriting edits in progress.
10
+ * Seeds the edit draft from the record, so the form has its values before it
11
+ * mounts. The reducer ignores a seed once the draft holds the record, which is
12
+ * what stops a background refetch from overwriting edits in progress.
13
+ *
14
+ * Opening the page counts as a trigger, not just the detail response: the page
15
+ * fetch is cache-guarded, so re-opening for a record already loaded this
16
+ * session issues no request at all — and a draft cleared in between (by a save
17
+ * or a cancel) would have nothing left to seed it.
14
18
  */
15
- const seedInvoicingCustomerFormDraftEpic = (actions$, state$) => actions$.pipe((0, operators_1.filter)(editInvoicingCustomerDetailViewReducer_1.updateInvoicingCustomerDetail.match), (0, operators_1.withLatestFrom)(state$), (0, operators_1.mergeMap)(([action, state]) => {
19
+ const seedInvoicingCustomerFormDraftEpic = (actions$, state$) => actions$.pipe((0, operators_1.filter)((action) => editInvoicingCustomerDetailViewReducer_1.updateInvoicingCustomerDetail.match(action) ||
20
+ editInvoicingCustomerDetailViewReducer_1.fetchInvoicingCustomerDetailPage.match(action)), (0, operators_1.withLatestFrom)(state$), (0, operators_1.mergeMap)(([action, state]) => {
16
21
  const { id } = action.payload;
22
+ if (id == null) {
23
+ return rxjs_1.EMPTY;
24
+ }
25
+ // The entity alone does not say the whole record is loaded — the list
26
+ // endpoints merge their own list-shaped rows into the same store. Only a
27
+ // completed detail load does, which is also exactly when the page's
28
+ // cache-guarded fetch issues no request and this is the sole seed.
29
+ // A forced refetch is the caller asking for fresh data: the response will
30
+ // seed, and seeding first from the entity as it stands would mark the
31
+ // draft loaded and lock that response out.
32
+ if (editInvoicingCustomerDetailViewReducer_1.fetchInvoicingCustomerDetailPage.match(action) &&
33
+ (action.payload.cacheOverride === true ||
34
+ state.editInvoicingCustomerDetailViewState.recordStateById[id]
35
+ ?.fetchState !== 'Completed')) {
36
+ return rxjs_1.EMPTY;
37
+ }
38
+ // Nothing to seed from on a cold open; the detail response triggers this
39
+ // again once it lands.
17
40
  const customer = (0, invoicingCustomerSelector_1.getInvoicingCustomerById)(state.invoicingCustomerState, id);
18
41
  if (customer == null) {
19
42
  return rxjs_1.EMPTY;
@@ -55,6 +55,10 @@ export declare const runInvoicingSubscriptionAction: import("@reduxjs/toolkit").
55
55
  id: ID;
56
56
  }, "editInvoicingSubscriptionDetailView/updateInvoicingSubscriptionDetailFailure">, updateInvoicingSubscriptionFormDraft: import("@reduxjs/toolkit").ActionCreatorWithPayload<UpdateInvoicingSubscriptionFormDraftPayload, "editInvoicingSubscriptionDetailView/updateInvoicingSubscriptionFormDraft">, submitInvoicingSubscriptionForm: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[payload: SubmitInvoicingSubscriptionFormPayload], SubmitInvoicingSubscriptionFormPayload, "editInvoicingSubscriptionDetailView/submitInvoicingSubscriptionForm", never, never>, submitInvoicingSubscriptionFormSuccess: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
57
57
  subscriptionId: ID;
58
- }, "editInvoicingSubscriptionDetailView/submitInvoicingSubscriptionFormSuccess">, submitInvoicingSubscriptionFormFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<ZeniAPIStatus<Record<string, unknown>>, "editInvoicingSubscriptionDetailView/submitInvoicingSubscriptionFormFailure">, resetEditInvoicingSubscriptionDetailView: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"editInvoicingSubscriptionDetailView/resetEditInvoicingSubscriptionDetailView">, initializeInvoicingSubscriptionAddress: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[payload: InitializeInvoicingSubscriptionAddressPayload], InitializeInvoicingSubscriptionAddressPayload, "editInvoicingSubscriptionDetailView/initializeInvoicingSubscriptionAddress", never, never>, syncInvoicingSubscriptionCoupons: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[payload: SyncInvoicingSubscriptionCouponsPayload], SyncInvoicingSubscriptionCouponsPayload, "editInvoicingSubscriptionDetailView/syncInvoicingSubscriptionCoupons", never, never>;
58
+ }, "editInvoicingSubscriptionDetailView/submitInvoicingSubscriptionFormSuccess">, submitInvoicingSubscriptionFormFailure: import("@reduxjs/toolkit").ActionCreatorWithPayload<ZeniAPIStatus<Record<string, unknown>>, "editInvoicingSubscriptionDetailView/submitInvoicingSubscriptionFormFailure">, resetEditInvoicingSubscriptionDetailView: import("@reduxjs/toolkit").ActionCreatorWithOptionalPayload<{
59
+ id?: ID;
60
+ } | undefined, "editInvoicingSubscriptionDetailView/resetEditInvoicingSubscriptionDetailView">, initializeInvoicingSubscriptionAddress: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[payload: InitializeInvoicingSubscriptionAddressPayload], InitializeInvoicingSubscriptionAddressPayload, "editInvoicingSubscriptionDetailView/initializeInvoicingSubscriptionAddress", never, never>, claimInvoicingSubscriptionShippingAddress: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
61
+ id: ID;
62
+ }, "editInvoicingSubscriptionDetailView/claimInvoicingSubscriptionShippingAddress">, syncInvoicingSubscriptionCoupons: import("@reduxjs/toolkit").ActionCreatorWithPreparedPayload<[payload: SyncInvoicingSubscriptionCouponsPayload], SyncInvoicingSubscriptionCouponsPayload, "editInvoicingSubscriptionDetailView/syncInvoicingSubscriptionCoupons", never, never>;
59
63
  declare const _default: import("redux").Reducer<EditInvoicingSubscriptionDetailViewState>;
60
64
  export default _default;
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  var _a;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.syncInvoicingSubscriptionCoupons = exports.initializeInvoicingSubscriptionAddress = exports.resetEditInvoicingSubscriptionDetailView = exports.submitInvoicingSubscriptionFormFailure = exports.submitInvoicingSubscriptionFormSuccess = exports.submitInvoicingSubscriptionForm = exports.updateInvoicingSubscriptionFormDraft = exports.updateInvoicingSubscriptionDetailFailure = exports.updateInvoicingSubscriptionDetail = exports.seedInvoicingSubscriptionFormDraft = exports.fetchInvoicingSubscriptionFormPage = exports.fetchInvoicingSubscriptionDetailPage = exports.fetchInvoicingSubscriptionDetail = exports.runInvoicingSubscriptionActionSuccess = exports.runInvoicingSubscriptionActionFailure = exports.runInvoicingSubscriptionAction = exports.initialState = void 0;
4
+ exports.syncInvoicingSubscriptionCoupons = exports.claimInvoicingSubscriptionShippingAddress = exports.initializeInvoicingSubscriptionAddress = exports.resetEditInvoicingSubscriptionDetailView = exports.submitInvoicingSubscriptionFormFailure = exports.submitInvoicingSubscriptionFormSuccess = exports.submitInvoicingSubscriptionForm = exports.updateInvoicingSubscriptionFormDraft = exports.updateInvoicingSubscriptionDetailFailure = exports.updateInvoicingSubscriptionDetail = exports.seedInvoicingSubscriptionFormDraft = exports.fetchInvoicingSubscriptionFormPage = exports.fetchInvoicingSubscriptionDetailPage = exports.fetchInvoicingSubscriptionDetail = exports.runInvoicingSubscriptionActionSuccess = exports.runInvoicingSubscriptionActionFailure = exports.runInvoicingSubscriptionAction = exports.initialState = void 0;
5
5
  const toolkit_1 = require("@reduxjs/toolkit");
6
6
  const rootActions_1 = require("../../../rootActions");
7
+ const invoicingFormDraft_1 = require("../invoicingFormDraft");
7
8
  const invoicingSubscriptionFormConfig_1 = require("./invoicingSubscriptionFormConfig");
8
9
  exports.initialState = {
9
10
  fetchState: 'Not-Started',
@@ -11,6 +12,7 @@ exports.initialState = {
11
12
  editDraftById: {},
12
13
  actionStateById: {},
13
14
  recordStateById: {},
15
+ shippingAddressOwnerId: undefined,
14
16
  newSubscriptionDraft: { ...invoicingSubscriptionFormConfig_1.initialInvoicingSubscriptionFormLocalData },
15
17
  savedSubscriptionId: undefined,
16
18
  hasValidState() {
@@ -69,15 +71,12 @@ const editInvoicingSubscriptionDetailView = (0, toolkit_1.createSlice)({
69
71
  fetchState: 'Error',
70
72
  error: action.payload.error,
71
73
  };
74
+ (0, invoicingFormDraft_1.applyInvoicingFormDraftFailure)(draft.editDraftById, action.payload.id, invoicingSubscriptionFormConfig_1.initialInvoicingSubscriptionFormLocalData);
72
75
  },
73
- /**
74
- * Seed the draft from the loaded record, once. Skipped when a draft already
75
- * exists so a late refetch cannot overwrite in-progress edits.
76
- */
76
+ /** Fill the draft from the loaded record — see `applyInvoicingFormDraftSeed`. */
77
77
  seedInvoicingSubscriptionFormDraft(draft, action) {
78
- if (draft.editDraftById[action.payload.id] == null) {
79
- draft.editDraftById[action.payload.id] = action.payload.localData;
80
- }
78
+ const { id, localData } = action.payload;
79
+ (0, invoicingFormDraft_1.applyInvoicingFormDraftSeed)(draft.editDraftById, id, localData);
81
80
  },
82
81
  fetchInvoicingSubscriptionFormPage: {
83
82
  reducer() {
@@ -91,7 +90,7 @@ const editInvoicingSubscriptionDetailView = (0, toolkit_1.createSlice)({
91
90
  updateInvoicingSubscriptionFormDraft(draft, action) {
92
91
  const { id, localData } = action.payload;
93
92
  if (id != null) {
94
- draft.editDraftById[id] = localData;
93
+ (0, invoicingFormDraft_1.applyInvoicingFormDraftEdit)(draft.editDraftById, id, localData);
95
94
  }
96
95
  else {
97
96
  draft.newSubscriptionDraft = localData;
@@ -111,19 +110,32 @@ const editInvoicingSubscriptionDetailView = (0, toolkit_1.createSlice)({
111
110
  draft.fetchState = 'Completed';
112
111
  draft.error = undefined;
113
112
  draft.savedSubscriptionId = action.payload.subscriptionId;
113
+ // An edit is refetched on success; keep the saved values on screen until
114
+ // that lands, and let it replace them.
115
+ (0, invoicingFormDraft_1.applyInvoicingFormDraftSaved)(draft.editDraftById, action.payload.subscriptionId);
114
116
  },
115
117
  submitInvoicingSubscriptionFormFailure(draft, action) {
116
118
  draft.fetchState = 'Error';
117
119
  draft.error = action.payload;
118
120
  },
119
- resetEditInvoicingSubscriptionDetailView(draft) {
121
+ /**
122
+ * Discard a form's draft — the subscription's own when `id` is given,
123
+ * the create draft otherwise. Scoped to the one form on purpose: another
124
+ * subscription's half-finished edit is not this form's to throw away.
125
+ * Clearing every draft at once is what `clearAll` is for.
126
+ */
127
+ resetEditInvoicingSubscriptionDetailView(draft, action) {
120
128
  draft.fetchState = 'Not-Started';
121
129
  draft.error = undefined;
122
130
  draft.savedSubscriptionId = undefined;
123
- draft.editDraftById = {};
124
- draft.newSubscriptionDraft = {
125
- ...invoicingSubscriptionFormConfig_1.initialInvoicingSubscriptionFormLocalData,
126
- };
131
+ const id = action.payload?.id;
132
+ if (id == null) {
133
+ draft.newSubscriptionDraft = {
134
+ ...invoicingSubscriptionFormConfig_1.initialInvoicingSubscriptionFormLocalData,
135
+ };
136
+ return;
137
+ }
138
+ delete draft.editDraftById[id];
127
139
  },
128
140
  // Trigger only — handled by initializeInvoicingSubscriptionAddressEpic.
129
141
  initializeInvoicingSubscriptionAddress: {
@@ -132,6 +144,10 @@ const editInvoicingSubscriptionDetailView = (0, toolkit_1.createSlice)({
132
144
  return { payload };
133
145
  },
134
146
  },
147
+ /** Record which subscription the shared shipping-address slot now belongs to. */
148
+ claimInvoicingSubscriptionShippingAddress(draft, action) {
149
+ draft.shippingAddressOwnerId = action.payload.id;
150
+ },
135
151
  // Trigger only — handled by syncInvoicingSubscriptionCouponsEpic.
136
152
  syncInvoicingSubscriptionCoupons: {
137
153
  reducer() { },
@@ -146,5 +162,5 @@ const editInvoicingSubscriptionDetailView = (0, toolkit_1.createSlice)({
146
162
  });
147
163
  },
148
164
  });
149
- _a = editInvoicingSubscriptionDetailView.actions, exports.runInvoicingSubscriptionAction = _a.runInvoicingSubscriptionAction, exports.runInvoicingSubscriptionActionFailure = _a.runInvoicingSubscriptionActionFailure, exports.runInvoicingSubscriptionActionSuccess = _a.runInvoicingSubscriptionActionSuccess, exports.fetchInvoicingSubscriptionDetail = _a.fetchInvoicingSubscriptionDetail, exports.fetchInvoicingSubscriptionDetailPage = _a.fetchInvoicingSubscriptionDetailPage, exports.fetchInvoicingSubscriptionFormPage = _a.fetchInvoicingSubscriptionFormPage, exports.seedInvoicingSubscriptionFormDraft = _a.seedInvoicingSubscriptionFormDraft, exports.updateInvoicingSubscriptionDetail = _a.updateInvoicingSubscriptionDetail, exports.updateInvoicingSubscriptionDetailFailure = _a.updateInvoicingSubscriptionDetailFailure, exports.updateInvoicingSubscriptionFormDraft = _a.updateInvoicingSubscriptionFormDraft, exports.submitInvoicingSubscriptionForm = _a.submitInvoicingSubscriptionForm, exports.submitInvoicingSubscriptionFormSuccess = _a.submitInvoicingSubscriptionFormSuccess, exports.submitInvoicingSubscriptionFormFailure = _a.submitInvoicingSubscriptionFormFailure, exports.resetEditInvoicingSubscriptionDetailView = _a.resetEditInvoicingSubscriptionDetailView, exports.initializeInvoicingSubscriptionAddress = _a.initializeInvoicingSubscriptionAddress, exports.syncInvoicingSubscriptionCoupons = _a.syncInvoicingSubscriptionCoupons;
165
+ _a = editInvoicingSubscriptionDetailView.actions, exports.runInvoicingSubscriptionAction = _a.runInvoicingSubscriptionAction, exports.runInvoicingSubscriptionActionFailure = _a.runInvoicingSubscriptionActionFailure, exports.runInvoicingSubscriptionActionSuccess = _a.runInvoicingSubscriptionActionSuccess, exports.fetchInvoicingSubscriptionDetail = _a.fetchInvoicingSubscriptionDetail, exports.fetchInvoicingSubscriptionDetailPage = _a.fetchInvoicingSubscriptionDetailPage, exports.fetchInvoicingSubscriptionFormPage = _a.fetchInvoicingSubscriptionFormPage, exports.seedInvoicingSubscriptionFormDraft = _a.seedInvoicingSubscriptionFormDraft, exports.updateInvoicingSubscriptionDetail = _a.updateInvoicingSubscriptionDetail, exports.updateInvoicingSubscriptionDetailFailure = _a.updateInvoicingSubscriptionDetailFailure, exports.updateInvoicingSubscriptionFormDraft = _a.updateInvoicingSubscriptionFormDraft, exports.submitInvoicingSubscriptionForm = _a.submitInvoicingSubscriptionForm, exports.submitInvoicingSubscriptionFormSuccess = _a.submitInvoicingSubscriptionFormSuccess, exports.submitInvoicingSubscriptionFormFailure = _a.submitInvoicingSubscriptionFormFailure, exports.resetEditInvoicingSubscriptionDetailView = _a.resetEditInvoicingSubscriptionDetailView, exports.initializeInvoicingSubscriptionAddress = _a.initializeInvoicingSubscriptionAddress, exports.claimInvoicingSubscriptionShippingAddress = _a.claimInvoicingSubscriptionShippingAddress, exports.syncInvoicingSubscriptionCoupons = _a.syncInvoicingSubscriptionCoupons;
150
166
  exports.default = editInvoicingSubscriptionDetailView.reducer;
@@ -1,8 +1,7 @@
1
+ import { Amount } from '../../../commonStateTypes/amount';
1
2
  import { FetchStateAndError, ID } from '../../../commonStateTypes/common';
2
3
  import { SelectorView } from '../../../commonStateTypes/viewAndReport/viewAndReport';
3
- import { InvoicingCoupon } from '../../../entity/invoicing/coupon/couponState';
4
4
  import { InvoicingFieldHelpByCode, InvoicingNamedOption, InvoicingSubscriptionLifecycleActionOption } from '../../../entity/invoicing/invoicingCommonPayload';
5
- import { InvoicingCustomer } from '../../../entity/invoicing/invoicingCustomer/invoicingCustomerState';
6
5
  import { InvoicingSubscription } from '../../../entity/invoicing/invoicingSubscription/invoicingSubscriptionState';
7
6
  import { InvoicingProduct } from '../../../entity/invoicing/product/productState';
8
7
  import { RootState } from '../../../rootStateTypes';
@@ -25,22 +24,35 @@ export interface EditInvoicingSubscriptionDetailView extends SelectorView {
25
24
  * The full data the subscription form screen needs in one selector: the draft
26
25
  * `localData` (seeded from the existing subscription in edit mode until the user
27
26
  * edits), the shipping-address label (from the shared Address screen), the
28
- * canonical billing-frequency value set, the raw option lists (customers, plans,
29
- * products, coupons), and the save state. The screen maps the raw lists/values
30
- * to its own option shapes, so this stays free of any web-components dependency.
27
+ * canonical billing-frequency value set, the picker option lists (customers,
28
+ * plans, add-ons, charges, coupons) and the save state. The options are plain
29
+ * `{label, value}` records, so this stays free of any web-components
30
+ * dependency while leaving the screen nothing to derive.
31
31
  */
32
32
  export interface InvoicingSubscriptionFormView extends SelectorView {
33
33
  /** Lifecycle action in flight, folded in from the record view. */
34
34
  actionState: FetchStateAndError;
35
+ /**
36
+ * Picker options, built here rather than by each screen so the create form
37
+ * and the detail page's inline edit cannot drift. `addons` and `charges` are
38
+ * already narrowed to what the selected plan allows.
39
+ */
40
+ addons: InvoicingSubscriptionOption[];
35
41
  /** Cancel reasons from `/1.0/config`, so the dialog hardcodes nothing. */
36
42
  cancelReasonCodeOptions: InvoicingNamedOption[];
37
- coupons: InvoicingCoupon[];
38
- customers: InvoicingCustomer[];
43
+ charges: InvoicingSubscriptionOption[];
44
+ coupons: InvoicingSubscriptionOption[];
45
+ customers: InvoicingSubscriptionOption[];
46
+ fieldHelp: InvoicingFieldHelpByCode;
39
47
  /**
40
- * Backend help copy for this screen's field group, keyed by field code.
41
- * Folded in here so the screen never reaches for the config view itself.
48
+ * True while the subscription being edited is still on its way to the form.
49
+ * The form holds a skeleton on this rather than mount: it seeds
50
+ * react-hook-form once, at first render, so fields mounted against the blank
51
+ * draft stay blank and the first change writes that blankness back over the
52
+ * subscription. A later refetch does not raise this again, so a refresh
53
+ * leaves the form on screen instead of blanking it mid-edit.
42
54
  */
43
- fieldHelp: InvoicingFieldHelpByCode;
55
+ isFormLoading: boolean;
44
56
  localData: InvoicingSubscriptionFormLocalData;
45
57
  mode: 'create' | 'edit';
46
58
  /**
@@ -54,16 +66,71 @@ export interface InvoicingSubscriptionFormView extends SelectorView {
54
66
  * real values.
55
67
  */
56
68
  pageFetchState: FetchStateAndError;
57
- plans: InvoicingProduct[];
58
- products: InvoicingProduct[];
69
+ plans: InvoicingSubscriptionOption[];
59
70
  recordState: FetchStateAndError;
60
71
  /** Config-owned edit/lifecycle rules, folded in so the screen reads one selector. */
61
72
  subscriptionCommercialEditAllowedFromStatuses: string[];
62
73
  subscriptionLifecycleActionOptions: InvoicingSubscriptionLifecycleActionOption[];
63
74
  subscriptionMetadataEditAllowedFromStatuses: string[];
75
+ /**
76
+ * A shipping address entered on the Address screen that the subscription has
77
+ * not saved yet. The inline-edit form cannot see it — the address lives in the
78
+ * shared address store, not in a field — so the detail page reads this to
79
+ * offer Save, or the address could never be linked.
80
+ */
81
+ hasUnsavedShippingAddress?: boolean;
64
82
  savedSubscriptionId?: ID;
65
83
  shippingAddressLabel?: string;
66
84
  subscription?: InvoicingSubscription;
67
85
  }
68
86
  export declare const getEditInvoicingSubscriptionDetailViewState: (state: RootState, invoicingSubscriptionID?: ID) => EditInvoicingSubscriptionDetailView;
87
+ /**
88
+ * The add-ons / one-time charges a plan actually accepts.
89
+ *
90
+ * A catalog item is either `restricted` — it names the item ids it may be
91
+ * subscribed alongside in `applicableItems` — or unrestricted, in which case
92
+ * every item is fair game. Matching is on `itemID` (the catalog item the price
93
+ * row was flattened from), not the price code, because applicability is
94
+ * declared item-to-item.
95
+ *
96
+ * Fails open where the rule is unknown: no plan selected yet, a plan that is
97
+ * not `restricted`, or a `restricted` plan whose allow-list never came down all
98
+ * return the full list. The create/update endpoint rejects a disallowed item
99
+ * with a 422 regardless, so offering one too many costs a server error the user
100
+ * already gets today, where hiding one too few makes a legitimate add-on
101
+ * unreachable with nothing on screen to explain why.
102
+ *
103
+ * An allow-list that arrived *empty* is not unknown — a restricted item is
104
+ * saved with `applicable_items: []` when nothing was picked — so that narrows
105
+ * to nothing, as the server does.
106
+ *
107
+ * Lives here rather than in a screen so the create/edit form and the detail
108
+ * page's inline edit narrow their pickers by the same rule.
109
+ */
110
+ export declare const getInvoicingItemsAllowedForPlan: (products: InvoicingProduct[], plan: InvoicingProduct | undefined) => InvoicingProduct[];
111
+ /**
112
+ * One entry in a subscription-form picker, ready for the screen to render.
113
+ *
114
+ * `label` and `value` are the whole contract for a coupon or a customer. A
115
+ * catalog item adds the pieces its subtitle is built from — `productCode`,
116
+ * `price`, `billingPeriodUnit` — rather than a finished string, because
117
+ * formatting money is the UI's job: CES holds {@link Amount} as data and has
118
+ * no currency formatter. `subtitle` is the escape hatch for a caller that
119
+ * already has the line it wants.
120
+ *
121
+ * A plan carries the extra fields the form prefills from it: its paid-cycle
122
+ * limit, its cadence, and its trial length.
123
+ */
124
+ export interface InvoicingSubscriptionOption {
125
+ label: string;
126
+ value: string;
127
+ billingCycles?: number;
128
+ billingPeriod?: number;
129
+ billingPeriodUnit?: string;
130
+ price?: Amount;
131
+ productCode?: string;
132
+ subtitle?: string;
133
+ trialPeriod?: number;
134
+ trialPeriodUnit?: string;
135
+ }
69
136
  export declare const getInvoicingSubscriptionFormView: (state: RootState, invoicingSubscriptionID?: ID) => InvoicingSubscriptionFormView;
@@ -1,13 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getInvoicingSubscriptionFormView = exports.getEditInvoicingSubscriptionDetailViewState = void 0;
3
+ exports.getInvoicingSubscriptionFormView = exports.getInvoicingItemsAllowedForPlan = exports.getEditInvoicingSubscriptionDetailViewState = void 0;
4
4
  const reduceFetchState_1 = require("../../../commonStateTypes/reduceFetchState");
5
+ const addressSelector_1 = require("../../../entity/address/addressSelector");
5
6
  const invoicingSubscriptionSelector_1 = require("../../../entity/invoicing/invoicingSubscription/invoicingSubscriptionSelector");
6
7
  const addressViewSelector_1 = require("../../addressView/addressViewSelector");
7
8
  const couponViewSelector_1 = require("../couponView/couponViewSelector");
8
9
  const invoicingCatalogListViewSelector_1 = require("../invoicingCatalogListView/invoicingCatalogListViewSelector");
9
10
  const invoicingConfigViewSelector_1 = require("../invoicingConfigView/invoicingConfigViewSelector");
10
11
  const invoicingCustomerListViewSelector_1 = require("../invoicingCustomerListView/invoicingCustomerListViewSelector");
12
+ const invoicingFormDraft_1 = require("../invoicingFormDraft");
11
13
  const invoicingSubscriptionFormConfig_1 = require("./invoicingSubscriptionFormConfig");
12
14
  const invoicingSubscriptionFormConfig_2 = require("./invoicingSubscriptionFormConfig");
13
15
  const getEditInvoicingSubscriptionDetailViewState = (state, invoicingSubscriptionID) => {
@@ -27,6 +29,130 @@ const getEditInvoicingSubscriptionDetailViewState = (state, invoicingSubscriptio
27
29
  };
28
30
  };
29
31
  exports.getEditInvoicingSubscriptionDetailViewState = getEditInvoicingSubscriptionDetailViewState;
32
+ /**
33
+ * Whether the shipping address entered on the Address screen is a change the
34
+ * subscription has not saved. Presence alone cannot say so: the detail page seeds
35
+ * the address store with the saved address on load so the Address screen opens
36
+ * prefilled. Only a pending address that differs from the saved one — or one
37
+ * where nothing is saved yet — is an unsaved change. Compared on what the user
38
+ * enters; a saved address's coordinates and timestamps are left out.
39
+ */
40
+ const isShippingAddressUnsaved = (state, id, savedShippingAddressId) => {
41
+ // The slot is keyed by address type alone; only a draft this subscription owns counts.
42
+ if (id == null ||
43
+ state.editInvoicingSubscriptionDetailViewState.shippingAddressOwnerId !== id) {
44
+ return false;
45
+ }
46
+ const pending = state.addressViewState.newAddressState?.[invoicingSubscriptionFormConfig_2.INVOICING_SUBSCRIPTION_SHIPPING_ADDRESS_TYPE]?.addressToCreate;
47
+ if (pending == null) {
48
+ return false;
49
+ }
50
+ const saved = savedShippingAddressId != null
51
+ ? (0, addressSelector_1.getAddressByAddressId)(state.addressState, savedShippingAddressId)
52
+ : undefined;
53
+ if (saved == null) {
54
+ return true;
55
+ }
56
+ return !(pending.street.join('\u0000') === saved.street.join('\u0000') &&
57
+ pending.city === saved.city &&
58
+ pending.state === saved.state &&
59
+ pending.postalCode === saved.postalCode &&
60
+ pending.country === saved.country &&
61
+ (pending.countryCode ?? '') === (saved.countryCode ?? ''));
62
+ };
63
+ /**
64
+ * The add-ons / one-time charges a plan actually accepts.
65
+ *
66
+ * A catalog item is either `restricted` — it names the item ids it may be
67
+ * subscribed alongside in `applicableItems` — or unrestricted, in which case
68
+ * every item is fair game. Matching is on `itemID` (the catalog item the price
69
+ * row was flattened from), not the price code, because applicability is
70
+ * declared item-to-item.
71
+ *
72
+ * Fails open where the rule is unknown: no plan selected yet, a plan that is
73
+ * not `restricted`, or a `restricted` plan whose allow-list never came down all
74
+ * return the full list. The create/update endpoint rejects a disallowed item
75
+ * with a 422 regardless, so offering one too many costs a server error the user
76
+ * already gets today, where hiding one too few makes a legitimate add-on
77
+ * unreachable with nothing on screen to explain why.
78
+ *
79
+ * An allow-list that arrived *empty* is not unknown — a restricted item is
80
+ * saved with `applicable_items: []` when nothing was picked — so that narrows
81
+ * to nothing, as the server does.
82
+ *
83
+ * Lives here rather than in a screen so the create/edit form and the detail
84
+ * page's inline edit narrow their pickers by the same rule.
85
+ */
86
+ const getInvoicingItemsAllowedForPlan = (products, plan) => {
87
+ const allowedItemIDs = plan?.applicableItems;
88
+ if (plan?.itemApplicability !== 'restricted' || allowedItemIDs == null) {
89
+ return products;
90
+ }
91
+ return products.filter((product) => product.itemID != null && allowedItemIDs.includes(product.itemID));
92
+ };
93
+ exports.getInvoicingItemsAllowedForPlan = getInvoicingItemsAllowedForPlan;
94
+ /**
95
+ * A price row can only be subscribed by its price code, so one without a code
96
+ * is not an option the form can offer.
97
+ */
98
+ const hasInvoicingPriceCode = (product) => product.priceCode != null && product.priceCode.trim() !== '';
99
+ /**
100
+ * `displayPrice` is the service-computed single-unit amount. Tiered, volume and
101
+ * stairstep price points store `price` as `0` because the money lives in their
102
+ * tiers, so `price` is only a fallback for responses predating `displayPrice`.
103
+ */
104
+ const invoicingUnitPrice = (product) => product.displayPrice ?? product.price;
105
+ const toInvoicingPlanOption = (plan) => ({
106
+ billingCycles: plan.billingCycles,
107
+ // The price point's own cadence: the backend derives billing period from the
108
+ // price point and rejects a subscription that claims a different one.
109
+ billingPeriod: plan.period,
110
+ billingPeriodUnit: plan.periodUnit,
111
+ label: plan.name ?? plan.id,
112
+ price: invoicingUnitPrice(plan),
113
+ productCode: plan.productCode,
114
+ trialPeriod: plan.trialPeriod,
115
+ trialPeriodUnit: plan.trialPeriodUnit,
116
+ value: plan.priceCode,
117
+ });
118
+ const toInvoicingCatalogItemOption = (product) => ({
119
+ // A one-time charge has no cadence, so its price is quoted flat rather than
120
+ // per period.
121
+ billingPeriodUnit: product.itemType === 'charge' ? undefined : product.periodUnit,
122
+ label: product.name ?? product.id,
123
+ price: invoicingUnitPrice(product),
124
+ productCode: product.productCode,
125
+ value: product.priceCode,
126
+ });
127
+ const toInvoicingCouponOption = (coupon) => ({
128
+ label: coupon.name ?? coupon.couponCode ?? coupon.id,
129
+ // Create/apply/remove coupon APIs resolve by coupon_code.
130
+ value: coupon.couponCode ?? coupon.id,
131
+ });
132
+ /**
133
+ * Company first, then the person's name, then the email — whichever the
134
+ * customer actually has. The id is the last resort so a record with no
135
+ * identifying field at all is still selectable rather than a blank row.
136
+ */
137
+ const invoicingCustomerDisplayName = (customer) => {
138
+ const fullName = [customer.firstName, customer.lastName]
139
+ .filter((part) => part != null && part.trim() !== '')
140
+ .join(' ')
141
+ .trim();
142
+ const company = customer.company?.trim() ?? '';
143
+ const email = customer.email?.trim() ?? '';
144
+ return company !== ''
145
+ ? company
146
+ : fullName !== ''
147
+ ? fullName
148
+ : email !== ''
149
+ ? email
150
+ : customer.id;
151
+ };
152
+ const toInvoicingCustomerOption = (customer) => ({
153
+ label: invoicingCustomerDisplayName(customer),
154
+ value: customer.id,
155
+ });
30
156
  const getInvoicingSubscriptionFormView = (state, invoicingSubscriptionID) => {
31
157
  const { editDraftById, error, fetchState, newSubscriptionDraft, savedSubscriptionId, } = state.editInvoicingSubscriptionDetailViewState;
32
158
  const mode = invoicingSubscriptionID != null ? 'edit' : 'create';
@@ -37,8 +163,7 @@ const getInvoicingSubscriptionFormView = (state, invoicingSubscriptionID) => {
37
163
  // `seedInvoicingSubscriptionFormDraftEpic` when the detail load lands. Until
38
164
  // then the page is showing its skeleton.
39
165
  const localData = invoicingSubscriptionID != null
40
- ? (editDraftById[invoicingSubscriptionID] ??
41
- invoicingSubscriptionFormConfig_1.initialInvoicingSubscriptionFormLocalData)
166
+ ? (0, invoicingFormDraft_1.getInvoicingFormDraftLocalData)(editDraftById, invoicingSubscriptionID, invoicingSubscriptionFormConfig_1.initialInvoicingSubscriptionFormLocalData)
42
167
  : newSubscriptionDraft;
43
168
  // Shipping address is captured via the shared Address screen and seeded there
44
169
  // (looked up in the shared address store by `shippingAddressId`) on detail
@@ -47,9 +172,17 @@ const getInvoicingSubscriptionFormView = (state, invoicingSubscriptionID) => {
47
172
  const shippingAddressLabel = newAddress?.addressToCreate != null
48
173
  ? (0, addressViewSelector_1.getFormattedAddress)(newAddress.addressToCreate)
49
174
  : undefined;
175
+ const hasUnsavedShippingAddress = isShippingAddressUnsaved(state, invoicingSubscriptionID, subscription?.shippingAddressId);
50
176
  const catalogListView = (0, invoicingCatalogListViewSelector_1.getInvoicingCatalogListView)(state);
51
- const planPrices = (0, invoicingCatalogListViewSelector_1.getInvoicingCatalogPrices)(catalogListView.plans);
52
- const addonAndChargePrices = (0, invoicingCatalogListViewSelector_1.getInvoicingCatalogPrices)(catalogListView.products);
177
+ const planPrices = (0, invoicingCatalogListViewSelector_1.getInvoicingCatalogPrices)(catalogListView.plans).filter(hasInvoicingPriceCode);
178
+ // A restricted plan names the catalog items it may be sold with, so the
179
+ // pickers offer only those: subscription create rejects the rest with a 422,
180
+ // and an option that can only fail is not a choice. The applicability rides
181
+ // on the price rows already in the store, so this is a join, not a refetch.
182
+ const allowedCatalogPrices = (0, exports.getInvoicingItemsAllowedForPlan)((0, invoicingCatalogListViewSelector_1.getInvoicingCatalogPrices)(catalogListView.products), planPrices.find((plan) => plan.priceCode === localData.planPriceCode)).filter(hasInvoicingPriceCode);
183
+ const catalogItemOptionsOfType = (itemType) => allowedCatalogPrices
184
+ .filter((product) => product.itemType === itemType)
185
+ .map(toInvoicingCatalogItemOption);
53
186
  // In create mode there is nothing to load, so the record state only applies
54
187
  // when editing an existing subscription.
55
188
  const recordState = invoicingSubscriptionID != null
@@ -69,15 +202,18 @@ const getInvoicingSubscriptionFormView = (state, invoicingSubscriptionID) => {
69
202
  subscriptionLifecycleActionOptions: configState.subscriptionLifecycleActionOptions,
70
203
  subscriptionMetadataEditAllowedFromStatuses: configState.subscriptionMetadataEditAllowedFromStatuses,
71
204
  recordState,
205
+ isFormLoading: (0, invoicingFormDraft_1.isInvoicingFormDraftPending)(editDraftById, invoicingSubscriptionID),
72
206
  fieldHelp: (0, invoicingConfigViewSelector_1.invoicingFieldHelpForGroup)(state, 'subscription'),
73
- coupons: (0, couponViewSelector_1.getInvoicingCouponListItems)(state),
74
- customers: (0, invoicingCustomerListViewSelector_1.getInvoicingCustomerListView)(state).customers,
207
+ addons: catalogItemOptionsOfType('addon'),
208
+ charges: catalogItemOptionsOfType('charge'),
209
+ coupons: (0, couponViewSelector_1.getInvoicingCouponListItems)(state).map(toInvoicingCouponOption),
210
+ customers: (0, invoicingCustomerListViewSelector_1.getInvoicingCustomerListView)(state).customers.map(toInvoicingCustomerOption),
75
211
  localData,
76
212
  mode,
77
- plans: planPrices,
78
- products: addonAndChargePrices,
213
+ plans: planPrices.map(toInvoicingPlanOption),
79
214
  savedSubscriptionId,
80
215
  shippingAddressLabel,
216
+ hasUnsavedShippingAddress,
81
217
  subscription,
82
218
  fetchState,
83
219
  error,
@@ -1,5 +1,6 @@
1
1
  import { Amount } from '../../../commonStateTypes/amount';
2
2
  import { FetchStateAndError, FetchedState, ID } from '../../../commonStateTypes/common';
3
+ import { InvoicingFormDraftById } from '../invoicingFormDraft';
3
4
  /**
4
5
  * Draft form state for the create/edit subscription form. Held in CES so the
5
6
  * form flows its data from here (mirrors the customer form / bill-pay pattern).
@@ -41,9 +42,15 @@ export interface InvoicingSubscriptionFormLocalData {
41
42
  export interface EditInvoicingSubscriptionDetailViewState extends FetchedState {
42
43
  /** Lifecycle action fetch state per subscription id. */
43
44
  actionStateById: Record<ID, FetchStateAndError>;
44
- editDraftById: Record<ID, InvoicingSubscriptionFormLocalData>;
45
+ editDraftById: InvoicingFormDraftById<InvoicingSubscriptionFormLocalData>;
45
46
  newSubscriptionDraft: InvoicingSubscriptionFormLocalData;
46
47
  /** Detail-load state per subscription id. */
47
48
  recordStateById: Record<ID, FetchStateAndError>;
48
49
  savedSubscriptionId?: ID;
50
+ /**
51
+ * The subscription whose shipping address currently occupies the shared address
52
+ * store slot. That slot is keyed by address type alone, so without this a
53
+ * draft seeded or edited for one subscription would pass for the next one's.
54
+ */
55
+ shippingAddressOwnerId?: ID;
49
56
  }
@@ -19,8 +19,11 @@ const fetchInvoicingSubscriptionFormPageEpic = (actions$) => actions$.pipe((0, o
19
19
  const { subscriptionId } = action.payload;
20
20
  const pageActions = [
21
21
  (0, invoicingCustomerListViewReducer_1.fetchInvoicingCustomerList)({ filters: {}, keepExistingListItems: false }),
22
+ // Plans only: the form's plan picker is the one consumer, and an
23
+ // unfiltered list would replace the store's plans with every catalog
24
+ // item.
22
25
  (0, invoicingCatalogListViewReducer_1.fetchInvoicingCatalogPlanList)({
23
- filters: {},
26
+ filters: { type: 'plan' },
24
27
  keepExistingListItems: false,
25
28
  }),
26
29
  (0, couponViewReducer_1.fetchInvoicingCouponList)({ filters: {}, keepExistingListItems: false }),
@@ -1,17 +1,21 @@
1
1
  import { ActionsObservable, StateObservable } from 'redux-observable';
2
2
  import { Observable } from 'rxjs';
3
3
  import { RootState } from '../../../rootStateTypes';
4
- import { newAddressInLocalStore } from '../../addressView/addressViewReducer';
5
- import { updateInvoicingSubscriptionDetail } from './editInvoicingSubscriptionDetailViewReducer';
6
- import { initializeInvoicingSubscriptionAddress } from './editInvoicingSubscriptionDetailViewReducer';
7
- export type ActionType = ReturnType<typeof initializeInvoicingSubscriptionAddress> | ReturnType<typeof updateInvoicingSubscriptionDetail> | ReturnType<typeof newAddressInLocalStore>;
4
+ import { newAddressInLocalStore, removeNewAddressDataInLocalStore } from '../../addressView/addressViewReducer';
5
+ import { claimInvoicingSubscriptionShippingAddress, fetchInvoicingSubscriptionDetail, fetchInvoicingSubscriptionDetailPage, initializeInvoicingSubscriptionAddress, updateInvoicingSubscriptionDetail } from './editInvoicingSubscriptionDetailViewReducer';
6
+ export type ActionType = ReturnType<typeof initializeInvoicingSubscriptionAddress> | ReturnType<typeof updateInvoicingSubscriptionDetail> | ReturnType<typeof fetchInvoicingSubscriptionDetailPage> | ReturnType<typeof fetchInvoicingSubscriptionDetail> | ReturnType<typeof claimInvoicingSubscriptionShippingAddress> | ReturnType<typeof newAddressInLocalStore> | ReturnType<typeof removeNewAddressDataInLocalStore>;
8
7
  /**
9
- * Seed the shared Address screen with an existing subscription's saved shipping
10
- * address (looked up in the shared address store by `shippingAddressId`, same as
11
- * the customer billing-address flow).
12
- * Runs both when the subscription detail loads and on an explicit initialize
13
- * trigger. Skips when an in-progress draft already exists, so an address the
14
- * user just entered survives navigating between the form and the Address screen.
15
- * Mirrors `initializeInvoicingCustomerAddressEpic`.
8
+ * Seed the shared Address screen with the subscription's saved shipping address, and
9
+ * record that the slot is this subscription's. Runs when a detail request is issued
10
+ * (every path that loads a record issues one — the detail page when not cached,
11
+ * the create/edit form always), when the page opens, when the detail load
12
+ * lands, and on an explicit initialize trigger.
13
+ *
14
+ * The address store slot is keyed by address type alone, so it can still hold
15
+ * another subscription's draft. Only a draft this subscription owns is left alone —
16
+ * that is what lets an address the user just entered survive a trip to the
17
+ * Address screen. Anything else is replaced by this subscription's saved address,
18
+ * or cleared when it has none. Opening the page counts as a trigger because a
19
+ * record already loaded this session issues no detail request at all.
16
20
  */
17
21
  export declare const initializeInvoicingSubscriptionAddressEpic: (actions$: ActionsObservable<ActionType>, state$: StateObservable<RootState>) => Observable<ActionType>;