@zeniai/client-epic-state 5.0.69-betaML11 → 5.0.69-betaML12

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.
@@ -54,26 +54,72 @@ const toTransaction = (payload) => {
54
54
  logo: (0, urlPayload_1.toURL)(payload.logo),
55
55
  attachments: payload.attachments?.map((attachmentPayload) => (0, attachmentPayload_1.toAttachment)(attachmentPayload)),
56
56
  qboId: payload.qbo_id,
57
- vendorId: Boolean(payload.vendor_id) === true ? payload.vendor_id : undefined,
58
- vendorName: Boolean(payload.vendor_name) === true ? payload.vendor_name : undefined,
59
- customerId: Boolean(payload.customer_id) === true ? payload.customer_id : undefined,
60
- customerName: Boolean(payload.customer_name) === true ? payload.customer_name : undefined,
61
- targetAccountId: Boolean(payload.target_account_id) === true ? payload.target_account_id : undefined,
62
- targetAccountIntegrationId: Boolean(payload.target_account_integration_id) === true ? payload.target_account_integration_id : undefined,
63
- targetAccountName: Boolean(payload.target_account_name) === true ? payload.target_account_name : undefined,
64
- toFromAccountId: Boolean(payload.to_from_account_id) === true ? payload.to_from_account_id : undefined,
65
- toFromAccountIntegrationId: Boolean(payload.to_from_account_integration_id) === true ? payload.to_from_account_integration_id : undefined,
66
- toFromAccountName: Boolean(payload.to_from_account_name) === true ? payload.to_from_account_name : undefined,
67
- categoryId: Boolean(payload.category_id) === true ? payload.category_id : undefined,
68
- categoryIntegrationId: Boolean(payload.category_integration_id) === true ? payload.category_integration_id : undefined,
69
- classId: Boolean(payload.class_id) === true ? payload.class_id : undefined,
70
- className: Boolean(payload.class_name) === true ? payload.class_name : undefined,
71
- projectId: Boolean(payload.accounting_project_id) === true ? payload.accounting_project_id : undefined,
72
- projectName: Boolean(payload.accounting_project_name) === true ? payload.accounting_project_name : undefined,
73
- categoryName: Boolean(payload.category_name) === true ? payload.category_name : undefined,
74
- accountIntegrationId: Boolean(payload.account_integration_id) === true ? payload.account_integration_id : undefined,
75
- qboAccountId: Boolean(payload.qbo_account_id) === true ? payload.qbo_account_id : undefined,
76
- qboAccountName: Boolean(payload.qbo_account_name) === true ? payload.qbo_account_name : undefined,
57
+ ...(payload.vendor_id != null && payload.vendor_id !== ''
58
+ ? { vendorId: payload.vendor_id }
59
+ : {}),
60
+ ...(payload.vendor_name != null && payload.vendor_name !== ''
61
+ ? { vendorName: payload.vendor_name }
62
+ : {}),
63
+ ...(payload.customer_id != null && payload.customer_id !== ''
64
+ ? { customerId: payload.customer_id }
65
+ : {}),
66
+ ...(payload.customer_name != null && payload.customer_name !== ''
67
+ ? { customerName: payload.customer_name }
68
+ : {}),
69
+ ...(payload.target_account_id != null && payload.target_account_id !== ''
70
+ ? { targetAccountId: payload.target_account_id }
71
+ : {}),
72
+ ...(payload.target_account_integration_id != null &&
73
+ payload.target_account_integration_id !== ''
74
+ ? { targetAccountIntegrationId: payload.target_account_integration_id }
75
+ : {}),
76
+ ...(payload.target_account_name != null && payload.target_account_name !== ''
77
+ ? { targetAccountName: payload.target_account_name }
78
+ : {}),
79
+ ...(payload.to_from_account_id != null && payload.to_from_account_id !== ''
80
+ ? { toFromAccountId: payload.to_from_account_id }
81
+ : {}),
82
+ ...(payload.to_from_account_integration_id != null &&
83
+ payload.to_from_account_integration_id !== ''
84
+ ? { toFromAccountIntegrationId: payload.to_from_account_integration_id }
85
+ : {}),
86
+ ...(payload.to_from_account_name != null && payload.to_from_account_name !== ''
87
+ ? { toFromAccountName: payload.to_from_account_name }
88
+ : {}),
89
+ ...(payload.category_id != null && payload.category_id !== ''
90
+ ? { categoryId: payload.category_id }
91
+ : {}),
92
+ ...(payload.category_integration_id != null &&
93
+ payload.category_integration_id !== ''
94
+ ? { categoryIntegrationId: payload.category_integration_id }
95
+ : {}),
96
+ ...(payload.class_id != null && payload.class_id !== ''
97
+ ? { classId: payload.class_id }
98
+ : {}),
99
+ ...(payload.class_name != null && payload.class_name !== ''
100
+ ? { className: payload.class_name }
101
+ : {}),
102
+ ...(payload.accounting_project_id != null &&
103
+ payload.accounting_project_id !== ''
104
+ ? { projectId: payload.accounting_project_id }
105
+ : {}),
106
+ ...(payload.accounting_project_name != null &&
107
+ payload.accounting_project_name !== ''
108
+ ? { projectName: payload.accounting_project_name }
109
+ : {}),
110
+ ...(payload.category_name != null && payload.category_name !== ''
111
+ ? { categoryName: payload.category_name }
112
+ : {}),
113
+ ...(payload.account_integration_id != null &&
114
+ payload.account_integration_id !== ''
115
+ ? { accountIntegrationId: payload.account_integration_id }
116
+ : {}),
117
+ ...(payload.qbo_account_id != null && payload.qbo_account_id !== ''
118
+ ? { qboAccountId: payload.qbo_account_id }
119
+ : {}),
120
+ ...(payload.qbo_account_name != null && payload.qbo_account_name !== ''
121
+ ? { qboAccountName: payload.qbo_account_name }
122
+ : {}),
77
123
  linkedTransactions: payload.linked_transactions?.map((transactionIDPayload) => (0, transactionIDPayload_1.toTransactionID)(transactionIDPayload)),
78
124
  lines,
79
125
  account: payloadHasAccountInfo(payload)
@@ -50,26 +50,72 @@ export const toTransaction = (payload) => {
50
50
  logo: toURL(payload.logo),
51
51
  attachments: payload.attachments?.map((attachmentPayload) => toAttachment(attachmentPayload)),
52
52
  qboId: payload.qbo_id,
53
- vendorId: Boolean(payload.vendor_id) === true ? payload.vendor_id : undefined,
54
- vendorName: Boolean(payload.vendor_name) === true ? payload.vendor_name : undefined,
55
- customerId: Boolean(payload.customer_id) === true ? payload.customer_id : undefined,
56
- customerName: Boolean(payload.customer_name) === true ? payload.customer_name : undefined,
57
- targetAccountId: Boolean(payload.target_account_id) === true ? payload.target_account_id : undefined,
58
- targetAccountIntegrationId: Boolean(payload.target_account_integration_id) === true ? payload.target_account_integration_id : undefined,
59
- targetAccountName: Boolean(payload.target_account_name) === true ? payload.target_account_name : undefined,
60
- toFromAccountId: Boolean(payload.to_from_account_id) === true ? payload.to_from_account_id : undefined,
61
- toFromAccountIntegrationId: Boolean(payload.to_from_account_integration_id) === true ? payload.to_from_account_integration_id : undefined,
62
- toFromAccountName: Boolean(payload.to_from_account_name) === true ? payload.to_from_account_name : undefined,
63
- categoryId: Boolean(payload.category_id) === true ? payload.category_id : undefined,
64
- categoryIntegrationId: Boolean(payload.category_integration_id) === true ? payload.category_integration_id : undefined,
65
- classId: Boolean(payload.class_id) === true ? payload.class_id : undefined,
66
- className: Boolean(payload.class_name) === true ? payload.class_name : undefined,
67
- projectId: Boolean(payload.accounting_project_id) === true ? payload.accounting_project_id : undefined,
68
- projectName: Boolean(payload.accounting_project_name) === true ? payload.accounting_project_name : undefined,
69
- categoryName: Boolean(payload.category_name) === true ? payload.category_name : undefined,
70
- accountIntegrationId: Boolean(payload.account_integration_id) === true ? payload.account_integration_id : undefined,
71
- qboAccountId: Boolean(payload.qbo_account_id) === true ? payload.qbo_account_id : undefined,
72
- qboAccountName: Boolean(payload.qbo_account_name) === true ? payload.qbo_account_name : undefined,
53
+ ...(payload.vendor_id != null && payload.vendor_id !== ''
54
+ ? { vendorId: payload.vendor_id }
55
+ : {}),
56
+ ...(payload.vendor_name != null && payload.vendor_name !== ''
57
+ ? { vendorName: payload.vendor_name }
58
+ : {}),
59
+ ...(payload.customer_id != null && payload.customer_id !== ''
60
+ ? { customerId: payload.customer_id }
61
+ : {}),
62
+ ...(payload.customer_name != null && payload.customer_name !== ''
63
+ ? { customerName: payload.customer_name }
64
+ : {}),
65
+ ...(payload.target_account_id != null && payload.target_account_id !== ''
66
+ ? { targetAccountId: payload.target_account_id }
67
+ : {}),
68
+ ...(payload.target_account_integration_id != null &&
69
+ payload.target_account_integration_id !== ''
70
+ ? { targetAccountIntegrationId: payload.target_account_integration_id }
71
+ : {}),
72
+ ...(payload.target_account_name != null && payload.target_account_name !== ''
73
+ ? { targetAccountName: payload.target_account_name }
74
+ : {}),
75
+ ...(payload.to_from_account_id != null && payload.to_from_account_id !== ''
76
+ ? { toFromAccountId: payload.to_from_account_id }
77
+ : {}),
78
+ ...(payload.to_from_account_integration_id != null &&
79
+ payload.to_from_account_integration_id !== ''
80
+ ? { toFromAccountIntegrationId: payload.to_from_account_integration_id }
81
+ : {}),
82
+ ...(payload.to_from_account_name != null && payload.to_from_account_name !== ''
83
+ ? { toFromAccountName: payload.to_from_account_name }
84
+ : {}),
85
+ ...(payload.category_id != null && payload.category_id !== ''
86
+ ? { categoryId: payload.category_id }
87
+ : {}),
88
+ ...(payload.category_integration_id != null &&
89
+ payload.category_integration_id !== ''
90
+ ? { categoryIntegrationId: payload.category_integration_id }
91
+ : {}),
92
+ ...(payload.class_id != null && payload.class_id !== ''
93
+ ? { classId: payload.class_id }
94
+ : {}),
95
+ ...(payload.class_name != null && payload.class_name !== ''
96
+ ? { className: payload.class_name }
97
+ : {}),
98
+ ...(payload.accounting_project_id != null &&
99
+ payload.accounting_project_id !== ''
100
+ ? { projectId: payload.accounting_project_id }
101
+ : {}),
102
+ ...(payload.accounting_project_name != null &&
103
+ payload.accounting_project_name !== ''
104
+ ? { projectName: payload.accounting_project_name }
105
+ : {}),
106
+ ...(payload.category_name != null && payload.category_name !== ''
107
+ ? { categoryName: payload.category_name }
108
+ : {}),
109
+ ...(payload.account_integration_id != null &&
110
+ payload.account_integration_id !== ''
111
+ ? { accountIntegrationId: payload.account_integration_id }
112
+ : {}),
113
+ ...(payload.qbo_account_id != null && payload.qbo_account_id !== ''
114
+ ? { qboAccountId: payload.qbo_account_id }
115
+ : {}),
116
+ ...(payload.qbo_account_name != null && payload.qbo_account_name !== ''
117
+ ? { qboAccountName: payload.qbo_account_name }
118
+ : {}),
73
119
  linkedTransactions: payload.linked_transactions?.map((transactionIDPayload) => toTransactionID(transactionIDPayload)),
74
120
  lines,
75
121
  account: payloadHasAccountInfo(payload)
@@ -21,8 +21,16 @@ const createTransferEntrySlice = createSlice({
21
21
  prepare(payload) {
22
22
  return { payload };
23
23
  },
24
+ /**
25
+ * Defense-in-depth: clear any stale `lastTransferEntryReplacement` on a
26
+ * fresh dispatch so a second `createTransferEntry` never inherits the
27
+ * previous run's replacement signal (e.g. if the prior `timer(0)` cleanup
28
+ * never fired). Mirrors the host hook's `consumedReplacementRef` guard
29
+ * (see `useTransferEntryRouteReplacement` in zeni-web-app-ui).
30
+ */
24
31
  reducer(draft) {
25
32
  draft.createTransferEntryStatus = 'In-Progress';
33
+ draft.lastTransferEntryReplacement = undefined;
26
34
  },
27
35
  },
28
36
  createTransferEntrySuccess(draft) {
@@ -25,6 +25,20 @@ const failureSnackbar = () => openSnackbar({
25
25
  function buildFailureActions() {
26
26
  return [createTransferEntryFailure(), failureSnackbar()];
27
27
  }
28
+ /**
29
+ * Defensive guard: a BE response that returns 200 with `transaction != null`
30
+ * but where the body lacks a usable id/type is unsafe to seed into the store —
31
+ * downstream code would store under a garbage key and the host hook would try
32
+ * to navigate to a broken url. Treat that case as failure so the source row
33
+ * stays visible and the user can retry.
34
+ */
35
+ function isWellFormedTransactionPayload(t) {
36
+ return (t != null &&
37
+ typeof t.transaction_id === 'string' &&
38
+ t.transaction_id !== '' &&
39
+ typeof t.transaction_type === 'string' &&
40
+ t.transaction_type !== '');
41
+ }
28
42
  function buildSuccessSideEffects(sourceTransactionId) {
29
43
  return [
30
44
  createTransferEntrySuccess(),
@@ -104,8 +118,12 @@ export const createTransferEntryEpic = (actions$, _state$, zeniAPI) => actions$.
104
118
  if (!isSuccessResponse(response)) {
105
119
  return from(buildFailureActions());
106
120
  }
107
- if (response.data?.transaction != null) {
108
- return handleSuccessWithTransactionBody(response.data.transaction, response.data.vendors, previousTransactionId, transactionId);
121
+ const transaction = response.data?.transaction;
122
+ if (transaction != null) {
123
+ if (!isWellFormedTransactionPayload(transaction)) {
124
+ return from(buildFailureActions());
125
+ }
126
+ return handleSuccessWithTransactionBody(transaction, response.data?.vendors, previousTransactionId, transactionId);
109
127
  }
110
128
  return handleSuccessWithoutTransactionBody(transactionId);
111
129
  }), catchError(() => from(buildFailureActions())));
@@ -25,8 +25,16 @@ const createTransferEntrySlice = (0, toolkit_1.createSlice)({
25
25
  prepare(payload) {
26
26
  return { payload };
27
27
  },
28
+ /**
29
+ * Defense-in-depth: clear any stale `lastTransferEntryReplacement` on a
30
+ * fresh dispatch so a second `createTransferEntry` never inherits the
31
+ * previous run's replacement signal (e.g. if the prior `timer(0)` cleanup
32
+ * never fired). Mirrors the host hook's `consumedReplacementRef` guard
33
+ * (see `useTransferEntryRouteReplacement` in zeni-web-app-ui).
34
+ */
28
35
  reducer(draft) {
29
36
  draft.createTransferEntryStatus = 'In-Progress';
37
+ draft.lastTransferEntryReplacement = undefined;
30
38
  },
31
39
  },
32
40
  createTransferEntrySuccess(draft) {
@@ -28,6 +28,20 @@ const failureSnackbar = () => (0, snackbarReducer_1.openSnackbar)({
28
28
  function buildFailureActions() {
29
29
  return [(0, createTransferEntryReducer_1.createTransferEntryFailure)(), failureSnackbar()];
30
30
  }
31
+ /**
32
+ * Defensive guard: a BE response that returns 200 with `transaction != null`
33
+ * but where the body lacks a usable id/type is unsafe to seed into the store —
34
+ * downstream code would store under a garbage key and the host hook would try
35
+ * to navigate to a broken url. Treat that case as failure so the source row
36
+ * stays visible and the user can retry.
37
+ */
38
+ function isWellFormedTransactionPayload(t) {
39
+ return (t != null &&
40
+ typeof t.transaction_id === 'string' &&
41
+ t.transaction_id !== '' &&
42
+ typeof t.transaction_type === 'string' &&
43
+ t.transaction_type !== '');
44
+ }
31
45
  function buildSuccessSideEffects(sourceTransactionId) {
32
46
  return [
33
47
  (0, createTransferEntryReducer_1.createTransferEntrySuccess)(),
@@ -107,8 +121,12 @@ const createTransferEntryEpic = (actions$, _state$, zeniAPI) => actions$.pipe((0
107
121
  if (!(0, responsePayload_1.isSuccessResponse)(response)) {
108
122
  return (0, rxjs_1.from)(buildFailureActions());
109
123
  }
110
- if (response.data?.transaction != null) {
111
- return handleSuccessWithTransactionBody(response.data.transaction, response.data.vendors, previousTransactionId, transactionId);
124
+ const transaction = response.data?.transaction;
125
+ if (transaction != null) {
126
+ if (!isWellFormedTransactionPayload(transaction)) {
127
+ return (0, rxjs_1.from)(buildFailureActions());
128
+ }
129
+ return handleSuccessWithTransactionBody(transaction, response.data?.vendors, previousTransactionId, transactionId);
112
130
  }
113
131
  return handleSuccessWithoutTransactionBody(transactionId);
114
132
  }), (0, operators_1.catchError)(() => (0, rxjs_1.from)(buildFailureActions())));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.0.69-betaML11",
3
+ "version": "5.0.69-betaML12",
4
4
  "description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib/esm/index.js",