@zeniai/client-epic-state 5.0.90-betaAR1 → 5.0.90-betaAR2

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.
@@ -91,19 +91,15 @@ export const toApprovalChangableInfoPayload = (approverViewUpdateData) => {
91
91
  return payload;
92
92
  };
93
93
  /**
94
- * V1 wire mapper — used when 'approval_rule_v3_config' resolves to OFF
95
- * for the current tenant. Emits the legacy snake_case array shape that
96
- * '/approval-rules' historically consumed:
97
- * criteria: [{type: 'range', range_entity: 'currency', min, max,
98
- * currency_code, currency_symbol}]
94
+ * V1 wire mapper — used when `approval_rule_v3_config` resolves to OFF
95
+ * for the current tenant. Translates the form-side amount criteria into
96
+ * the legacy `{rangeType, rangeEntity, range: {min, max}}` shape. The
97
+ * V1 backend does not understand vendor / department criteria or the
98
+ * rule-level 3.0 fields, so those are dropped on the wire.
99
99
  *
100
- * Open-ended semantics are preserved on the wire: an absent 'min' or
101
- * 'max' is sent as 'null' rather than synthesised to '0' or
102
- * 'MAX_SAFE_INTEGER', so a 'greater_than' / 'less_than' rule stays
103
- * one-sided after rollback rather than collapsing into a closed range.
104
- *
105
- * The V1 backend does not understand vendor / department criteria or
106
- * the rule-level 3.0 fields, so those are dropped on the wire.
100
+ * The form-side amount slot is always read as a `range`-style criteria
101
+ * here: V1 schemas only supported ranges, so an absent `min` or `max`
102
+ * falls back to `0` / `Number.MAX_SAFE_INTEGER`.
107
103
  */
108
104
  export const toApprovalChangableInfoPayloadV1 = (approverViewUpdateData) => {
109
105
  const amount = approverViewUpdateData.criteria?.amount;
@@ -129,18 +125,25 @@ export const toApprovalChangableInfoPayloadV1 = (approverViewUpdateData) => {
129
125
  };
130
126
  });
131
127
  const isUpdate = approverViewUpdateData.approvalRuleId != null;
132
- const criteriaEntry = {
133
- type: 'range',
134
- range_entity: 'currency',
135
- min: minAmount?.amount ?? null,
136
- max: maxAmount?.amount ?? null,
137
- currency_code: currencyCode,
138
- currency_symbol: currencySymbol,
139
- };
140
128
  const payload = {
141
129
  steps,
142
130
  is_applicable_on_pending_approval_entity: approverViewUpdateData.isApplicableOnPendingApprovalEntity ?? true,
143
- criteria: [criteriaEntry],
131
+ criteria: {
132
+ rangeType: 'range',
133
+ rangeEntity: 'currency',
134
+ range: {
135
+ min: {
136
+ amount: minAmount?.amount ?? 0,
137
+ currencyCode,
138
+ currencySymbol,
139
+ },
140
+ max: {
141
+ amount: maxAmount?.amount ?? Number.MAX_SAFE_INTEGER,
142
+ currencyCode,
143
+ currencySymbol,
144
+ },
145
+ },
146
+ },
144
147
  };
145
148
  if (isUpdate) {
146
149
  payload.approval_rule_id = approverViewUpdateData.approvalRuleId;
@@ -1,6 +1,6 @@
1
1
  import recordGet from 'lodash/get';
2
2
  import has from 'lodash/has';
3
- import { getAmountCriteria, getDepartmentCriteria, getVendorCriteria, } from '../../../../entity/approvalRule/approvalRuleSelector';
3
+ import { getAmountCriteria } from '../../../../entity/approvalRule/approvalRuleSelector';
4
4
  import { reduceFetchState } from '../../../../commonStateTypes/reduceFetchState';
5
5
  import { getBankAccountByBankAccountId, getBankAccountsByBankAccountIds, } from '../../../../entity/bankAccount/bankAccountSelector';
6
6
  import { getContactById, getContactsByIds, } from '../../../../entity/billPay/contact/contactSelector';
@@ -679,50 +679,16 @@ export const getDefaultWithdrawFromAccount = (state, depositAccounts, companyId,
679
679
  }
680
680
  return undefined;
681
681
  };
682
- export const checkIfCreatorIsApprover = (billAmount, approvalRules, signedInUser, billVendorId, billDepartmentId) => {
682
+ export const checkIfCreatorIsApprover = (billAmount, approvalRules, signedInUser) => {
683
683
  let isCreatorAlsoApprover = false;
684
- // Approval Rules 3.0 — a rule is applicable when every criterion it
685
- // carries matches the bill. Amount uses inclusive bounds ('gte'/'lte'
686
- // on the wire), and either bound may be absent for a one-sided
687
- // 'greater_than' or 'less_than' rule. Vendor / department criteria
688
- // narrow the rule by entity; an absent criterion means 'no narrowing
689
- // on this axis'. Rules without an amount criterion at all are not
690
- // considered here, preserving the pre-3.0 behaviour where this
691
- // selector only ever matched amount-bearing rules.
692
684
  const approvalRule = approvalRules.find((rule) => {
693
685
  const amountCriteria = getAmountCriteria(rule.criteria);
694
- if (amountCriteria == null) {
686
+ if (amountCriteria?.min == null) {
695
687
  return false;
696
688
  }
697
689
  const { min, max } = amountCriteria;
698
- const matchesMin = min == null || billAmount >= min.amount;
699
- const matchesMax = max == null || billAmount <= max.amount;
700
- if (!matchesMin || !matchesMax) {
701
- return false;
702
- }
703
- const vendorCriteria = getVendorCriteria(rule.criteria);
704
- if (vendorCriteria != null) {
705
- const includesVendor = billVendorId != null &&
706
- vendorCriteria.vendorIds.includes(billVendorId);
707
- if (vendorCriteria.operator === 'is' && !includesVendor) {
708
- return false;
709
- }
710
- if (vendorCriteria.operator === 'is_not' && includesVendor) {
711
- return false;
712
- }
713
- }
714
- const departmentCriteria = getDepartmentCriteria(rule.criteria);
715
- if (departmentCriteria != null) {
716
- const includesDepartment = billDepartmentId != null &&
717
- departmentCriteria.departmentIds.includes(billDepartmentId);
718
- if (departmentCriteria.operator === 'is' && !includesDepartment) {
719
- return false;
720
- }
721
- if (departmentCriteria.operator === 'is_not' && includesDepartment) {
722
- return false;
723
- }
724
- }
725
- return true;
690
+ return (billAmount > min.amount &&
691
+ (max?.amount != null ? billAmount <= max.amount : true));
726
692
  });
727
693
  if (approvalRule != null) {
728
694
  const requireApprovalSteps = approvalRule.steps.filter((step) => step.action === 'require_approval');
@@ -30,48 +30,42 @@ interface ApprovalUpdatableInfoPayload {
30
30
  }
31
31
  /**
32
32
  * V1 / pre-3.0 wire shape. Kept so the rollback flag (gated by
33
- * 'approval_rule_v3_config' dynamic config) can target the original
34
- * '/approval-rules' endpoint with the legacy snake_case array of range
35
- * entries. None of the 3.0 rule-level fields are sent.
36
- *
37
- * The wire historically expected:
38
- * criteria: [{type, range_entity, min, max, currency_code, currency_symbol}]
39
- * with 'min' and 'max' as scalar numbers, and 'null' representing an
40
- * absent bound for one-sided rules. See 'LegacyAmountRangeCriterionPayload'
41
- * in '../../../../../entity/approvalRule/approvalRulePayload.ts' for the
42
- * matching read-side shape.
33
+ * `approval_rule_v3_config` dynamic config) can target the original
34
+ * `/approval-rules` endpoint with the legacy `{rangeType, rangeEntity, range}`
35
+ * criteria. None of the 3.0 rule-level fields are sent.
43
36
  */
44
- interface ApprovalUpdatableInfoPayloadV1CriterionEntry {
45
- currency_code: string;
46
- currency_symbol: string;
47
- /** Upper bound. 'null' for a 'greater_than' rule. */
48
- max: number | null;
49
- /** Lower bound. 'null' for a 'less_than' rule. */
50
- min: number | null;
51
- range_entity: 'currency';
52
- type: 'range';
53
- }
54
37
  interface ApprovalUpdatableInfoPayloadV1 {
55
- criteria: ApprovalUpdatableInfoPayloadV1CriterionEntry[];
38
+ criteria: {
39
+ range: {
40
+ max: {
41
+ amount: number;
42
+ currencyCode: string;
43
+ currencySymbol: string;
44
+ };
45
+ min: {
46
+ amount: number;
47
+ currencyCode: string;
48
+ currencySymbol: string;
49
+ };
50
+ };
51
+ rangeEntity: 'currency';
52
+ rangeType: 'range';
53
+ };
56
54
  is_applicable_on_pending_approval_entity: boolean;
57
55
  steps: ApproverViewStepPayload[];
58
56
  approval_rule_id?: ID;
59
57
  }
60
58
  export declare const toApprovalChangableInfoPayload: (approverViewUpdateData: ApprovalRuleUpdateData | ApprovalRuleCreateData) => Partial<ApprovalUpdatableInfoPayload>;
61
59
  /**
62
- * V1 wire mapper — used when 'approval_rule_v3_config' resolves to OFF
63
- * for the current tenant. Emits the legacy snake_case array shape that
64
- * '/approval-rules' historically consumed:
65
- * criteria: [{type: 'range', range_entity: 'currency', min, max,
66
- * currency_code, currency_symbol}]
67
- *
68
- * Open-ended semantics are preserved on the wire: an absent 'min' or
69
- * 'max' is sent as 'null' rather than synthesised to '0' or
70
- * 'MAX_SAFE_INTEGER', so a 'greater_than' / 'less_than' rule stays
71
- * one-sided after rollback rather than collapsing into a closed range.
60
+ * V1 wire mapper — used when `approval_rule_v3_config` resolves to OFF
61
+ * for the current tenant. Translates the form-side amount criteria into
62
+ * the legacy `{rangeType, rangeEntity, range: {min, max}}` shape. The
63
+ * V1 backend does not understand vendor / department criteria or the
64
+ * rule-level 3.0 fields, so those are dropped on the wire.
72
65
  *
73
- * The V1 backend does not understand vendor / department criteria or
74
- * the rule-level 3.0 fields, so those are dropped on the wire.
66
+ * The form-side amount slot is always read as a `range`-style criteria
67
+ * here: V1 schemas only supported ranges, so an absent `min` or `max`
68
+ * falls back to `0` / `Number.MAX_SAFE_INTEGER`.
75
69
  */
76
70
  export declare const toApprovalChangableInfoPayloadV1: (approverViewUpdateData: ApprovalRuleUpdateData | ApprovalRuleCreateData) => Partial<ApprovalUpdatableInfoPayloadV1>;
77
71
  export {};
@@ -95,19 +95,15 @@ const toApprovalChangableInfoPayload = (approverViewUpdateData) => {
95
95
  };
96
96
  exports.toApprovalChangableInfoPayload = toApprovalChangableInfoPayload;
97
97
  /**
98
- * V1 wire mapper — used when 'approval_rule_v3_config' resolves to OFF
99
- * for the current tenant. Emits the legacy snake_case array shape that
100
- * '/approval-rules' historically consumed:
101
- * criteria: [{type: 'range', range_entity: 'currency', min, max,
102
- * currency_code, currency_symbol}]
98
+ * V1 wire mapper — used when `approval_rule_v3_config` resolves to OFF
99
+ * for the current tenant. Translates the form-side amount criteria into
100
+ * the legacy `{rangeType, rangeEntity, range: {min, max}}` shape. The
101
+ * V1 backend does not understand vendor / department criteria or the
102
+ * rule-level 3.0 fields, so those are dropped on the wire.
103
103
  *
104
- * Open-ended semantics are preserved on the wire: an absent 'min' or
105
- * 'max' is sent as 'null' rather than synthesised to '0' or
106
- * 'MAX_SAFE_INTEGER', so a 'greater_than' / 'less_than' rule stays
107
- * one-sided after rollback rather than collapsing into a closed range.
108
- *
109
- * The V1 backend does not understand vendor / department criteria or
110
- * the rule-level 3.0 fields, so those are dropped on the wire.
104
+ * The form-side amount slot is always read as a `range`-style criteria
105
+ * here: V1 schemas only supported ranges, so an absent `min` or `max`
106
+ * falls back to `0` / `Number.MAX_SAFE_INTEGER`.
111
107
  */
112
108
  const toApprovalChangableInfoPayloadV1 = (approverViewUpdateData) => {
113
109
  const amount = approverViewUpdateData.criteria?.amount;
@@ -133,18 +129,25 @@ const toApprovalChangableInfoPayloadV1 = (approverViewUpdateData) => {
133
129
  };
134
130
  });
135
131
  const isUpdate = approverViewUpdateData.approvalRuleId != null;
136
- const criteriaEntry = {
137
- type: 'range',
138
- range_entity: 'currency',
139
- min: minAmount?.amount ?? null,
140
- max: maxAmount?.amount ?? null,
141
- currency_code: currencyCode,
142
- currency_symbol: currencySymbol,
143
- };
144
132
  const payload = {
145
133
  steps,
146
134
  is_applicable_on_pending_approval_entity: approverViewUpdateData.isApplicableOnPendingApprovalEntity ?? true,
147
- criteria: [criteriaEntry],
135
+ criteria: {
136
+ rangeType: 'range',
137
+ rangeEntity: 'currency',
138
+ range: {
139
+ min: {
140
+ amount: minAmount?.amount ?? 0,
141
+ currencyCode,
142
+ currencySymbol,
143
+ },
144
+ max: {
145
+ amount: maxAmount?.amount ?? Number.MAX_SAFE_INTEGER,
146
+ currencyCode,
147
+ currencySymbol,
148
+ },
149
+ },
150
+ },
148
151
  };
149
152
  if (isUpdate) {
150
153
  payload.approval_rule_id = approverViewUpdateData.approvalRuleId;
@@ -157,4 +157,4 @@ export declare const getDefaultWithdrawFromAccount: (state: RootState, depositAc
157
157
  accountId: ID;
158
158
  accountType: AccountTypeSubConfigCodeKeyType;
159
159
  }) => FundingAccount | undefined;
160
- export declare const checkIfCreatorIsApprover: (billAmount: number, approvalRules: ApprovalRuleWithUser[], signedInUser?: LoggedInUser, billVendorId?: ID, billDepartmentId?: ID) => boolean;
160
+ export declare const checkIfCreatorIsApprover: (billAmount: number, approvalRules: ApprovalRuleWithUser[], signedInUser?: LoggedInUser) => boolean;
@@ -696,50 +696,16 @@ const getDefaultWithdrawFromAccount = (state, depositAccounts, companyId, lastSe
696
696
  return undefined;
697
697
  };
698
698
  exports.getDefaultWithdrawFromAccount = getDefaultWithdrawFromAccount;
699
- const checkIfCreatorIsApprover = (billAmount, approvalRules, signedInUser, billVendorId, billDepartmentId) => {
699
+ const checkIfCreatorIsApprover = (billAmount, approvalRules, signedInUser) => {
700
700
  let isCreatorAlsoApprover = false;
701
- // Approval Rules 3.0 — a rule is applicable when every criterion it
702
- // carries matches the bill. Amount uses inclusive bounds ('gte'/'lte'
703
- // on the wire), and either bound may be absent for a one-sided
704
- // 'greater_than' or 'less_than' rule. Vendor / department criteria
705
- // narrow the rule by entity; an absent criterion means 'no narrowing
706
- // on this axis'. Rules without an amount criterion at all are not
707
- // considered here, preserving the pre-3.0 behaviour where this
708
- // selector only ever matched amount-bearing rules.
709
701
  const approvalRule = approvalRules.find((rule) => {
710
702
  const amountCriteria = (0, approvalRuleSelector_1.getAmountCriteria)(rule.criteria);
711
- if (amountCriteria == null) {
703
+ if (amountCriteria?.min == null) {
712
704
  return false;
713
705
  }
714
706
  const { min, max } = amountCriteria;
715
- const matchesMin = min == null || billAmount >= min.amount;
716
- const matchesMax = max == null || billAmount <= max.amount;
717
- if (!matchesMin || !matchesMax) {
718
- return false;
719
- }
720
- const vendorCriteria = (0, approvalRuleSelector_1.getVendorCriteria)(rule.criteria);
721
- if (vendorCriteria != null) {
722
- const includesVendor = billVendorId != null &&
723
- vendorCriteria.vendorIds.includes(billVendorId);
724
- if (vendorCriteria.operator === 'is' && !includesVendor) {
725
- return false;
726
- }
727
- if (vendorCriteria.operator === 'is_not' && includesVendor) {
728
- return false;
729
- }
730
- }
731
- const departmentCriteria = (0, approvalRuleSelector_1.getDepartmentCriteria)(rule.criteria);
732
- if (departmentCriteria != null) {
733
- const includesDepartment = billDepartmentId != null &&
734
- departmentCriteria.departmentIds.includes(billDepartmentId);
735
- if (departmentCriteria.operator === 'is' && !includesDepartment) {
736
- return false;
737
- }
738
- if (departmentCriteria.operator === 'is_not' && includesDepartment) {
739
- return false;
740
- }
741
- }
742
- return true;
707
+ return (billAmount > min.amount &&
708
+ (max?.amount != null ? billAmount <= max.amount : true));
743
709
  });
744
710
  if (approvalRule != null) {
745
711
  const requireApprovalSteps = approvalRule.steps.filter((step) => step.action === 'require_approval');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.0.90-betaAR1",
3
+ "version": "5.0.90-betaAR2",
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",