@zauru-sdk/utils 6.19.0 → 6.20.0

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.
@@ -1,6 +1,7 @@
1
1
  import { handlePossibleAxiosErrors } from "@zauru-sdk/common";
2
2
  import { createWebAppTableRegister, getVariablesByName, getWebAppTableRegisters, updateWebAppTableRegister, } from "@zauru-sdk/services";
3
3
  const TABLE_VAR = "price_adjustment_rules_web_app_table_id";
4
+ export const PRICE_ADJUSTMENT_REVERSAL_RULES_TABLE_VAR = "price_adjustment_reversal_rules_web_app_table_id";
4
5
  const DEFAULT_FILTERS = {
5
6
  itemMode: "all",
6
7
  itemIds: [],
@@ -44,6 +45,29 @@ export const updatePriceAdjustmentRule = (headers, session, id, body) => {
44
45
  return updateWebAppTableRegister(headers, tableId, Number(id), body);
45
46
  });
46
47
  };
48
+ const getReversalTableId = async (headers, session) => {
49
+ const vars = await getVariablesByName(headers, session, [
50
+ PRICE_ADJUSTMENT_REVERSAL_RULES_TABLE_VAR,
51
+ ]);
52
+ return vars[PRICE_ADJUSTMENT_REVERSAL_RULES_TABLE_VAR];
53
+ };
54
+ export const filterActivePriceAdjustmentReversalRules = (rules) => (rules ?? []).filter((rule) => !rule.data?.fechaEliminacion && rule.data?.activa !== false);
55
+ export const getPriceAdjustmentReversalRules = (headers, session) => handlePossibleAxiosErrors(async () => {
56
+ const tableId = await getReversalTableId(headers, session);
57
+ const response = await getWebAppTableRegisters(session, tableId);
58
+ if (response.error) {
59
+ throw new Error(`Ocurrió un error al consultar las reglas de reversión de precio: ${response.userMsg}`);
60
+ }
61
+ return response.data ?? [];
62
+ });
63
+ export const createPriceAdjustmentReversalRule = (headers, session, body) => handlePossibleAxiosErrors(async () => {
64
+ const tableId = await getReversalTableId(headers, session);
65
+ return createWebAppTableRegister(headers, tableId, body);
66
+ });
67
+ export const updatePriceAdjustmentReversalRule = (headers, session, id, body) => handlePossibleAxiosErrors(async () => {
68
+ const tableId = await getReversalTableId(headers, session);
69
+ return updateWebAppTableRegister(headers, tableId, Number(id), body);
70
+ });
47
71
  export const normalizeComparableValue = (value) => String(value ?? "").replace(/\s+/g, " ").trim();
48
72
  export const formatReceptionTypeValue = (type) => [type?.Nombre, type?.Codigo]
49
73
  .map((part) => String(part ?? "").trim())
@@ -104,11 +128,15 @@ export const applyPriceAdjustmentRules = (basePrice, rules, ctx) => {
104
128
  let runningTotal = safeBase;
105
129
  const steps = [];
106
130
  const normalized = rules
107
- .map((rule) => ("data" in rule ? rule.data : rule))
108
- .filter((rule) => Boolean(rule))
109
- .filter((rule) => !rule.fechaEliminacion && rule.activa !== false)
110
- .sort((a, b) => (a.prioridad ?? 0) - (b.prioridad ?? 0));
111
- for (const rule of normalized) {
131
+ .map((rule) => ({
132
+ rule: "data" in rule ? rule.data : rule,
133
+ ruleId: "data" in rule ? rule.id : undefined,
134
+ }))
135
+ .filter((entry) => Boolean(entry.rule));
136
+ const activeRules = normalized
137
+ .filter(({ rule }) => !rule.fechaEliminacion && rule.activa !== false)
138
+ .sort((a, b) => (a.rule.prioridad ?? 0) - (b.rule.prioridad ?? 0));
139
+ for (const { rule, ruleId } of activeRules) {
112
140
  if (!priceAdjustmentRuleMatches(rule, ctx)) {
113
141
  continue;
114
142
  }
@@ -125,6 +153,7 @@ export const applyPriceAdjustmentRules = (basePrice, rules, ctx) => {
125
153
  }
126
154
  runningTotal = roundMoney(runningTotal);
127
155
  steps.push({
156
+ ...(ruleId !== undefined ? { ruleId } : {}),
128
157
  ruleName: rule.nombre,
129
158
  operation: rule.operacion,
130
159
  valueType: rule.tipoValor,
@@ -142,3 +171,78 @@ export const applyPriceAdjustmentRules = (basePrice, rules, ctx) => {
142
171
  result.description = formatPriceAdjustmentDescription(result);
143
172
  return result;
144
173
  };
174
+ const sameMoney = (left, right) => Math.abs(roundMoney(left, 2) - roundMoney(right, 2)) < 0.000001;
175
+ export const buildPriceAdjustmentReversalPlan = ({ calculation, currentPrice, reversalRules, history, }) => {
176
+ const base = {
177
+ calculationId: calculation.calculationId,
178
+ detailId: calculation.detailId,
179
+ itemId: calculation.itemId,
180
+ previousPrice: roundMoney(currentPrice, 2),
181
+ finalPrice: roundMoney(currentPrice, 2),
182
+ penaltyAmount: 0,
183
+ matches: [],
184
+ };
185
+ if (!calculation.calculationId || calculation.detailId == null) {
186
+ return {
187
+ ...base,
188
+ status: "ineligible",
189
+ reason: "El cálculo no contiene calculationId y detailId.",
190
+ };
191
+ }
192
+ const activeRules = filterActivePriceAdjustmentReversalRules(reversalRules);
193
+ const reversalBySource = new Map(activeRules.map((entry) => [entry.data.sourceRuleId, entry]));
194
+ const applicable = calculation.steps.flatMap((step) => {
195
+ if (step.ruleId == null ||
196
+ step.operation !== "add" ||
197
+ step.appliedAmount <= 0) {
198
+ return [];
199
+ }
200
+ const reversal = reversalBySource.get(step.ruleId);
201
+ if (!reversal)
202
+ return [];
203
+ return [
204
+ {
205
+ sourceRuleId: step.ruleId,
206
+ sourceRuleName: step.ruleName,
207
+ reversalRuleId: reversal.id,
208
+ reversalRuleName: reversal.data.nombre,
209
+ appliedAmount: roundMoney(step.appliedAmount),
210
+ },
211
+ ];
212
+ });
213
+ if (applicable.length === 0) {
214
+ return {
215
+ ...base,
216
+ status: "ineligible",
217
+ reason: "Ningún paso del cálculo tiene una regla de reversión activa.",
218
+ };
219
+ }
220
+ const calculationHistory = (history ?? []).filter((entry) => entry.calculationId === calculation.calculationId &&
221
+ entry.detailId === calculation.detailId);
222
+ const alreadyAppliedSourceIds = new Set(calculationHistory.map((entry) => entry.sourceRuleId));
223
+ const pending = applicable.filter((match) => !alreadyAppliedSourceIds.has(match.sourceRuleId));
224
+ if (pending.length === 0) {
225
+ return {
226
+ ...base,
227
+ status: "already_applied",
228
+ reason: "Todas las reglas vinculadas ya fueron penalizadas.",
229
+ };
230
+ }
231
+ const expectedCurrentPrice = roundMoney(Number(calculation.finalPrice) +
232
+ calculationHistory.reduce((sum, entry) => sum + entry.amount, 0), 2);
233
+ if (!sameMoney(currentPrice, expectedCurrentPrice)) {
234
+ return {
235
+ ...base,
236
+ status: "price_changed",
237
+ reason: `El precio actual ${roundMoney(currentPrice, 2)} no coincide con el precio esperado ${expectedCurrentPrice}.`,
238
+ };
239
+ }
240
+ const penaltyAmount = roundMoney(-pending.reduce((sum, match) => sum + match.appliedAmount, 0));
241
+ return {
242
+ ...base,
243
+ status: "eligible",
244
+ finalPrice: roundMoney(currentPrice + penaltyAmount, 2),
245
+ penaltyAmount,
246
+ matches: pending,
247
+ };
248
+ };
@@ -1,10 +1,15 @@
1
1
  import type { Session } from "@remix-run/node";
2
- import { AxiosUtilsResponse, PriceAdjustmentContext, PriceAdjustmentFilters, PriceAdjustmentResult, PriceAdjustmentRule, WebAppRowGraphQL, WebAppTableUpdateResponse } from "@zauru-sdk/types";
2
+ import { AxiosUtilsResponse, PriceAdjustmentContext, PriceAdjustmentFilters, PriceAdjustmentResult, PriceAdjustmentRule, PriceAdjustmentReversalPlan, PriceAdjustmentReversalRule, PriceAdjustmentStep, CertificationPenaltyHistoryEntry, WebAppRowGraphQL, WebAppTableUpdateResponse } from "@zauru-sdk/types";
3
+ export declare const PRICE_ADJUSTMENT_REVERSAL_RULES_TABLE_VAR = "price_adjustment_reversal_rules_web_app_table_id";
3
4
  export declare const defaultPriceAdjustmentFilters: () => PriceAdjustmentFilters;
4
5
  export declare const filterActivePriceAdjustmentRules: (rules?: WebAppRowGraphQL<PriceAdjustmentRule>[]) => WebAppRowGraphQL<PriceAdjustmentRule>[];
5
6
  export declare const getPriceAdjustmentRules: (headers: any, session: Session) => Promise<AxiosUtilsResponse<WebAppRowGraphQL<PriceAdjustmentRule>[]>>;
6
7
  export declare const createPriceAdjustmentRule: (headers: any, session: Session, body: PriceAdjustmentRule) => Promise<AxiosUtilsResponse<WebAppTableUpdateResponse>>;
7
8
  export declare const updatePriceAdjustmentRule: (headers: any, session: Session, id: string, body: Partial<PriceAdjustmentRule>) => Promise<AxiosUtilsResponse<WebAppTableUpdateResponse>>;
9
+ export declare const filterActivePriceAdjustmentReversalRules: (rules?: WebAppRowGraphQL<PriceAdjustmentReversalRule>[]) => WebAppRowGraphQL<PriceAdjustmentReversalRule>[];
10
+ export declare const getPriceAdjustmentReversalRules: (headers: any, session: Session) => Promise<AxiosUtilsResponse<WebAppRowGraphQL<PriceAdjustmentReversalRule>[]>>;
11
+ export declare const createPriceAdjustmentReversalRule: (headers: any, session: Session, body: PriceAdjustmentReversalRule) => Promise<AxiosUtilsResponse<WebAppTableUpdateResponse>>;
12
+ export declare const updatePriceAdjustmentReversalRule: (headers: any, session: Session, id: string | number, body: Partial<PriceAdjustmentReversalRule>) => Promise<AxiosUtilsResponse<WebAppTableUpdateResponse>>;
8
13
  export declare const normalizeComparableValue: (value: string | number | undefined | null) => string;
9
14
  export declare const formatReceptionTypeValue: (type?: {
10
15
  Nombre?: string;
@@ -13,3 +18,15 @@ export declare const formatReceptionTypeValue: (type?: {
13
18
  export declare const priceAdjustmentRuleMatches: (rule: PriceAdjustmentRule, ctx: PriceAdjustmentContext) => boolean;
14
19
  export declare const formatPriceAdjustmentDescription: (result: Pick<PriceAdjustmentResult, "basePrice" | "finalPrice" | "steps">, baseLabel?: string) => string;
15
20
  export declare const applyPriceAdjustmentRules: (basePrice: number, rules: Array<PriceAdjustmentRule | WebAppRowGraphQL<PriceAdjustmentRule>>, ctx: PriceAdjustmentContext) => PriceAdjustmentResult;
21
+ export declare const buildPriceAdjustmentReversalPlan: ({ calculation, currentPrice, reversalRules, history, }: {
22
+ calculation: {
23
+ calculationId?: string;
24
+ detailId?: number;
25
+ itemId: number;
26
+ finalPrice: number;
27
+ steps: PriceAdjustmentStep[];
28
+ };
29
+ currentPrice: number;
30
+ reversalRules: WebAppRowGraphQL<PriceAdjustmentReversalRule>[];
31
+ history?: CertificationPenaltyHistoryEntry[];
32
+ }) => PriceAdjustmentReversalPlan;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zauru-sdk/utils",
3
- "version": "6.19.0",
3
+ "version": "6.20.0",
4
4
  "description": "Utilidades, parseo de request y demás para desarrollo de webapps de Zauru",
5
5
  "main": "./dist/esm/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -71,12 +71,12 @@
71
71
  "dependencies": {
72
72
  "@aws-sdk/client-cloudwatch-logs": "^3.1123.0",
73
73
  "@remix-run/node": "^2.8.1",
74
- "@zauru-sdk/common": "^6.8.1",
74
+ "@zauru-sdk/common": "^6.8.3",
75
75
  "@zauru-sdk/config": "^6.0.0",
76
76
  "@zauru-sdk/graphql": "^6.8.0",
77
- "@zauru-sdk/services": "^6.11.4",
78
- "@zauru-sdk/types": "^6.19.0",
77
+ "@zauru-sdk/services": "^6.11.6",
78
+ "@zauru-sdk/types": "^6.21.0",
79
79
  "moment": "^2.30.1"
80
80
  },
81
- "gitHead": "a5b35540efd2b3395e05ea0182c97d78f059c3c4"
81
+ "gitHead": "7d373b9f5c25ab0198e72590c661bc9eb17cf80f"
82
82
  }