@zeniai/client-epic-state 5.2.29 → 5.2.30-betaRD0

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 (105) hide show
  1. package/lib/entity/aiCfo/aiCfoPayload.d.ts +21 -0
  2. package/lib/entity/aiCfo/aiCfoReducer.js +73 -1
  3. package/lib/entity/aiCfo/aiCfoState.d.ts +24 -0
  4. package/lib/entity/file/fileState.d.ts +3 -3
  5. package/lib/entity/file/fileState.js +2 -0
  6. package/lib/entity/invoicing/creditNote/creditNotePayload.d.ts +0 -1
  7. package/lib/entity/invoicing/creditNote/creditNotePayload.js +0 -1
  8. package/lib/entity/invoicing/creditNote/creditNoteState.d.ts +0 -1
  9. package/lib/entity/invoicing/invoice/invoicePayload.d.ts +0 -1
  10. package/lib/entity/invoicing/invoice/invoicePayload.js +0 -1
  11. package/lib/entity/invoicing/invoice/invoiceState.d.ts +0 -1
  12. package/lib/entity/invoicing/invoicingCommonPayload.d.ts +2 -0
  13. package/lib/entity/invoicing/invoicingCustomer/invoicingCustomerPayload.d.ts +0 -1
  14. package/lib/entity/invoicing/invoicingCustomer/invoicingCustomerPayload.js +0 -1
  15. package/lib/entity/invoicing/invoicingCustomer/invoicingCustomerState.d.ts +0 -1
  16. package/lib/entity/invoicing/invoicingSubscription/invoicingSubscriptionPayload.d.ts +1 -2
  17. package/lib/entity/invoicing/invoicingSubscription/invoicingSubscriptionPayload.js +1 -2
  18. package/lib/entity/invoicing/invoicingSubscription/invoicingSubscriptionState.d.ts +1 -2
  19. package/lib/epic.d.ts +2 -1
  20. package/lib/epic.js +2 -0
  21. package/lib/esm/entity/aiCfo/aiCfoReducer.js +73 -1
  22. package/lib/esm/entity/file/fileState.js +2 -0
  23. package/lib/esm/entity/invoicing/creditNote/creditNotePayload.js +0 -1
  24. package/lib/esm/entity/invoicing/invoice/invoicePayload.js +0 -1
  25. package/lib/esm/entity/invoicing/invoicingCustomer/invoicingCustomerPayload.js +0 -1
  26. package/lib/esm/entity/invoicing/invoicingSubscription/invoicingSubscriptionPayload.js +1 -2
  27. package/lib/esm/epic.js +2 -0
  28. package/lib/esm/index.js +2 -2
  29. package/lib/esm/view/aiCfoView/aiCfoViewReducer.js +5 -0
  30. package/lib/esm/view/aiCfoView/aiCfoViewSelector.js +1 -0
  31. package/lib/esm/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulesEpic.js +29 -11
  32. package/lib/esm/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +4 -2
  33. package/lib/esm/view/invoicing/couponView/couponViewState.js +4 -4
  34. package/lib/esm/view/invoicing/dataImportView/dataImportViewSelector.js +2 -0
  35. package/lib/esm/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewSelector.js +1 -0
  36. package/lib/esm/view/invoicing/invoicingConfigView/invoicingConfigViewReducer.js +2 -0
  37. package/lib/esm/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewState.js +3 -3
  38. package/lib/esm/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewState.js +4 -4
  39. package/lib/esm/view/invoicing/invoicingOverview/invoicingOverviewPayload.js +0 -1
  40. package/lib/esm/view/invoicing/invoicingSubscriptionDetailView/invoicingSubscriptionDetailViewSelector.js +2 -1
  41. package/lib/esm/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewSelector.js +1 -0
  42. package/lib/esm/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewState.js +0 -2
  43. package/lib/esm/view/invoicing/settingsView/invoicingBrandingFormConfig.js +5 -0
  44. package/lib/esm/view/invoicing/settingsView/settingsViewSelector.js +5 -1
  45. package/lib/esm/view/recommendation/recommendationReducer.js +13 -1
  46. package/lib/esm/view/recommendation/submitRecommendationFeedbackEpic.js +52 -0
  47. package/lib/esm/view/spendManagement/billPay/billPaySetupApproverView/epic/fetchBillPaySetupApproverViewEpic.js +18 -2
  48. package/lib/esm/view/spendManagement/billPay/billPaySetupApproverView/epic/initializeBillPaySetupApproverViewUpdateDataEpic.js +0 -1
  49. package/lib/esm/view/spendManagement/billPay/billPaySetupApproverView/types/commonPayload.js +0 -3
  50. package/lib/esm/view/spendManagement/reimbursement/remiSetupApproverView/epic/fetchRemiSetupApproverViewEpic.js +18 -2
  51. package/lib/esm/view/spendManagement/reimbursement/remiSetupApproverView/epic/initializeRemiSetupApproverViewUpdateDataEpic.js +0 -1
  52. package/lib/index.d.ts +7 -5
  53. package/lib/index.js +24 -23
  54. package/lib/view/aiCfoView/aiCfoViewReducer.js +5 -0
  55. package/lib/view/aiCfoView/aiCfoViewSelector.js +1 -0
  56. package/lib/view/aiCfoView/aiCfoViewState.d.ts +7 -0
  57. package/lib/view/expenseAutomationView/epics/jeSchedule/fetchJeSchedulesEpic.js +29 -11
  58. package/lib/view/expenseAutomationView/selectorTypes/jeSchedulesSelectorTypes.d.ts +5 -3
  59. package/lib/view/expenseAutomationView/selectors/jeSchedulesViewSelector.d.ts +2 -5
  60. package/lib/view/expenseAutomationView/selectors/jeSchedulesViewSelector.js +4 -2
  61. package/lib/view/invoicing/couponView/couponViewState.d.ts +2 -2
  62. package/lib/view/invoicing/couponView/couponViewState.js +4 -4
  63. package/lib/view/invoicing/dataImportView/dataImportViewSelector.d.ts +2 -0
  64. package/lib/view/invoicing/dataImportView/dataImportViewSelector.js +2 -0
  65. package/lib/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewSelector.d.ts +1 -0
  66. package/lib/view/invoicing/invoicingCatalogListView/invoicingCatalogListViewSelector.js +1 -0
  67. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewPayload.d.ts +1 -0
  68. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewReducer.d.ts +1 -1
  69. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewReducer.js +2 -0
  70. package/lib/view/invoicing/invoicingConfigView/invoicingConfigViewState.d.ts +1 -0
  71. package/lib/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewState.d.ts +2 -2
  72. package/lib/view/invoicing/invoicingCustomerListView/invoicingCustomerListViewState.js +3 -3
  73. package/lib/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewState.d.ts +2 -2
  74. package/lib/view/invoicing/invoicingDunningCaseListView/invoicingDunningCaseListViewState.js +4 -4
  75. package/lib/view/invoicing/invoicingOverview/invoicingOverviewPayload.d.ts +0 -1
  76. package/lib/view/invoicing/invoicingOverview/invoicingOverviewPayload.js +0 -1
  77. package/lib/view/invoicing/invoicingOverview/invoicingOverviewState.d.ts +0 -1
  78. package/lib/view/invoicing/invoicingSubscriptionDetailView/invoicingSubscriptionDetailViewSelector.d.ts +3 -1
  79. package/lib/view/invoicing/invoicingSubscriptionDetailView/invoicingSubscriptionDetailViewSelector.js +1 -0
  80. package/lib/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewSelector.d.ts +2 -0
  81. package/lib/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewSelector.js +1 -0
  82. package/lib/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewState.d.ts +2 -2
  83. package/lib/view/invoicing/invoicingSubscriptionListView/invoicingSubscriptionListViewState.js +0 -2
  84. package/lib/view/invoicing/issueCreditNote/issueCreditNotePayload.d.ts +0 -1
  85. package/lib/view/invoicing/settingsView/invoicingBrandingFormConfig.d.ts +1 -0
  86. package/lib/view/invoicing/settingsView/invoicingBrandingFormConfig.js +5 -0
  87. package/lib/view/invoicing/settingsView/settingsViewSelector.d.ts +5 -1
  88. package/lib/view/invoicing/settingsView/settingsViewSelector.js +4 -0
  89. package/lib/view/invoicing/subscriptionAction/subscriptionActionRequestPayload.d.ts +8 -0
  90. package/lib/view/recommendation/recommendationPayload.d.ts +34 -0
  91. package/lib/view/recommendation/recommendationReducer.d.ts +2 -1
  92. package/lib/view/recommendation/recommendationReducer.js +14 -2
  93. package/lib/view/recommendation/submitRecommendationFeedbackEpic.d.ts +7 -0
  94. package/lib/view/recommendation/submitRecommendationFeedbackEpic.js +56 -0
  95. package/lib/view/spendManagement/billPay/billPaySetupApproverView/epic/fetchBillPaySetupApproverViewEpic.d.ts +2 -1
  96. package/lib/view/spendManagement/billPay/billPaySetupApproverView/epic/fetchBillPaySetupApproverViewEpic.js +18 -2
  97. package/lib/view/spendManagement/billPay/billPaySetupApproverView/epic/initializeBillPaySetupApproverViewUpdateDataEpic.js +0 -1
  98. package/lib/view/spendManagement/billPay/billPaySetupApproverView/types/commonPayload.d.ts +0 -1
  99. package/lib/view/spendManagement/billPay/billPaySetupApproverView/types/commonPayload.js +0 -3
  100. package/lib/view/spendManagement/billPay/billPaySetupApproverView/types/commonState.d.ts +0 -1
  101. package/lib/view/spendManagement/commonHistoryView/commonHistory.d.ts +1 -1
  102. package/lib/view/spendManagement/reimbursement/remiSetupApproverView/epic/fetchRemiSetupApproverViewEpic.d.ts +2 -1
  103. package/lib/view/spendManagement/reimbursement/remiSetupApproverView/epic/fetchRemiSetupApproverViewEpic.js +18 -2
  104. package/lib/view/spendManagement/reimbursement/remiSetupApproverView/epic/initializeRemiSetupApproverViewUpdateDataEpic.js +0 -1
  105. package/package.json +1 -1
@@ -110,6 +110,7 @@ function chatHistoryToQA(history) {
110
110
  ? parsedAgentMessage.follow_up_questions
111
111
  : [],
112
112
  suggestedReply: parsedAgentMessage.suggested_reply ?? undefined,
113
+ capabilityPromo: toCapabilityPromo(parsedAgentMessage.capability_promo),
113
114
  assumptions: parsedAgentMessage.assumptions ?? '',
114
115
  insights: parsedAgentMessage.insights ?? '',
115
116
  summarizedTitle: parsedAgentMessage.summarized_title ?? undefined,
@@ -118,6 +119,7 @@ function chatHistoryToQA(history) {
118
119
  artifactTitle: parsedAgentMessage.artifact_title ?? undefined,
119
120
  artifactHostUrl: parsedAgentMessage.artifact_host_url ?? undefined,
120
121
  exports: toAiCfoExports(parsedAgentMessage.exports),
122
+ skillDraft: toSkillDraft(parsedAgentMessage.skill_draft),
121
123
  },
122
124
  createdAt: zeniDate(agentMessage.created_at),
123
125
  };
@@ -368,8 +370,76 @@ const toAiCfoVisualizationData = (data) => {
368
370
  columns,
369
371
  };
370
372
  };
373
+ const AI_CFO_CAPABILITY_PROMO_SURFACES = [
374
+ 'banner',
375
+ 'inline',
376
+ ];
377
+ const toAiCfoCapabilityPromoSurface = (value) => AI_CFO_CAPABILITY_PROMO_SURFACES.find((surface) => surface === value);
378
+ const trimmedOrUndefined = (value) => {
379
+ const trimmed = value?.trim() ?? '';
380
+ return trimmed.length > 0 ? trimmed : undefined;
381
+ };
382
+ const toSkillDraft = (payload) => {
383
+ // Dropped rather than partially built when either required field is missing:
384
+ // a Save control wired to a body the create endpoint would reject is worse
385
+ // than no control, because the failure only surfaces after the click.
386
+ if (payload == null) {
387
+ return undefined;
388
+ }
389
+ // Trimmed to '' FIRST, then chosen by length. Not `??`: a whitespace-only
390
+ // display_name trims to '', which is not nullish, so `??` would keep the
391
+ // empty string and drop a draft that has a perfectly good `name`.
392
+ const rawName = payload.name?.trim() ?? '';
393
+ const rawDisplayName = payload.display_name?.trim() ?? '';
394
+ const name = rawName.length > 0 ? rawName : rawDisplayName;
395
+ const displayName = rawDisplayName.length > 0 ? rawDisplayName : name;
396
+ const instructions = payload.instructions?.trim() ?? '';
397
+ if (name.length === 0 || instructions.length === 0) {
398
+ return undefined;
399
+ }
400
+ const examples = payload.parameter_hints?.examples;
401
+ return {
402
+ // Kept SEPARATE even though the agent currently sends both the same: the
403
+ // create endpoint takes `name` and `display_name` as distinct fields, and
404
+ // collapsing them here would submit the display label as the identifier if
405
+ // they ever diverge.
406
+ name,
407
+ displayName,
408
+ instructions,
409
+ // An empty description is ABSENT, matching the way blank parameter hints
410
+ // are dropped below — `?? undefined` would store ''.
411
+ description: trimmedOrUndefined(payload.description),
412
+ parameterHints: Array.isArray(examples)
413
+ ? examples.filter((hint) => typeof hint === 'string' && hint.trim().length > 0)
414
+ : undefined,
415
+ };
416
+ };
417
+ const toCapabilityPromo = (payload) => {
418
+ // Runtime payloads can miss fields regardless of the wire type — drop the
419
+ // promo on a missing/empty id or copy, and narrow surface to the union.
420
+ if (payload == null || !payload.id || !payload.copy) {
421
+ return undefined;
422
+ }
423
+ const surface = toAiCfoCapabilityPromoSurface(payload.surface);
424
+ if (surface == null) {
425
+ return undefined;
426
+ }
427
+ const cta = toCapabilityPromoCta(payload.cta);
428
+ return {
429
+ copy: payload.copy,
430
+ id: payload.id,
431
+ surface,
432
+ ...(cta != null && { cta }),
433
+ };
434
+ };
435
+ const toCapabilityPromoCta = (payload) => {
436
+ if (payload == null || !payload.label || !payload.prefill) {
437
+ return undefined;
438
+ }
439
+ return { label: payload.label, prefill: payload.prefill };
440
+ };
371
441
  const toResponseBlockType = (answer, userId) => {
372
- const { metadata, state, type, text, visualization, table, follow_up_questions, suggested_reply, assumptions, insights, summarized_title, artifact, artifact_title, artifact_host_url, exports, } = answer;
442
+ const { metadata, state, type, text, visualization, table, follow_up_questions, suggested_reply, assumptions, insights, summarized_title, artifact, artifact_title, artifact_host_url, capability_promo, exports, skill_draft, } = answer;
373
443
  const { chat_session_id, message_id, timestamp, chain_of_thought_action_steps: chainOfThoughtActionSteps = [], } = metadata;
374
444
  return {
375
445
  messageId: message_id,
@@ -384,6 +454,7 @@ const toResponseBlockType = (answer, userId) => {
384
454
  table: table != null ? toAiCfoVisualization(table) : undefined,
385
455
  followUpQuestions: follow_up_questions ?? [],
386
456
  suggestedReply: suggested_reply ?? undefined,
457
+ capabilityPromo: toCapabilityPromo(capability_promo),
387
458
  assumptions: assumptions ?? '',
388
459
  insights: insights ?? '',
389
460
  summarizedTitle: summarized_title ?? undefined,
@@ -392,6 +463,7 @@ const toResponseBlockType = (answer, userId) => {
392
463
  artifactTitle: artifact_title ?? undefined,
393
464
  artifactHostUrl: artifact_host_url ?? undefined,
394
465
  exports: toAiCfoExports(exports),
466
+ skillDraft: toSkillDraft(skill_draft),
395
467
  },
396
468
  createdAt: zeniDate(timestamp),
397
469
  };
@@ -72,6 +72,7 @@ const ALLOWED_DOCUMENT_TYPES = [
72
72
  'bill_pay',
73
73
  'certificate_of_incorporation',
74
74
  'check',
75
+ 'company_logo',
75
76
  'contract',
76
77
  'driving_license',
77
78
  'employer_identification_number_confirmation',
@@ -95,6 +96,7 @@ const ALLOWED_PURPOSE_IDS = [
95
96
  'business_verification_others',
96
97
  'bill_pay',
97
98
  'international_wire_business_verification',
99
+ 'company_logo',
98
100
  'others',
99
101
  ];
100
102
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -9,7 +9,6 @@ export const mapInvoicingCreditNoteLineItemPayloadToState = (payload, currencyCo
9
9
  unitAmount: toAmount(payload.unit_amount, currencyCode, currencySymbol),
10
10
  entityType: payload.entity_type,
11
11
  id: payload.id,
12
- productCode: payload.product_code,
13
12
  });
14
13
  export const mapInvoicingCreditNoteAllocationPayloadToState = (payload, currencyCode, currencySymbol) => ({
15
14
  allocatedAmount: toAmount(payload.allocated_amount, currencyCode, currencySymbol),
@@ -19,7 +19,6 @@ const mapLineItem = (payload, currencyCode, currencySymbol) => ({
19
19
  itemLevelDiscountAmount: payload.item_level_discount_amount != null
20
20
  ? toAmount(payload.item_level_discount_amount, currencyCode, currencySymbol)
21
21
  : undefined,
22
- productCode: payload.product_code,
23
22
  subscriptionID: payload.subscription_id,
24
23
  taxAmount: payload.tax_amount != null
25
24
  ? toAmount(payload.tax_amount, currencyCode, currencySymbol)
@@ -72,7 +72,6 @@ export const mapInvoicingCustomerPayloadToState = (payload) => {
72
72
  phone: payload.phone,
73
73
  plan: payload.plan,
74
74
  planName: payload.plan_name,
75
- productCode: payload.product_code,
76
75
  preferredCurrencyCode: payload.preferred_currency_code,
77
76
  promotionalCredits: payload.promotional_credits != null
78
77
  ? toAmount(payload.promotional_credits, currencyCode, currencySymbol)
@@ -12,7 +12,6 @@ export const mapInvoicingSubscriptionItemPayloadToState = (payload, currencyCode
12
12
  itemPriceCode: payload.item_price_code,
13
13
  itemPriceID: payload.item_price_id,
14
14
  itemType: payload.item_type,
15
- productCode: payload.product_code,
16
15
  quantity: payload.quantity,
17
16
  unitPrice: payload.unit_price != null
18
17
  ? toAmount(payload.unit_price, currencyCode, currencySymbol)
@@ -58,6 +57,7 @@ export const mapInvoicingSubscriptionPayloadToState = (payload) => {
58
57
  billingPeriod: payload.billing_period,
59
58
  billingPeriodUnit: payload.billing_period_unit,
60
59
  cancelReason: payload.cancel_reason,
60
+ cancelReasonCode: payload.cancel_reason_code,
61
61
  cancelledAt: payload.cancelled_at != null ? date(payload.cancelled_at) : undefined,
62
62
  catalogItemID: payload.catalog_item_id,
63
63
  contractTerm: payload.contract_term != null
@@ -88,7 +88,6 @@ export const mapInvoicingSubscriptionPayloadToState = (payload) => {
88
88
  : undefined,
89
89
  planID: payload.plan_id,
90
90
  planName: payload.plan_name,
91
- productCode: payload.product_code,
92
91
  poNumber: payload.po_number,
93
92
  remainingBillingCycles: payload.remaining_billing_cycles,
94
93
  resumeDate: payload.resume_date != null ? date(payload.resume_date) : undefined,
package/lib/esm/epic.js CHANGED
@@ -400,6 +400,7 @@ import { fetchEntityRecommendationsByTransactionIdEpic, } from './view/recommend
400
400
  import { fetchEntityRecommendationsForLineUpdateEpic, } from './view/recommendation/fetchEntityRecommendationsForLineUpdateEpic';
401
401
  import { fetchRecommendationByEntityIdEpic, } from './view/recommendation/fetchRecommendationByEntityIdEpic';
402
402
  import { fetchRecommendationByEntityNameEpic, } from './view/recommendation/fetchRecommendationByEntityNameEpic';
403
+ import { submitRecommendationFeedbackEpic, } from './view/recommendation/submitRecommendationFeedbackEpic';
403
404
  import { fetchReferralsEpic, } from './view/referralView/epics/fetchReferralsEpic';
404
405
  import { fetchRewardsPlanEpic, } from './view/referralView/epics/fetchRewardsPlanEpic';
405
406
  import { resendReferralInviteEpic, } from './view/referralView/epics/resendReferralInviteEpic';
@@ -1382,6 +1383,7 @@ const ALL_EPICS = [
1382
1383
  submitInvoicingBrandingFormEpic,
1383
1384
  submitInvoicingQboSettingsEpic,
1384
1385
  submitQuestionEpic,
1386
+ submitRecommendationFeedbackEpic,
1385
1387
  switchExpenseAutomationJESchedulesTabEpic,
1386
1388
  syncInvoicingSubscriptionCouponsEpic,
1387
1389
  syncTabsAfterAutomatchEpic,
package/lib/esm/index.js CHANGED
@@ -266,7 +266,7 @@ import { fetchProfitAndLossForTimeframeProjectView, fetchProfitAndLossProjectVie
266
266
  import { getProfitAndLossProjectView } from './view/profitAndLossProjectView/profitAndLossProjectViewSelector';
267
267
  import { fetchProjectList } from './view/projectList/projectListReducer';
268
268
  import { getProjectList, } from './view/projectList/projectListSelector';
269
- import { fetchEntityRecommendationsByTransactionId, fetchEntityRecommendationsForLineUpdate, fetchRecommendationByEntityId, fetchRecommendationByEntityName, } from './view/recommendation/recommendationReducer';
269
+ import { fetchEntityRecommendationsByTransactionId, fetchEntityRecommendationsForLineUpdate, fetchRecommendationByEntityId, fetchRecommendationByEntityName, submitRecommendationFeedback, } from './view/recommendation/recommendationReducer';
270
270
  import { DEFAULT_REFERRER_AMOUNT, clearReferrals, fetchReferrals, fetchRewardsPlan, resendReferralInvite, saveReferralFormDataInLocalStore, sendReferralInvite, updateReferViewed, updateReferralListSortUiState, } from './view/referralView/referralReducer';
271
271
  import { getInviteFormView, getReferralListView, getRewardsPlanCard, } from './view/referralView/referralSelector';
272
272
  import { AmountStatusTypes, StatusTypes, toReferralListViewSortKeyType, } from './view/referralView/referralState';
@@ -632,7 +632,7 @@ export { getSpendManagementEffectiveListPeriod, getSelectedCompanyOfficer, showB
632
632
  export { canSendMonthEndEmailReport, shouldEnableCalendarPickerForLastReportSent, };
633
633
  export { getInternationalSubConfigCodeKey, BILL_NEW_PAYMENT_METHODS, NEW_INTERNATIONAL_METHOD_SUBTEXT, SWIFT_OUR_INTERNATIONAL_METHOD_SUBTEXT, LOCAL_CURRENCY_INTERNATIONAL_METHOD_SUBTEXT, };
634
634
  export { getVendorList, updateSortUiState, updatePageToken, updateYTDSelectionUIState, isColumnYTDSpend, isVendorsTabVisible, toVendorReportIDType, getVendorFiling1099List, fetchVendorsFiling1099Download, };
635
- export { fetchEntityRecommendationsByTransactionId, fetchEntityRecommendationsForLineUpdate, fetchRecommendationByEntityId, fetchRecommendationByEntityName, };
635
+ export { fetchEntityRecommendationsByTransactionId, fetchEntityRecommendationsForLineUpdate, fetchRecommendationByEntityId, fetchRecommendationByEntityName, submitRecommendationFeedback, };
636
636
  export { fetchOwnerList };
637
637
  export { getZeniAccountsConfigDetail, getZeniAccountList, fetchZeniAccountList, fetchAccountList, createCheckingAccount, fetchPaymentAccountList, unlinkPaymentAccount, getDepositAccountDetail, getAllDepositAccounts, getDepositAccountDetailForPDF, fetchDepositAccount, fetchZeniAccountsConfig, updateTransferMoneyLocalData, updateTransferToLocalData, updateDepositToLocalData, transferMoney, getTransferDetail, clearTransferDetail, clearReviewTransferDetail, fetchReviewTransferDetail, fetchDepositAccountDetail, depositCheck, updateCheckDepositLocalData, clearCheckDeposit, getCheckDepositDetail, updateDepositAccount, fetchDepositAccountHistorySuccess, fetchDepositAccountHistoryFailure, fetchZeniAccStatementPage, getZeniAccStatements, };
638
638
  export { fetchDepositAccountTransactionList, getDepositAccountTransactionList, };
@@ -58,6 +58,7 @@ export const initialAiCfoViewState = {
58
58
  fetchState: 'Not-Started',
59
59
  error: undefined,
60
60
  savedMacroId: undefined,
61
+ kind: undefined,
61
62
  },
62
63
  skillVersionBody: {
63
64
  fetchState: 'Not-Started',
@@ -777,6 +778,7 @@ const aiCfoView = createSlice({
777
778
  fetchState: 'In-Progress',
778
779
  error: undefined,
779
780
  savedMacroId: undefined,
781
+ kind: 'save',
780
782
  };
781
783
  },
782
784
  prepare(payload) {
@@ -828,6 +830,7 @@ const aiCfoView = createSlice({
828
830
  fetchState: 'In-Progress',
829
831
  error: undefined,
830
832
  savedMacroId: undefined,
833
+ kind: 'visibility',
831
834
  };
832
835
  },
833
836
  prepare(payload) {
@@ -863,6 +866,7 @@ const aiCfoView = createSlice({
863
866
  fetchState: 'In-Progress',
864
867
  error: undefined,
865
868
  savedMacroId: undefined,
869
+ kind: 'delete',
866
870
  };
867
871
  },
868
872
  prepare(payload) {
@@ -891,6 +895,7 @@ const aiCfoView = createSlice({
891
895
  fetchState: 'Not-Started',
892
896
  error: undefined,
893
897
  savedMacroId: undefined,
898
+ kind: undefined,
894
899
  };
895
900
  },
896
901
  openSkillsBrowser(draft) {
@@ -62,6 +62,7 @@ const EMPTY_SKILL_MUTATION_STATE = {
62
62
  fetchState: 'Not-Started',
63
63
  error: undefined,
64
64
  savedMacroId: undefined,
65
+ kind: undefined,
65
66
  };
66
67
  export function getSkills(state) {
67
68
  return state.aiCfoViewState?.skills ?? EMPTY_SKILLS_STATE;
@@ -9,6 +9,18 @@ import { clearAll } from '../../../../rootActions';
9
9
  import { date } from '../../../../zeniDayJS';
10
10
  import { SERVICE_MONTH_FORMAT, } from '../../payload/jeSchedulesPayload';
11
11
  import { fetchJeSchedules, fetchJeSchedulesFailure, fetchJeSchedulesSuccess, initializeJeScheduleLocalData, } from '../../reducers/jeSchedulesViewReducer';
12
+ /**
13
+ * How far back to look for accrued schedules, matching the Vendors page
14
+ * (`getDefaultSelectedTimeframeForScheduleType`, 'accrued_expenses'), which queries the same
15
+ * start_date filter through /1.0/schedules.
16
+ */
17
+ const ACCRUED_LOOKBACK_MONTHS = 24;
18
+ /**
19
+ * One-time accruals keep their own, narrower lookback. Unlike an accrued schedule they belong to
20
+ * exactly one service month and never span months, so the reason the accrued window has to reach
21
+ * back does not apply to them -- widening it would only add older history to the Existing tab.
22
+ */
23
+ const ONE_TIME_ACCRUAL_LOOKBACK_MONTHS = 12;
12
24
  export const fetchJeSchedulesEpic = (actions$, state$, zeniAPI) => actions$.pipe(filter(fetchJeSchedules.match), mergeMap((action) => {
13
25
  const state = state$.value;
14
26
  const { expenseAutomationViewState: { selectedPeriodByTenantId }, } = state;
@@ -30,15 +42,21 @@ export const fetchJeSchedulesEpic = (actions$, state$, zeniAPI) => actions$.pipe
30
42
  const endDate = isClosePeriodTab || existingEndDate == null
31
43
  ? toString(period.end)
32
44
  : toString(existingEndDate);
33
- // Accrued rows are windowed on the schedule's own start_date rather than a transaction date,
34
- // so they get their own bounds. Review wants the month being closed; Existing wants the year
35
- // up to endDate, snapped to the 1st — the same window the server used to compute for itself.
36
- const accruedStartDate = isClosePeriodTab
37
- ? toString(period.start)
38
- : date(endDate)
39
- .subtract(1, 'year')
40
- .startOf('month')
41
- .format(DEFAULT_DATE_FORMAT);
45
+ // Accrued rows are windowed on the schedule's own start_date -- its FIRST posting date --
46
+ // rather than a transaction date, so the lower bound has to reach back past the start of
47
+ // every accrual still running in this period, not just the ones that began in it. Review
48
+ // used to send the close month itself, which returned only accruals whose first posting fell
49
+ // in that month and hid every longer-running one.
50
+ const parsedEndDate = date(endDate);
51
+ const accruedStartDate = parsedEndDate
52
+ .subtract(ACCRUED_LOOKBACK_MONTHS, 'month')
53
+ .startOf('month')
54
+ .format(DEFAULT_DATE_FORMAT);
55
+ // Formatted as a service month, not a date: that is the only thing it is sent as.
56
+ const oneTimeAccrualServiceMonthStart = parsedEndDate
57
+ .subtract(ONE_TIME_ACCRUAL_LOOKBACK_MONTHS, 'month')
58
+ .startOf('month')
59
+ .format(SERVICE_MONTH_FORMAT);
42
60
  const queryParam = {
43
61
  base_transaction_end_date: endDate,
44
62
  end_date: endDate,
@@ -59,8 +77,8 @@ export const fetchJeSchedulesEpic = (actions$, state$, zeniAPI) => actions$.pipe
59
77
  filter_failed_je_postings_by_date: true,
60
78
  }
61
79
  : {
62
- one_time_accrual_service_month_start: date(accruedStartDate).format(SERVICE_MONTH_FORMAT),
63
- one_time_accrual_service_month_end: date(endDate).format(SERVICE_MONTH_FORMAT),
80
+ one_time_accrual_service_month_start: oneTimeAccrualServiceMonthStart,
81
+ one_time_accrual_service_month_end: parsedEndDate.format(SERVICE_MONTH_FORMAT),
64
82
  // Existing lists what actually reached the books.
65
83
  one_time_accrual_statuses: ['posted'],
66
84
  // A failed posting from an earlier month still needs to surface here, so this list
@@ -12,8 +12,8 @@ import { getClassList, getNestedClassListHierarchy, } from '../../classList/clas
12
12
  import { getAccountsTypesForScheduleDetails } from '../../scheduleView/scheduleDetailView/scheduleDetailLocalDataHelper';
13
13
  import { filterNestedAccountHierarchyById } from '../../scheduleView/scheduleDetailView/scheduleDetailSelector';
14
14
  import { getAccountLabelForScheduleType, getAccountsTypesForScheduleList, } from '../../scheduleView/scheduleListView/scheduleListHelper';
15
- import { getAllSteps } from './getAllStepsHelper';
16
15
  import { ALL_JE_PAGE_TABS, getJESchedulePeriodTabKey, } from '../types/jeSchedulesViewState';
16
+ import { getAllSteps } from './getAllStepsHelper';
17
17
  import { getJEAccountSettingsView } from './jeAccountSettingsViewSelector';
18
18
  import { getNewScheduleView } from './jeNewScheduleViewSelector';
19
19
  // Matches the AI-automation user addresses webc's schedule list row already
@@ -330,7 +330,9 @@ export function getExpenseAutomationJESchedulesView(state) {
330
330
  */
331
331
  const sumToAmount = (schedules) => {
332
332
  const first = schedules[0];
333
- const currency = first != null ? getScheduleAmountObject(first) : allScheduleAmountFallback;
333
+ const currency = first != null
334
+ ? getScheduleAmountObject(first)
335
+ : allScheduleAmountFallback;
334
336
  return {
335
337
  ...currency,
336
338
  amount: sumAmount(schedules),
@@ -1,7 +1,7 @@
1
1
  import { stringToUnion } from '../../../commonStateTypes/stringToUnion';
2
2
  export const ALL_INVOICING_COUPON_SORT_KEYS = [
3
- 'coupon_id',
4
- 'name',
3
+ 'code',
4
+ 'coupon',
5
5
  'discount',
6
6
  'duration',
7
7
  'created',
@@ -12,8 +12,8 @@ export const ALL_INVOICING_COUPON_SORT_KEYS = [
12
12
  ];
13
13
  export const toInvoicingCouponSortKey = (v) => stringToUnion(v, ALL_INVOICING_COUPON_SORT_KEYS);
14
14
  export const INVOICING_COUPON_COLUMNS = [
15
- { key: 'coupon_id', sortable: true },
16
- { key: 'name', sortable: true },
15
+ { key: 'code', sortable: true },
16
+ { key: 'coupon', sortable: true },
17
17
  { key: 'discount', sortable: false },
18
18
  { key: 'duration', sortable: true },
19
19
  { key: 'created', sortable: true },
@@ -1,3 +1,4 @@
1
+ import { invoicingFieldHelpForGroup } from '../invoicingConfigView/invoicingConfigViewSelector';
1
2
  /**
2
3
  * Single bundled view for the invoicing data-import surfaces — consumers read
3
4
  * only this. Carries the migration `sessions` list + `sessionsFetch`, the
@@ -7,6 +8,7 @@
7
8
  export const getInvoicingDataImportView = (state) => {
8
9
  const dataImportState = state.invoicingDataImportViewState;
9
10
  return {
11
+ fieldHelp: invoicingFieldHelpForGroup(state, 'data_import'),
10
12
  activeMigrationSessionId: dataImportState.activeDetailId,
11
13
  sessionDetail: dataImportState.sessionDetail,
12
14
  sessionDetailFetch: dataImportState.sessionDetailFetch,
@@ -18,6 +18,7 @@ export const getInvoicingCatalogPrices = (items) => items.flatMap((item) => (ite
18
18
  export const getInvoicingCatalogListView = createSelector((state) => state.invoicingPlanState, (state) => state.invoicingCatalogListViewState, (state) => state.invoicingConfigViewState.catalogItemLifecycleActionOptions, (planState, listViewState, catalogItemLifecycleActionOptions) => ({
19
19
  catalogItemLifecycleActionOptions,
20
20
  plans: getInvoicingPlansByIds(planState, listViewState.planIds),
21
+ planSort: listViewState.planSort,
21
22
  productColumns: INVOICING_PRODUCT_COLUMNS,
22
23
  products: getInvoicingPlansByIds(planState, listViewState.productIds),
23
24
  productSort: listViewState.productSort,
@@ -34,6 +34,7 @@ export const initialState = {
34
34
  discountStatusOptions: [],
35
35
  dunningCaseStatusOptions: [],
36
36
  invoiceStatusOptions: [],
37
+ subscriptionCancelReasonCodeOptions: [],
37
38
  subscriptionStatusOptions: [],
38
39
  transactionStatusOptions: [],
39
40
  transactionTypeOptions: [],
@@ -103,6 +104,7 @@ export function mapInvoicingConfigPayloadToState(payload) {
103
104
  discountStatusOptions: (payload.discount_status_options ?? []).map(toInvoicingNamedOption),
104
105
  dunningCaseStatusOptions: (payload.dunning_case_status_options ?? []).map(toInvoicingNamedOption),
105
106
  invoiceStatusOptions: (payload.invoice_status_options ?? []).map(toInvoicingNamedOption),
107
+ subscriptionCancelReasonCodeOptions: (payload.subscription_cancel_reason_code_options ?? []).map(toInvoicingNamedOption),
106
108
  subscriptionStatusOptions: (payload.subscription_status_options ?? []).map(toInvoicingNamedOption),
107
109
  transactionStatusOptions: (payload.transaction_status_options ?? []).map(toInvoicingNamedOption),
108
110
  transactionTypeOptions: (payload.transaction_type_options ?? []).map(toInvoicingNamedOption),
@@ -3,16 +3,16 @@ export const ALL_INVOICING_CUSTOMER_SORT_KEYS = [
3
3
  'name',
4
4
  'email',
5
5
  'billingAddress',
6
- 'cardStatus',
6
+ 'card',
7
7
  'autoCollect',
8
8
  'status',
9
9
  ];
10
10
  export const toInvoicingCustomerSortKey = (v) => stringToUnion(v, ALL_INVOICING_CUSTOMER_SORT_KEYS);
11
11
  export const INVOICING_CUSTOMER_COLUMNS = [
12
- { key: 'name', sortable: true },
12
+ { key: 'name', sortable: false },
13
13
  { key: 'email', sortable: true },
14
14
  { key: 'billingAddress', sortable: false },
15
- { key: 'cardStatus', sortable: true },
15
+ { key: 'card', sortable: true },
16
16
  { key: 'autoCollect', sortable: true },
17
17
  { key: 'status', sortable: true },
18
18
  ];
@@ -3,8 +3,8 @@ export const ALL_INVOICING_DUNNING_CASE_SORT_KEYS = [
3
3
  'customer',
4
4
  'invoice',
5
5
  'attempts',
6
- 'nextRetry',
7
- 'daysPastDue',
6
+ 'next_retry',
7
+ 'days_past_due',
8
8
  'failureReason',
9
9
  'status',
10
10
  'amount',
@@ -14,8 +14,8 @@ export const INVOICING_DUNNING_CASE_COLUMNS = [
14
14
  { key: 'customer', sortable: true },
15
15
  { key: 'invoice', sortable: true },
16
16
  { key: 'attempts', sortable: true },
17
- { key: 'nextRetry', sortable: true },
18
- { key: 'daysPastDue', sortable: true },
17
+ { key: 'next_retry', sortable: true },
18
+ { key: 'days_past_due', sortable: true },
19
19
  { key: 'failureReason', sortable: false },
20
20
  { key: 'status', sortable: true },
21
21
  { key: 'amount', sortable: true },
@@ -37,7 +37,6 @@ export const mapInvoicingPlanRevenuePointPayloadToState = (payload) => ({
37
37
  mrr: toAmount(payload.mrr, 'USD', '$'),
38
38
  pct: payload.pct,
39
39
  planID: payload.plan,
40
- productCode: payload.product_code,
41
40
  });
42
41
  export const mapInvoicingDashboardSummaryPayloadToState = (payload) => ({
43
42
  customerMovement: {
@@ -1,11 +1,12 @@
1
1
  import { getInvoicingSubscriptionById as getInvoicingSubscriptionByIdFromState } from '../../../entity/invoicing/invoicingSubscription/invoicingSubscriptionSelector';
2
- import { invoicingFieldHelpForGroup } from '../invoicingConfigView/invoicingConfigViewSelector';
2
+ import { getInvoicingConfigView, invoicingFieldHelpForGroup, } from '../invoicingConfigView/invoicingConfigViewSelector';
3
3
  import { getInvoicingSubscriptionActionState } from '../subscriptionAction/subscriptionActionSelector';
4
4
  export const getInvoicingSubscriptionById = (state, id) => getInvoicingSubscriptionByIdFromState(state.invoicingSubscriptionState, id);
5
5
  export const getInvoicingSubscriptionDetail = (state, invoicingSubscriptionID) => {
6
6
  const { error, fetchState } = state.invoicingSubscriptionDetailViewState;
7
7
  return {
8
8
  fieldHelp: invoicingFieldHelpForGroup(state, 'subscription'),
9
+ cancelReasonCodeOptions: getInvoicingConfigView(state).subscriptionCancelReasonCodeOptions,
9
10
  actionState: getInvoicingSubscriptionActionState(invoicingSubscriptionID)(state),
10
11
  subscription: getInvoicingSubscriptionById(state, invoicingSubscriptionID),
11
12
  fetchState,
@@ -28,6 +28,7 @@ export const getInvoicingSubscriptionListView = createSelector((state) => state.
28
28
  count: listViewState.counts,
29
29
  countsFetchState: listViewState.countsFetchState,
30
30
  subscriptionCommercialEditAllowedFromStatuses: configState.subscriptionCommercialEditAllowedFromStatuses,
31
+ cancelReasonCodeOptions: configState.subscriptionCancelReasonCodeOptions,
31
32
  subscriptionLifecycleActionOptions: configState.subscriptionLifecycleActionOptions,
32
33
  subscriptionMetadataEditAllowedFromStatuses: configState.subscriptionMetadataEditAllowedFromStatuses,
33
34
  statusOptions,
@@ -3,7 +3,6 @@ export const ALL_INVOICING_SUBSCRIPTION_SORT_KEYS = [
3
3
  'subscription',
4
4
  'customer',
5
5
  'plan',
6
- 'product_code',
7
6
  'term_end',
8
7
  'next_billing',
9
8
  'auto_collect',
@@ -15,7 +14,6 @@ export const INVOICING_SUBSCRIPTION_COLUMNS = [
15
14
  { key: 'subscription', sortable: true },
16
15
  { key: 'customer', sortable: false },
17
16
  { key: 'plan', sortable: true },
18
- { key: 'product_code', sortable: true },
19
17
  { key: 'term_end', sortable: true },
20
18
  { key: 'next_billing', sortable: true },
21
19
  { key: 'auto_collect', sortable: true },
@@ -7,6 +7,7 @@ export const initialInvoicingBrandingFormLocalData = {
7
7
  dateFormat: '',
8
8
  defaultMemo: '',
9
9
  email: '',
10
+ invoiceFooterText: '',
10
11
  invoicePrefix: '',
11
12
  logoFileId: '',
12
13
  phone: '',
@@ -19,6 +20,7 @@ export const entityToInvoicingBrandingFormLocalData = (settings) => ({
19
20
  dateFormat: settings?.date_format ?? '',
20
21
  defaultMemo: settings?.default_memo ?? '',
21
22
  email: settings?.email ?? '',
23
+ invoiceFooterText: settings?.invoice_footer_text ?? '',
22
24
  invoicePrefix: settings?.invoice_prefix ?? '',
23
25
  logoFileId: settings?.logo_file_id ?? '',
24
26
  phone: settings?.phone ?? '',
@@ -39,6 +41,9 @@ export const brandingLocalDataToSettingsPayload = (localData, address) => ({
39
41
  date_format: trimOrUndefined(localData.dateFormat),
40
42
  default_memo: trimOrUndefined(localData.defaultMemo),
41
43
  email: trimOrUndefined(localData.email),
44
+ // Sent even when blank: "" is how the API is told to hide the footer line,
45
+ // so this one must not collapse to `undefined` like the fields above.
46
+ invoice_footer_text: localData.invoiceFooterText.trim(),
42
47
  invoice_prefix: trimOrUndefined(localData.invoicePrefix),
43
48
  logo_file_id: localData.logoFileId,
44
49
  phone: trimOrUndefined(localData.phone),
@@ -1,6 +1,6 @@
1
1
  import { getInvoicingQboAccountMappingId, } from '../../../entity/invoicing/invoicingCommonPayload';
2
2
  import { getFormattedAddress } from '../../addressView/addressViewSelector';
3
- import { getInvoicingConfigView } from '../invoicingConfigView/invoicingConfigViewSelector';
3
+ import { getInvoicingConfigView, invoicingFieldHelpForGroup, } from '../invoicingConfigView/invoicingConfigViewSelector';
4
4
  import { getInvoicingQboView, isInvoicingAccountingPickerDataLoading, } from '../invoicingQboView/invoicingQboViewSelector';
5
5
  import { INVOICING_BUSINESS_ADDRESS_TYPE, entityToInvoicingBrandingFormLocalData, } from './invoicingBrandingFormConfig';
6
6
  import { initialInvoicingQboSettingsLocalData, } from './invoicingQboSettingsFormConfig';
@@ -26,6 +26,7 @@ export const getInvoicingSettingsFetchState = (state) => ({
26
26
  export const getInvoicingSettingsView = (state) => {
27
27
  const { error, fetchState, saveFetchState, settings, stripeFetchState } = state.invoicingSettingsViewState;
28
28
  return {
29
+ fieldHelp: invoicingFieldHelpForGroup(state, 'settings'),
29
30
  error,
30
31
  fetchState,
31
32
  saveState: saveFetchState,
@@ -78,6 +79,7 @@ export const getInvoicingSettingsHubView = (state) => {
78
79
  const { dunningScheduleActionOptions, enableState, error: configError, fetchState: configFetchState, qboAccountMappingKindOptions, qboCustomerMatchingOptions, qboSyncStatusOptions, status, } = getInvoicingConfigView(state);
79
80
  const { canEnable, isQboConfigured, isStripeConnected } = deriveInvoicingEnablePrerequisites(state.invoicingSettingsViewState.settings, state.invoicingQboViewState.connection?.connected === true, qboAccountMappingKindOptions);
80
81
  return {
82
+ fieldHelp: invoicingFieldHelpForGroup(state, 'settings'),
81
83
  // Keeps the onboarding step in the gate: a tenant that has not accepted the
82
84
  // terms cannot enable invoicing, whatever the integrations say.
83
85
  canEnableInvoicing: status === 'settings' && canEnable,
@@ -108,6 +110,7 @@ export const getInvoicingQboSettingsFormView = (state) => {
108
110
  const { fetchState: configFetchState, qboAccountMappingKindOptions, qboCustomerMatchingOptions, qboSyncStatusOptions, } = getInvoicingConfigView(state);
109
111
  const qboView = getInvoicingQboView(state);
110
112
  return {
113
+ fieldHelp: invoicingFieldHelpForGroup(state, 'settings'),
111
114
  accountMappingKindOptions: qboAccountMappingKindOptions,
112
115
  accounts: qboView.accounts,
113
116
  backfillState: qboView.backfillState,
@@ -147,6 +150,7 @@ export const getInvoicingBrandingFormView = (state) => {
147
150
  ? getFormattedAddress(newAddress.addressToCreate)
148
151
  : undefined;
149
152
  return {
153
+ fieldHelp: invoicingFieldHelpForGroup(state, 'settings'),
150
154
  addressLabel,
151
155
  dateFormatOptions: getInvoicingConfigView(state).dateFormatOptions,
152
156
  localData: brandingDraft ?? entityToInvoicingBrandingFormLocalData(settings),
@@ -131,6 +131,18 @@ const recommendation = createSlice({
131
131
  };
132
132
  },
133
133
  },
134
+ // Fire-and-forget human feedback on a recommendation (accept, ranked
135
+ // override or outside override). Handled by submitRecommendationFeedbackEpic
136
+ // which POSTs to the accounting recommendation-feedback endpoint. No state
137
+ // mutation: the categorization UX must not depend on the feedback round-trip.
138
+ submitRecommendationFeedback: {
139
+ reducer() {
140
+ // Fire-and-forget: no state mutation; the epic performs the POST.
141
+ },
142
+ prepare(args) {
143
+ return { payload: args };
144
+ },
145
+ },
134
146
  },
135
147
  extraReducers: (builder) => {
136
148
  builder.addCase(clearAll, (draft) => {
@@ -138,7 +150,7 @@ const recommendation = createSlice({
138
150
  });
139
151
  },
140
152
  });
141
- export const { fetchEntityRecommendationsByTransactionId, fetchEntityRecommendationsForLineUpdate, fetchRecommendationByEntityName, fetchRecommendationByEntityId, updateRecommendationForEntity, updateRecommendationForEntityFailure, indicateReadyToInitializeRecommendation, } = recommendation.actions;
153
+ export const { fetchEntityRecommendationsByTransactionId, fetchEntityRecommendationsForLineUpdate, fetchRecommendationByEntityName, fetchRecommendationByEntityId, updateRecommendationForEntity, updateRecommendationForEntityFailure, indicateReadyToInitializeRecommendation, submitRecommendationFeedback, } = recommendation.actions;
142
154
  export default recommendation.reducer;
143
155
  const doUpdateRecommendationForEntity = (draft, entityId, entityType, payload) => {
144
156
  const key = getEntityRecommendationKey(entityType, entityId);