@zeniai/client-epic-state 5.1.76 → 5.1.77-betaAK1

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 (101) hide show
  1. package/lib/entity/invoicing/invoicingCommonPayload.d.ts +42 -0
  2. package/lib/entity/invoicing/invoicingCommonPayload.js +66 -1
  3. package/lib/entity/notificationRegistry/notificationRegistryPayload.d.ts +1 -0
  4. package/lib/entity/notificationRegistry/notificationRegistryPayload.js +3 -0
  5. package/lib/entity/notificationRegistry/notificationRegistryState.d.ts +1 -0
  6. package/lib/entity/tenant/clearAllEpic.d.ts +2 -1
  7. package/lib/entity/tenant/clearAllEpic.js +2 -0
  8. package/lib/epic.d.ts +2 -1
  9. package/lib/epic.js +7 -1
  10. package/lib/esm/entity/invoicing/invoicingCommonPayload.js +61 -0
  11. package/lib/esm/entity/notificationRegistry/notificationRegistryPayload.js +3 -0
  12. package/lib/esm/entity/tenant/clearAllEpic.js +2 -0
  13. package/lib/esm/epic.js +7 -1
  14. package/lib/esm/index.js +5 -1
  15. package/lib/esm/reducer.js +3 -0
  16. package/lib/esm/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +4 -18
  17. package/lib/esm/view/invoicing/createCreditNote/createCreditNoteFormConfig.js +8 -1
  18. package/lib/esm/view/invoicing/createCreditNote/createCreditNotePayload.js +1 -8
  19. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/catalogDetailEditPayload.js +5 -5
  20. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +2 -3
  21. package/lib/esm/view/invoicing/editInvoicingCatalogItemDetailView/invoicingCatalogItemFormConfig.js +5 -4
  22. package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewPayload.js +2 -2
  23. package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewReducer.js +1 -1
  24. package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewSelector.js +1 -1
  25. package/lib/esm/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewState.js +45 -1
  26. package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewPayload.js +3 -4
  27. package/lib/esm/view/invoicing/editInvoicingSubscriptionDetailView/invoicingSubscriptionFormConfig.js +4 -2
  28. package/lib/esm/view/invoicing/invoicingConfigView/invoicingConfigViewReducer.js +7 -1
  29. package/lib/esm/view/invoicing/invoicingConfigView/invoicingConfigViewSelector.js +3 -0
  30. package/lib/esm/view/invoicing/invoicingQboView/epics/fetchInvoicingQboAccountsEpic.js +25 -0
  31. package/lib/esm/view/invoicing/invoicingQboView/epics/fetchInvoicingQboClassesEpic.js +15 -0
  32. package/lib/esm/view/invoicing/invoicingQboView/epics/fetchInvoicingQboConnectionEpic.js +15 -0
  33. package/lib/esm/view/invoicing/invoicingQboView/epics/fetchInvoicingQboSyncHealthEpic.js +15 -0
  34. package/lib/esm/view/invoicing/invoicingQboView/epics/invoicingQboViewActionType.js +1 -0
  35. package/lib/esm/view/invoicing/invoicingQboView/epics/retryInvoicingQboSyncTaskEpic.js +26 -0
  36. package/lib/esm/view/invoicing/invoicingQboView/epics/startInvoicingQboBackfillEpic.js +18 -0
  37. package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewPayload.js +47 -0
  38. package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewReducer.js +128 -0
  39. package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +20 -0
  40. package/lib/esm/view/invoicing/invoicingQboView/invoicingQboViewState.js +1 -0
  41. package/lib/esm/view/invoicing/issueCreditNote/issueCreditNotePayload.js +4 -11
  42. package/lib/esm/view/invoicing/recordPayment/recordPaymentFormConfig.js +2 -1
  43. package/lib/esm/view/invoicing/recordPayment/recordPaymentPayload.js +5 -14
  44. package/lib/index.d.ts +7 -1
  45. package/lib/index.js +20 -4
  46. package/lib/reducer.d.ts +3 -0
  47. package/lib/reducer.js +3 -0
  48. package/lib/view/expenseAutomationView/selectors/transactionCategorizationSelector.js +3 -17
  49. package/lib/view/invoicing/createCreditNote/createCreditNoteFormConfig.js +8 -1
  50. package/lib/view/invoicing/createCreditNote/createCreditNotePayload.js +1 -8
  51. package/lib/view/invoicing/createCreditNote/createCreditNoteState.d.ts +2 -1
  52. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/catalogDetailEditPayload.d.ts +2 -1
  53. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/catalogDetailEditPayload.js +5 -5
  54. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewPayload.js +2 -3
  55. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/editInvoicingCatalogItemDetailViewState.d.ts +3 -2
  56. package/lib/view/invoicing/editInvoicingCatalogItemDetailView/invoicingCatalogItemFormConfig.js +5 -4
  57. package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewPayload.js +2 -2
  58. package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewReducer.js +3 -3
  59. package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewSelector.js +2 -2
  60. package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewState.d.ts +8 -2
  61. package/lib/view/invoicing/editInvoicingCouponDetailView/editInvoicingCouponDetailViewState.js +47 -0
  62. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewPayload.js +3 -4
  63. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/editInvoicingSubscriptionDetailViewState.d.ts +2 -1
  64. package/lib/view/invoicing/editInvoicingSubscriptionDetailView/invoicingSubscriptionFormConfig.js +4 -2
  65. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewPayload.d.ts +4 -1
  66. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewReducer.d.ts +1 -1
  67. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewReducer.js +6 -0
  68. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewSelector.d.ts +4 -1
  69. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewSelector.js +3 -0
  70. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewState.d.ts +4 -1
  71. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboAccountsEpic.d.ts +6 -0
  72. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboAccountsEpic.js +29 -0
  73. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboClassesEpic.d.ts +6 -0
  74. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboClassesEpic.js +19 -0
  75. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboConnectionEpic.d.ts +6 -0
  76. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboConnectionEpic.js +19 -0
  77. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboSyncHealthEpic.d.ts +6 -0
  78. package/lib/view/invoicing/invoicingQboView/epics/fetchInvoicingQboSyncHealthEpic.js +19 -0
  79. package/lib/view/invoicing/invoicingQboView/epics/invoicingQboViewActionType.d.ts +2 -0
  80. package/lib/view/invoicing/invoicingQboView/epics/invoicingQboViewActionType.js +2 -0
  81. package/lib/view/invoicing/invoicingQboView/epics/retryInvoicingQboSyncTaskEpic.d.ts +6 -0
  82. package/lib/view/invoicing/invoicingQboView/epics/retryInvoicingQboSyncTaskEpic.js +30 -0
  83. package/lib/view/invoicing/invoicingQboView/epics/startInvoicingQboBackfillEpic.d.ts +6 -0
  84. package/lib/view/invoicing/invoicingQboView/epics/startInvoicingQboBackfillEpic.js +22 -0
  85. package/lib/view/invoicing/invoicingQboView/invoicingQboViewPayload.d.ts +79 -0
  86. package/lib/view/invoicing/invoicingQboView/invoicingQboViewPayload.js +52 -0
  87. package/lib/view/invoicing/invoicingQboView/invoicingQboViewReducer.d.ts +25 -0
  88. package/lib/view/invoicing/invoicingQboView/invoicingQboViewReducer.js +132 -0
  89. package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.d.ts +21 -0
  90. package/lib/view/invoicing/invoicingQboView/invoicingQboViewSelector.js +24 -0
  91. package/lib/view/invoicing/invoicingQboView/invoicingQboViewState.d.ts +14 -0
  92. package/lib/view/invoicing/invoicingQboView/invoicingQboViewState.js +2 -0
  93. package/lib/view/invoicing/issueCreditNote/issueCreditNotePayload.js +4 -11
  94. package/lib/view/invoicing/issueCreditNote/issueCreditNoteState.d.ts +3 -2
  95. package/lib/view/invoicing/recordPayment/recordPaymentFormConfig.js +2 -1
  96. package/lib/view/invoicing/recordPayment/recordPaymentPayload.js +5 -14
  97. package/lib/view/invoicing/recordPayment/recordPaymentState.d.ts +3 -2
  98. package/package.json +1 -1
  99. package/lib/esm/view/invoicing/editInvoicingCouponDetailView/invoicingCouponFormConfig.js +0 -44
  100. package/lib/view/invoicing/editInvoicingCouponDetailView/invoicingCouponFormConfig.d.ts +0 -5
  101. package/lib/view/invoicing/editInvoicingCouponDetailView/invoicingCouponFormConfig.js +0 -48
@@ -1,3 +1,4 @@
1
+ import { toAmount } from '../../../commonStateTypes/amount';
1
2
  import { invoicingProductToCatalogItemFormSubFamily, isTieredCatalogPricingModel, } from './invoicingCatalogItemFormConfig';
2
3
  const trimOrUndefined = (value) => value.trim() !== '' ? value.trim() : undefined;
3
4
  const trimOrNull = (value) => value.trim() !== '' ? value.trim() : null;
@@ -31,7 +32,7 @@ const tierToPayload = (tier) => ({
31
32
  package_size: hasValue(tier.packageSize)
32
33
  ? Number(tier.packageSize)
33
34
  : undefined,
34
- price: toAmountValue(tier.price),
35
+ price: tier.price.amount,
35
36
  pricing_type: tier.pricingType,
36
37
  starting_unit: Number(tier.startingUnit) || 1,
37
38
  });
@@ -45,7 +46,7 @@ const subFamilyToProductUpdate = (subFamily, itemType) => {
45
46
  is_taxable: subFamily.isTaxable,
46
47
  period: hasValue(subFamily.period) ? Number(subFamily.period) : null,
47
48
  period_unit: hasValue(subFamily.period) ? subFamily.periodUnit : null,
48
- price: tiered ? 0 : toAmountValue(subFamily.priceDollars),
49
+ price: tiered ? 0 : subFamily.priceDollars.amount,
49
50
  pricing_model: subFamily.pricingModel,
50
51
  show_description_in_quotes: subFamily.showDescriptionInQuotes,
51
52
  tiers: tiered ? subFamily.tiers.map(tierToPayload) : [],
@@ -112,7 +113,6 @@ export const catalogDetailEditValuesToSavePayload = (values, itemType = 'plan',
112
113
  productUpdates,
113
114
  };
114
115
  };
115
- const toAmountValue = (value) => Number(value) || 0;
116
116
  /** Build POST /catalog/products body for Create sub-family. */
117
117
  export const createSubFamilyFormToPayload = (itemId, itemType, values) => {
118
118
  const name = values.name.trim();
@@ -123,7 +123,7 @@ export const createSubFamilyFormToPayload = (itemId, itemType, values) => {
123
123
  is_taxable: true,
124
124
  item_id: itemId,
125
125
  name,
126
- price: toAmountValue(values.priceDollars),
126
+ price: values.priceDollars.amount,
127
127
  price_code: priceCode,
128
128
  pricing_model: values.pricingModel || 'flat_fee',
129
129
  show_description_in_invoices: true,
@@ -162,7 +162,7 @@ export const productToCreateSubFamilyFormValues = (product) => ({
162
162
  period: product?.period != null ? String(product.period) : '1',
163
163
  periodUnit: product?.periodUnit ?? 'month',
164
164
  priceCode: '',
165
- priceDollars: product?.price != null ? String(product.price.amount) : '',
165
+ priceDollars: product?.price ?? toAmount(0, 'USD', '$'),
166
166
  pricingModel: product?.pricingModel ?? 'flat_fee',
167
167
  prorationType: product?.prorationType ?? 'site_default',
168
168
  trialEndAction: product?.trialEndAction ?? 'site_default',
@@ -7,7 +7,6 @@ const toProductCode = (value) => value
7
7
  .toLowerCase()
8
8
  .replace(/[^a-z0-9]+/g, '-')
9
9
  .replace(/^-|-$/g, '');
10
- const toAmountValue = (value) => Number(value) || 0;
11
10
  const buildAccountingDetail = (localData) => {
12
11
  const detail = {};
13
12
  if (localData.sku.trim() !== '') {
@@ -31,7 +30,7 @@ const buildSubFamilyPayload = (localData, subFamily, clearEmptyValues) => {
31
30
  free_quantity: Number(subFamily.freeQuantity) || 0,
32
31
  is_taxable: subFamily.isTaxable,
33
32
  name,
34
- price: tiered ? 0 : toAmountValue(subFamily.priceDollars),
33
+ price: tiered ? 0 : subFamily.priceDollars.amount,
35
34
  price_code: priceCode,
36
35
  pricing_model: subFamily.pricingModel,
37
36
  show_description_in_invoices: subFamily.showDescriptionInInvoices,
@@ -70,7 +69,7 @@ const buildSubFamilyPayload = (localData, subFamily, clearEmptyValues) => {
70
69
  payload.tiers = subFamily.tiers.map((tier) => ({
71
70
  ending_unit: tier.endingUnit.trim() !== '' ? Number(tier.endingUnit) : undefined,
72
71
  package_size: tier.packageSize.trim() !== '' ? Number(tier.packageSize) : undefined,
73
- price: toAmountValue(tier.price),
72
+ price: tier.price.amount,
74
73
  pricing_type: tier.pricingType,
75
74
  starting_unit: Number(tier.startingUnit) || 1,
76
75
  }));
@@ -1,3 +1,4 @@
1
+ import { toAmount } from '../../../commonStateTypes/amount';
1
2
  /**
2
3
  * Canonical value sets for the catalog item form. CES owns the values;
3
4
  * web-components maps each value to a localized label.
@@ -44,7 +45,7 @@ export const isTieredCatalogPricingModel = (model) => ['tiered', 'volume', 'stai
44
45
  export const blankInvoicingCatalogTier = (startingUnit = '1') => ({
45
46
  endingUnit: '',
46
47
  packageSize: '',
47
- price: '',
48
+ price: toAmount(0, 'USD', '$'),
48
49
  pricingType: 'per_unit',
49
50
  startingUnit,
50
51
  });
@@ -58,7 +59,7 @@ export const blankInvoicingCatalogSubFamily = () => ({
58
59
  name: '',
59
60
  period: '1',
60
61
  periodUnit: 'month',
61
- priceDollars: '',
62
+ priceDollars: toAmount(0, 'USD', '$'),
62
63
  priceCode: '',
63
64
  pricingModel: 'flat_fee',
64
65
  prorationType: 'site_default',
@@ -85,7 +86,7 @@ export const invoicingProductToCatalogItemFormSubFamily = (product) => {
85
86
  name: product.name ?? '',
86
87
  period: product.period != null ? String(product.period) : '1',
87
88
  periodUnit: product.periodUnit ?? 'month',
88
- priceDollars: product.price != null ? String(product.price.amount) : '',
89
+ priceDollars: product.price ?? toAmount(0, 'USD', '$'),
89
90
  priceCode: product.priceCode ?? '',
90
91
  pricingModel: product.pricingModel ?? 'flat_fee',
91
92
  prorationType: product.prorationType ?? 'site_default',
@@ -95,7 +96,7 @@ export const invoicingProductToCatalogItemFormSubFamily = (product) => {
95
96
  ? product.tiers.map((tier) => ({
96
97
  endingUnit: tier.endingUnit != null ? String(tier.endingUnit) : '',
97
98
  packageSize: tier.packageSize != null ? String(tier.packageSize) : '',
98
- price: String(tier.price.amount),
99
+ price: tier.price,
99
100
  pricingType: tier.pricingType ?? 'per_unit',
100
101
  startingUnit: String(tier.startingUnit ?? 1),
101
102
  }))
@@ -22,8 +22,8 @@ export const localDataToSaveInvoicingCouponPayload = (localData, isCreate) => {
22
22
  data.discount_percentage = Number(localData.discountPercentage);
23
23
  }
24
24
  if (localData.discountType === 'fixed_amount') {
25
- if (localData.discountAmount.trim() !== '') {
26
- data.discount_amount = Number(localData.discountAmount);
25
+ if (localData.discountAmount.amount !== 0) {
26
+ data.discount_amount = localData.discountAmount.amount;
27
27
  }
28
28
  data.currency_code = 'USD';
29
29
  }
@@ -1,5 +1,5 @@
1
1
  import { createSlice } from '@reduxjs/toolkit';
2
- import { initialInvoicingCouponFormLocalData } from './invoicingCouponFormConfig';
2
+ import { initialInvoicingCouponFormLocalData, } from './editInvoicingCouponDetailViewState';
3
3
  export const initialState = {
4
4
  fetchState: 'Not-Started',
5
5
  error: undefined,
@@ -1,6 +1,6 @@
1
1
  import { INVOICING_COUPON_APPLY_ON_OPTIONS, INVOICING_COUPON_DURATION_TYPE_OPTIONS, INVOICING_COUPON_PERIOD_UNIT_OPTIONS, INVOICING_DISCOUNT_TYPE_OPTIONS, } from '../../../entity/invoicing/coupon/couponConstants';
2
2
  import { getInvoicingCouponById } from '../../../entity/invoicing/coupon/couponSelector';
3
- import { entityToInvoicingCouponFormLocalData } from './invoicingCouponFormConfig';
3
+ import { entityToInvoicingCouponFormLocalData, } from './editInvoicingCouponDetailViewState';
4
4
  /**
5
5
  * Bundled view for the discount create/edit form: the save fetch state/error,
6
6
  * the id of the coupon last saved, and — when a `couponId` is passed (edit
@@ -1 +1,45 @@
1
- export {};
1
+ import { toAmount } from '../../../commonStateTypes/amount';
2
+ export const initialInvoicingCouponFormLocalData = {
3
+ applyOn: 'invoice_amount',
4
+ couponCode: '',
5
+ couponId: '',
6
+ discountAmount: toAmount(0, 'USD', '$'),
7
+ discountPercentage: '',
8
+ discountQuantity: '',
9
+ discountType: 'percentage',
10
+ durationType: 'one_time',
11
+ invoiceName: '',
12
+ invoiceNotes: '',
13
+ maxRedemptions: '',
14
+ name: '',
15
+ period: '',
16
+ periodUnit: 'month',
17
+ validFrom: '',
18
+ validTill: '',
19
+ };
20
+ /** Seed the form draft from an existing coupon entity (edit mode). */
21
+ export const entityToInvoicingCouponFormLocalData = (coupon) => {
22
+ if (coupon == null) {
23
+ return { ...initialInvoicingCouponFormLocalData };
24
+ }
25
+ return {
26
+ applyOn: coupon.applyOn ?? 'invoice_amount',
27
+ couponCode: coupon.couponCode ?? '',
28
+ couponId: coupon.id ?? '',
29
+ discountAmount: coupon.discountAmount ?? toAmount(0, 'USD', '$'),
30
+ discountPercentage: coupon.discountPercentage != null
31
+ ? String(coupon.discountPercentage)
32
+ : '',
33
+ discountQuantity: coupon.discountQuantity != null ? String(coupon.discountQuantity) : '',
34
+ discountType: coupon.discountType ?? 'percentage',
35
+ durationType: coupon.durationType ?? 'one_time',
36
+ invoiceName: coupon.invoiceName ?? '',
37
+ invoiceNotes: coupon.invoiceNotes ?? '',
38
+ maxRedemptions: coupon.maxRedemptions != null ? String(coupon.maxRedemptions) : '',
39
+ name: coupon.name ?? '',
40
+ period: coupon.period != null ? String(coupon.period) : '',
41
+ periodUnit: coupon.periodUnit ?? 'month',
42
+ validFrom: coupon.validFrom != null ? coupon.validFrom.format('YYYY-MM-DD') : '',
43
+ validTill: coupon.validTill != null ? coupon.validTill.format('YYYY-MM-DD') : '',
44
+ };
45
+ };
@@ -24,8 +24,7 @@ export const localDataToSaveInvoicingSubscriptionPayload = (localData, shippingA
24
24
  quantity: 1,
25
25
  })),
26
26
  ];
27
- const setupFee = localData.setupFee.trim();
28
- const parsedSetupFee = Number.parseFloat(setupFee);
27
+ const setupFee = localData.setupFee.amount;
29
28
  const poNumber = localData.poNumber.trim();
30
29
  const trialEnd = localData.trialEnd.trim();
31
30
  return {
@@ -35,8 +34,8 @@ export const localDataToSaveInvoicingSubscriptionPayload = (localData, shippingA
35
34
  currency_code: localData.preferredCurrencyCode,
36
35
  customer_id: localData.customerId,
37
36
  subscription_items: items,
38
- ...(setupFee !== '' && !Number.isNaN(parsedSetupFee)
39
- ? { setup_fee: parsedSetupFee }
37
+ ...(Number.isFinite(setupFee) && setupFee !== 0
38
+ ? { setup_fee: setupFee }
40
39
  : {}),
41
40
  ...(poNumber !== '' ? { po_number: poNumber } : {}),
42
41
  ...(trialEnd !== '' ? { trial_end: trialEnd } : {}),
@@ -1,3 +1,4 @@
1
+ import { toAmount } from '../../../commonStateTypes/amount';
1
2
  /** `AddressType` key used when the subscription form opens the shared Address screen. */
2
3
  export const INVOICING_SUBSCRIPTION_SHIPPING_ADDRESS_TYPE = 'invoicing_subscription_shipping_address';
3
4
  /**
@@ -44,7 +45,7 @@ export const initialInvoicingSubscriptionFormLocalData = {
44
45
  planQuantity: 1,
45
46
  poNumber: '',
46
47
  preferredCurrencyCode: 'USD',
47
- setupFee: '',
48
+ setupFee: toAmount(0, 'USD', '$'),
48
49
  trialEnd: '',
49
50
  };
50
51
  /** Seed the form draft from an existing subscription entity (edit mode). */
@@ -66,7 +67,8 @@ export const entityToInvoicingSubscriptionFormLocalData = (subscription) => {
66
67
  planQuantity: planItem?.quantity ?? 1,
67
68
  poNumber: subscription.poNumber ?? '',
68
69
  preferredCurrencyCode: subscription.currencyCode ?? 'USD',
69
- setupFee: '',
70
+ setupFee: subscription.setupFee ??
71
+ toAmount(0, subscription.currencyCode ?? 'USD', '$'),
70
72
  trialEnd: subscription.trialEnd != null
71
73
  ? subscription.trialEnd.format('YYYY-MM-DD')
72
74
  : '',
@@ -1,5 +1,5 @@
1
1
  import { createSlice } from '@reduxjs/toolkit';
2
- import { toAuditLogEntityRouteOption, toInvoicingCatalogItemLifecycleActionOption, toInvoicingCatalogProductLifecycleActionOption, toInvoicingDiscountLifecycleActionOption, toInvoicingDunningAction, toInvoicingNamedOption, toInvoicingSubscriptionEditFieldOption, toInvoicingSubscriptionLifecycleActionOption, } from '../../../entity/invoicing/invoicingCommonPayload';
2
+ import { toAuditLogEntityRouteOption, toInvoicingCatalogItemLifecycleActionOption, toInvoicingCatalogProductLifecycleActionOption, toInvoicingDiscountLifecycleActionOption, toInvoicingDunningAction, toInvoicingNamedOption, toInvoicingQboAccountMappingKindOption, toInvoicingSubscriptionEditFieldOption, toInvoicingSubscriptionLifecycleActionOption, } from '../../../entity/invoicing/invoicingCommonPayload';
3
3
  export const initialState = {
4
4
  fetchState: 'Not-Started',
5
5
  error: undefined,
@@ -18,6 +18,9 @@ export const initialState = {
18
18
  catalogItemLifecycleActionOptions: [],
19
19
  catalogProductLifecycleActionOptions: [],
20
20
  discountLifecycleActionOptions: [],
21
+ qboAccountMappingKindOptions: [],
22
+ qboCustomerMatchingOptions: [],
23
+ qboSyncStatusOptions: [],
21
24
  isInvoicingEnabled: undefined,
22
25
  isInvoicingTosAccepted: undefined,
23
26
  invoicingTosAcceptanceIp: undefined,
@@ -53,6 +56,9 @@ export function mapInvoicingConfigPayloadToState(payload) {
53
56
  catalogItemLifecycleActionOptions: (payload.catalog_item_lifecycle_action_options ?? []).map((option) => toInvoicingCatalogItemLifecycleActionOption(option)),
54
57
  catalogProductLifecycleActionOptions: (payload.catalog_product_lifecycle_action_options ?? []).map((option) => toInvoicingCatalogProductLifecycleActionOption(option)),
55
58
  discountLifecycleActionOptions: (payload.discount_lifecycle_action_options ?? []).map((option) => toInvoicingDiscountLifecycleActionOption(option)),
59
+ qboAccountMappingKindOptions: (payload.qbo_account_mapping_kind_options ?? []).map(toInvoicingQboAccountMappingKindOption),
60
+ qboCustomerMatchingOptions: (payload.qbo_customer_matching_options ?? []).map(toInvoicingNamedOption),
61
+ qboSyncStatusOptions: (payload.qbo_sync_status_options ?? []).map(toInvoicingNamedOption),
56
62
  isInvoicingEnabled: payload.is_invoicing_enabled,
57
63
  isInvoicingTosAccepted: payload.is_invoicing_tos_accepted,
58
64
  invoicingTosAcceptanceIp: payload.invoicing_tos_acceptance_ip ?? undefined,
@@ -23,6 +23,9 @@ export const getInvoicingConfigView = (state) => {
23
23
  catalogItemLifecycleActionOptions: configState.catalogItemLifecycleActionOptions,
24
24
  catalogProductLifecycleActionOptions: configState.catalogProductLifecycleActionOptions,
25
25
  discountLifecycleActionOptions: configState.discountLifecycleActionOptions,
26
+ qboAccountMappingKindOptions: configState.qboAccountMappingKindOptions,
27
+ qboCustomerMatchingOptions: configState.qboCustomerMatchingOptions,
28
+ qboSyncStatusOptions: configState.qboSyncStatusOptions,
26
29
  error: configState.error,
27
30
  fetchState: configState.fetchState,
28
31
  isInvoicingEnabled: configState.isInvoicingEnabled,
@@ -0,0 +1,25 @@
1
+ import { of } from 'rxjs';
2
+ import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
+ import { mapInvoicingQboAccountOptions, } from '../invoicingQboViewPayload';
5
+ import { fetchInvoicingQboAccounts, updateInvoicingQboAccounts, updateInvoicingQboAccountsFailure, } from '../invoicingQboViewReducer';
6
+ export const fetchInvoicingQboAccountsEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingQboAccounts.match), mergeMap((action) => {
7
+ const { accountTypes } = action.payload;
8
+ const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/qbo/accounts?account_types=${encodeURIComponent(accountTypes)}`;
9
+ return zeniAPI.getJSON(apiUrl).pipe(switchMap((response) => {
10
+ if (isSuccessResponse(response)) {
11
+ return of(updateInvoicingQboAccounts({
12
+ accountTypes,
13
+ accounts: mapInvoicingQboAccountOptions(response.data?.accounts ?? []),
14
+ }));
15
+ }
16
+ return of(updateInvoicingQboAccountsFailure({
17
+ accountTypes,
18
+ status: response.status,
19
+ }));
20
+ }), catchError((error) => of(updateInvoicingQboAccountsFailure({
21
+ accountTypes,
22
+ status: createZeniAPIStatus('Unexpected Error', 'Fetch QBO accounts REST API call errored out ' +
23
+ JSON.stringify(error)),
24
+ }))));
25
+ }));
@@ -0,0 +1,15 @@
1
+ import { of } from 'rxjs';
2
+ import { catchError, filter, switchMap } from 'rxjs/operators';
3
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
+ import { mapInvoicingQboAccountOptions, } from '../invoicingQboViewPayload';
5
+ import { fetchInvoicingQboClasses, updateInvoicingQboClasses, updateInvoicingQboClassesFailure, } from '../invoicingQboViewReducer';
6
+ export const fetchInvoicingQboClassesEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingQboClasses.match), switchMap(() => {
7
+ const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/qbo/classes`;
8
+ return zeniAPI.getJSON(apiUrl).pipe(switchMap((response) => {
9
+ if (isSuccessResponse(response)) {
10
+ return of(updateInvoicingQboClasses(mapInvoicingQboAccountOptions(response.data?.accounts ?? [])));
11
+ }
12
+ return of(updateInvoicingQboClassesFailure(response.status));
13
+ }), catchError((error) => of(updateInvoicingQboClassesFailure(createZeniAPIStatus('Unexpected Error', 'Fetch QBO classes REST API call errored out ' +
14
+ JSON.stringify(error))))));
15
+ }));
@@ -0,0 +1,15 @@
1
+ import { of } from 'rxjs';
2
+ import { catchError, filter, switchMap } from 'rxjs/operators';
3
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
+ import { mapInvoicingQboConnection, } from '../invoicingQboViewPayload';
5
+ import { fetchInvoicingQboConnection, updateInvoicingQboConnection, updateInvoicingQboConnectionFailure, } from '../invoicingQboViewReducer';
6
+ export const fetchInvoicingQboConnectionEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingQboConnection.match), switchMap(() => {
7
+ const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/qbo/connection`;
8
+ return zeniAPI.getJSON(apiUrl).pipe(switchMap((response) => {
9
+ if (isSuccessResponse(response)) {
10
+ return of(updateInvoicingQboConnection(mapInvoicingQboConnection(response.data ?? { connected: false })));
11
+ }
12
+ return of(updateInvoicingQboConnectionFailure(response.status));
13
+ }), catchError((error) => of(updateInvoicingQboConnectionFailure(createZeniAPIStatus('Unexpected Error', 'Fetch QBO connection REST API call errored out ' +
14
+ JSON.stringify(error))))));
15
+ }));
@@ -0,0 +1,15 @@
1
+ import { of } from 'rxjs';
2
+ import { catchError, filter, switchMap } from 'rxjs/operators';
3
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
+ import { mapInvoicingQboSyncHealth, } from '../invoicingQboViewPayload';
5
+ import { fetchInvoicingQboSyncHealth, updateInvoicingQboSyncHealth, updateInvoicingQboSyncHealthFailure, } from '../invoicingQboViewReducer';
6
+ export const fetchInvoicingQboSyncHealthEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(fetchInvoicingQboSyncHealth.match), switchMap(() => {
7
+ const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/qbo/sync-health`;
8
+ return zeniAPI.getJSON(apiUrl).pipe(switchMap((response) => {
9
+ if (isSuccessResponse(response)) {
10
+ return of(updateInvoicingQboSyncHealth(mapInvoicingQboSyncHealth(response.data ?? {})));
11
+ }
12
+ return of(updateInvoicingQboSyncHealthFailure(response.status));
13
+ }), catchError((error) => of(updateInvoicingQboSyncHealthFailure(createZeniAPIStatus('Unexpected Error', 'Fetch QBO sync health REST API call errored out ' +
14
+ JSON.stringify(error))))));
15
+ }));
@@ -0,0 +1,26 @@
1
+ import { of } from 'rxjs';
2
+ import { catchError, filter, mergeMap, switchMap } from 'rxjs/operators';
3
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
+ import { fetchInvoicingQboSyncHealth, retryInvoicingQboSyncTask, retryInvoicingQboSyncTaskFailure, retryInvoicingQboSyncTaskSuccess, } from '../invoicingQboViewReducer';
5
+ export const retryInvoicingQboSyncTaskEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(retryInvoicingQboSyncTask.match), mergeMap((action) => {
6
+ const { taskId } = action.payload;
7
+ const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/qbo/sync-tasks/${encodeURIComponent(taskId)}/retry`;
8
+ return zeniAPI
9
+ .postAndGetJSON(apiUrl, {})
10
+ .pipe(switchMap((response) => {
11
+ if (isSuccessResponse(response)) {
12
+ return of(retryInvoicingQboSyncTaskSuccess({
13
+ status: response.data?.status ?? '',
14
+ taskId: response.data?.task_id ?? taskId,
15
+ }), fetchInvoicingQboSyncHealth());
16
+ }
17
+ return of(retryInvoicingQboSyncTaskFailure({
18
+ status: response.status,
19
+ taskId,
20
+ }));
21
+ }), catchError((error) => of(retryInvoicingQboSyncTaskFailure({
22
+ status: createZeniAPIStatus('Unexpected Error', 'Retry QBO sync task REST API call errored out ' +
23
+ JSON.stringify(error)),
24
+ taskId,
25
+ }))));
26
+ }));
@@ -0,0 +1,18 @@
1
+ import { of } from 'rxjs';
2
+ import { catchError, filter, switchMap } from 'rxjs/operators';
3
+ import { createZeniAPIStatus, isSuccessResponse, } from '../../../../responsePayload';
4
+ import { fetchInvoicingQboSyncHealth, startInvoicingQboBackfill, startInvoicingQboBackfillFailure, startInvoicingQboBackfillSuccess, } from '../invoicingQboViewReducer';
5
+ export const startInvoicingQboBackfillEpic = (actions$, _state$, zeniAPI) => actions$.pipe(filter(startInvoicingQboBackfill.match), switchMap((action) => {
6
+ const apiUrl = `${zeniAPI.apiEndPoints.invoicingMicroServiceBaseUrl}/1.0/qbo/backfill`;
7
+ return zeniAPI
8
+ .postAndGetJSON(apiUrl, {
9
+ start_date: action.payload.startDate,
10
+ })
11
+ .pipe(switchMap((response) => {
12
+ if (isSuccessResponse(response)) {
13
+ return of(startInvoicingQboBackfillSuccess(), fetchInvoicingQboSyncHealth());
14
+ }
15
+ return of(startInvoicingQboBackfillFailure(response.status));
16
+ }), catchError((error) => of(startInvoicingQboBackfillFailure(createZeniAPIStatus('Unexpected Error', 'Start QBO backfill REST API call errored out ' +
17
+ JSON.stringify(error))))));
18
+ }));
@@ -0,0 +1,47 @@
1
+ export function mapInvoicingQboConnection(payload) {
2
+ return {
3
+ connected: payload.connected,
4
+ companyName: payload.company_name ?? undefined,
5
+ connectionId: payload.connection_id ?? undefined,
6
+ realmId: payload.realm_id ?? undefined,
7
+ };
8
+ }
9
+ export function mapInvoicingQboAccountOptions(rows) {
10
+ return rows.flatMap((row) => {
11
+ // Only qbo_id is a valid QBO AccountRef/ClassRef value — platform-internal
12
+ // ids (account_id / id) are display metadata and must never be submitted.
13
+ const accountId = row.qbo_id ?? row.id ?? row.account_id ?? '';
14
+ if (accountId === '') {
15
+ return [];
16
+ }
17
+ const accountName = row.name ??
18
+ row.account_name ??
19
+ row.accounting_class_name ??
20
+ row.qbo_accounting_class_name ??
21
+ row.fully_qualified_name ??
22
+ accountId;
23
+ return [
24
+ {
25
+ id: accountId,
26
+ name: accountName,
27
+ accountType: row.account_type ?? undefined,
28
+ },
29
+ ];
30
+ });
31
+ }
32
+ export function mapInvoicingQboSyncHealth(payload) {
33
+ return {
34
+ connection: payload.connection != null
35
+ ? mapInvoicingQboConnection(payload.connection)
36
+ : undefined,
37
+ countsByStatus: payload.counts_by_status ?? {},
38
+ failedTasks: (payload.failed_tasks ?? []).map((task) => ({
39
+ attempts: task.attempts,
40
+ entityId: task.entity_id,
41
+ entityType: task.entity_type,
42
+ taskId: task.task_id,
43
+ lastError: task.last_error ?? undefined,
44
+ updatedAt: task.updated_at ?? undefined,
45
+ })),
46
+ };
47
+ }
@@ -0,0 +1,128 @@
1
+ import { createSlice } from '@reduxjs/toolkit';
2
+ export const initialState = {
3
+ accountsByAccountTypes: {},
4
+ accountsFetchStateByAccountTypes: {},
5
+ backfillFetchState: { fetchState: 'Not-Started', error: undefined },
6
+ classes: [],
7
+ classesFetchState: { fetchState: 'Not-Started', error: undefined },
8
+ connection: undefined,
9
+ connectionFetchState: { fetchState: 'Not-Started', error: undefined },
10
+ retryFetchStateByTaskId: {},
11
+ syncHealth: undefined,
12
+ syncHealthFetchState: { fetchState: 'Not-Started', error: undefined },
13
+ };
14
+ const invoicingQboView = createSlice({
15
+ name: 'invoicingQboView',
16
+ initialState,
17
+ reducers: {
18
+ fetchInvoicingQboConnection(draft) {
19
+ draft.connectionFetchState = {
20
+ fetchState: 'In-Progress',
21
+ error: undefined,
22
+ };
23
+ },
24
+ updateInvoicingQboConnection(draft, action) {
25
+ draft.connection = action.payload;
26
+ draft.connectionFetchState = { fetchState: 'Completed', error: undefined };
27
+ },
28
+ updateInvoicingQboConnectionFailure(draft, action) {
29
+ draft.connectionFetchState = { fetchState: 'Error', error: action.payload };
30
+ },
31
+ fetchInvoicingQboAccounts: {
32
+ reducer(draft, action) {
33
+ draft.accountsFetchStateByAccountTypes[action.payload.accountTypes] = {
34
+ fetchState: 'In-Progress',
35
+ error: undefined,
36
+ };
37
+ },
38
+ prepare(accountTypes) {
39
+ return { payload: { accountTypes } };
40
+ },
41
+ },
42
+ updateInvoicingQboAccounts(draft, action) {
43
+ draft.accountsByAccountTypes[action.payload.accountTypes] =
44
+ action.payload.accounts;
45
+ draft.accountsFetchStateByAccountTypes[action.payload.accountTypes] = {
46
+ fetchState: 'Completed',
47
+ error: undefined,
48
+ };
49
+ },
50
+ updateInvoicingQboAccountsFailure(draft, action) {
51
+ draft.accountsFetchStateByAccountTypes[action.payload.accountTypes] = {
52
+ fetchState: 'Error',
53
+ error: action.payload.status,
54
+ };
55
+ },
56
+ fetchInvoicingQboClasses(draft) {
57
+ draft.classesFetchState = { fetchState: 'In-Progress', error: undefined };
58
+ },
59
+ updateInvoicingQboClasses(draft, action) {
60
+ draft.classes = action.payload;
61
+ draft.classesFetchState = { fetchState: 'Completed', error: undefined };
62
+ },
63
+ updateInvoicingQboClassesFailure(draft, action) {
64
+ draft.classesFetchState = { fetchState: 'Error', error: action.payload };
65
+ },
66
+ fetchInvoicingQboSyncHealth(draft) {
67
+ draft.syncHealthFetchState = {
68
+ fetchState: 'In-Progress',
69
+ error: undefined,
70
+ };
71
+ },
72
+ updateInvoicingQboSyncHealth(draft, action) {
73
+ draft.syncHealth = action.payload;
74
+ draft.syncHealthFetchState = { fetchState: 'Completed', error: undefined };
75
+ },
76
+ updateInvoicingQboSyncHealthFailure(draft, action) {
77
+ draft.syncHealthFetchState = {
78
+ fetchState: 'Error',
79
+ error: action.payload,
80
+ };
81
+ },
82
+ retryInvoicingQboSyncTask: {
83
+ reducer(draft, action) {
84
+ draft.retryFetchStateByTaskId[action.payload.taskId] = {
85
+ fetchState: 'In-Progress',
86
+ error: undefined,
87
+ };
88
+ },
89
+ prepare(taskId) {
90
+ return { payload: { taskId } };
91
+ },
92
+ },
93
+ retryInvoicingQboSyncTaskSuccess(draft, action) {
94
+ draft.retryFetchStateByTaskId[action.payload.taskId] = {
95
+ fetchState: 'Completed',
96
+ error: undefined,
97
+ };
98
+ },
99
+ retryInvoicingQboSyncTaskFailure(draft, action) {
100
+ draft.retryFetchStateByTaskId[action.payload.taskId] = {
101
+ fetchState: 'Error',
102
+ error: action.payload.status,
103
+ };
104
+ },
105
+ startInvoicingQboBackfill: {
106
+ reducer(draft) {
107
+ draft.backfillFetchState = {
108
+ fetchState: 'In-Progress',
109
+ error: undefined,
110
+ };
111
+ },
112
+ prepare(startDate) {
113
+ return { payload: { startDate } };
114
+ },
115
+ },
116
+ startInvoicingQboBackfillSuccess(draft) {
117
+ draft.backfillFetchState = { fetchState: 'Completed', error: undefined };
118
+ },
119
+ startInvoicingQboBackfillFailure(draft, action) {
120
+ draft.backfillFetchState = { fetchState: 'Error', error: action.payload };
121
+ },
122
+ clearInvoicingQboView(draft) {
123
+ Object.assign(draft, initialState);
124
+ },
125
+ },
126
+ });
127
+ export const { fetchInvoicingQboConnection, updateInvoicingQboConnection, updateInvoicingQboConnectionFailure, fetchInvoicingQboAccounts, updateInvoicingQboAccounts, updateInvoicingQboAccountsFailure, fetchInvoicingQboClasses, updateInvoicingQboClasses, updateInvoicingQboClassesFailure, fetchInvoicingQboSyncHealth, updateInvoicingQboSyncHealth, updateInvoicingQboSyncHealthFailure, retryInvoicingQboSyncTask, retryInvoicingQboSyncTaskSuccess, retryInvoicingQboSyncTaskFailure, startInvoicingQboBackfill, startInvoicingQboBackfillSuccess, startInvoicingQboBackfillFailure, clearInvoicingQboView, } = invoicingQboView.actions;
128
+ export default invoicingQboView.reducer;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Single bundled view for the QBO integration settings screen — the
3
+ * connection, per-account-types account options, classes, sync health, and the
4
+ * fetch/action states for each of them.
5
+ */
6
+ export const getInvoicingQboView = (state) => {
7
+ const qboState = state.invoicingQboViewState;
8
+ return {
9
+ accountsByAccountTypes: qboState.accountsByAccountTypes,
10
+ accountsFetchStateByAccountTypes: qboState.accountsFetchStateByAccountTypes,
11
+ backfillState: qboState.backfillFetchState,
12
+ classes: qboState.classes,
13
+ classesFetchState: qboState.classesFetchState,
14
+ connection: qboState.connection,
15
+ connectionFetchState: qboState.connectionFetchState,
16
+ retryFetchStateByTaskId: qboState.retryFetchStateByTaskId,
17
+ syncHealth: qboState.syncHealth,
18
+ syncHealthFetchState: qboState.syncHealthFetchState,
19
+ };
20
+ };
@@ -1,15 +1,8 @@
1
- const dollarsFromAmountString = (value) => {
2
- const parsed = Number.parseFloat(value);
3
- if (Number.isNaN(parsed)) {
4
- return 0;
5
- }
6
- return parsed;
7
- };
8
1
  export const computeIssueCreditNoteTotal = (lineItems) => lineItems
9
2
  .filter((lineItem) => lineItem.selected)
10
3
  .reduce((total, lineItem) => {
11
4
  const quantity = Number.parseFloat(lineItem.quantity);
12
- const unitAmount = dollarsFromAmountString(lineItem.unitAmount);
5
+ const unitAmount = lineItem.unitAmount.amount;
13
6
  if (Number.isNaN(quantity) || quantity <= 0 || unitAmount <= 0) {
14
7
  return total;
15
8
  }
@@ -20,7 +13,7 @@ export const localDataToIssueCreditNotePayload = (localData) => {
20
13
  .filter((lineItem) => lineItem.selected)
21
14
  .map((lineItem) => {
22
15
  const quantity = Math.round(Number.parseFloat(lineItem.quantity));
23
- const unitAmount = dollarsFromAmountString(lineItem.unitAmount);
16
+ const unitAmount = lineItem.unitAmount.amount;
24
17
  return {
25
18
  description: lineItem.description,
26
19
  quantity,
@@ -43,9 +36,9 @@ export const initializeIssueCreditNoteDraftFromInvoice = (invoice) => ({
43
36
  description: lineItem.description,
44
37
  id: lineItem.id,
45
38
  maxQuantity: lineItem.quantity,
46
- maxUnitAmount: lineItem.unitAmount.amount,
39
+ maxUnitAmount: lineItem.unitAmount,
47
40
  quantity: String(lineItem.quantity),
48
41
  selected: true,
49
- unitAmount: lineItem.unitAmount.amount.toFixed(2),
42
+ unitAmount: lineItem.unitAmount,
50
43
  })),
51
44
  });