@zauru-sdk/common 6.11.0 → 6.11.1

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,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkProviderRestrictionValue = exports.validateProviderRestrictionInput = exports.matchReceptionTypeCode = exports.isRestrictionValueAllowed = exports.resolveAllowedRestrictionValues = exports.filterActiveProviderRestrictions = exports.isRestrictionActiveOnDate = exports.restrictionHasDateRange = exports.normalizeProviderRestrictionValues = exports.isProviderRestrictionMode = exports.formatRestrictionTypeValue = exports.normalizeRestrictionValue = exports.PROVIDER_ITEM_RESTRICTIONS_TABLE_VAR = exports.PROVIDER_TYPE_RESTRICTIONS_TABLE_VAR = void 0;
3
+ exports.checkProviderRestrictionValue = exports.validateProviderRestrictionInput = exports.restrictionLeavesNoAllowedValues = exports.sampleDatesForRestrictionRules = exports.matchReceptionTypeCode = exports.isRestrictionValueAllowed = exports.resolveAllowedRestrictionValues = exports.filterActiveProviderRestrictions = exports.isRestrictionActiveOnDate = exports.restrictionHasDateRange = exports.normalizeProviderRestrictionValues = exports.isProviderRestrictionMode = exports.formatRestrictionTypeValue = exports.normalizeRestrictionValue = exports.PROVIDER_ITEM_RESTRICTIONS_TABLE_VAR = exports.PROVIDER_TYPE_RESTRICTIONS_TABLE_VAR = void 0;
4
4
  const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
5
5
  exports.PROVIDER_TYPE_RESTRICTIONS_TABLE_VAR = "ctl_calidad_admin_restricciones_tipos_proveedor_web_app_table_id";
6
6
  exports.PROVIDER_ITEM_RESTRICTIONS_TABLE_VAR = "ctl_calidad_admin_restricciones_verduras_proveedor_web_app_table_id";
@@ -119,7 +119,47 @@ const matchReceptionTypeCode = (formValue, types) => {
119
119
  return wanted;
120
120
  };
121
121
  exports.matchReceptionTypeCode = matchReceptionTypeCode;
122
- const validateProviderRestrictionInput = (input) => {
122
+ const shiftYmd = (ymd, days) => {
123
+ const [year, month, day] = ymd.split("-").map(Number);
124
+ const utc = new Date(Date.UTC(year, month - 1, day + days));
125
+ const nextYear = utc.getUTCFullYear();
126
+ const nextMonth = String(utc.getUTCMonth() + 1).padStart(2, "0");
127
+ const nextDay = String(utc.getUTCDate()).padStart(2, "0");
128
+ return `${nextYear}-${nextMonth}-${nextDay}`;
129
+ };
130
+ const sampleDatesForRestrictionRules = (rules) => {
131
+ const dates = new Set(["1970-01-01", "2999-12-31"]);
132
+ for (const rule of rules) {
133
+ const start = (0, exports.normalizeRestrictionValue)(rule.start_date);
134
+ const end = (0, exports.normalizeRestrictionValue)(rule.end_date);
135
+ if (DATE_RE.test(start)) {
136
+ dates.add(start);
137
+ dates.add(shiftYmd(start, -1));
138
+ }
139
+ if (DATE_RE.test(end)) {
140
+ dates.add(end);
141
+ dates.add(shiftYmd(end, 1));
142
+ }
143
+ }
144
+ return [...dates].sort();
145
+ };
146
+ exports.sampleDatesForRestrictionRules = sampleDatesForRestrictionRules;
147
+ const restrictionLeavesNoAllowedValues = (input) => {
148
+ const universe = input.universe
149
+ .map(exports.normalizeRestrictionValue)
150
+ .filter(Boolean);
151
+ if (universe.length === 0) {
152
+ return false;
153
+ }
154
+ return (0, exports.sampleDatesForRestrictionRules)(input.rules ?? []).some((date) => (0, exports.resolveAllowedRestrictionValues)({
155
+ payeeId: input.payeeId,
156
+ date,
157
+ rules: input.rules,
158
+ universe,
159
+ }).length === 0);
160
+ };
161
+ exports.restrictionLeavesNoAllowedValues = restrictionLeavesNoAllowedValues;
162
+ const validateProviderRestrictionInput = (input, options) => {
123
163
  const errors = [];
124
164
  if (!Number(input.payee_id) || Number(input.payee_id) < 1) {
125
165
  errors.push({ field: "payee_id", message: "Seleccione un proveedor." });
@@ -166,6 +206,35 @@ const validateProviderRestrictionInput = (input) => {
166
206
  });
167
207
  }
168
208
  }
209
+ const universe = (options?.universe ?? [])
210
+ .map(exports.normalizeRestrictionValue)
211
+ .filter(Boolean);
212
+ if (universe.length > 0 &&
213
+ Number(input.payee_id) > 0 &&
214
+ (0, exports.isProviderRestrictionMode)(input.mode) &&
215
+ (0, exports.normalizeProviderRestrictionValues)(input.values).length > 0) {
216
+ const proposed = {
217
+ payee_id: Number(input.payee_id),
218
+ mode: input.mode,
219
+ values: (0, exports.normalizeProviderRestrictionValues)(input.values),
220
+ start_date: start,
221
+ end_date: end,
222
+ motivo: (0, exports.normalizeRestrictionValue)(input.motivo),
223
+ };
224
+ const otherRules = (options?.otherRules ?? []).filter((rule) => Number(rule.payee_id) === Number(input.payee_id) &&
225
+ !(0, exports.normalizeRestrictionValue)(rule.fechaEliminacion));
226
+ if ((0, exports.restrictionLeavesNoAllowedValues)({
227
+ payeeId: Number(input.payee_id),
228
+ rules: [...otherRules, proposed],
229
+ universe,
230
+ })) {
231
+ const noun = (0, exports.normalizeRestrictionValue)(options?.valueNoun) || "opciones";
232
+ errors.push({
233
+ field: "values",
234
+ message: `Esta regla dejaría al proveedor sin ${noun} para escoger. Debe quedar al menos uno.`,
235
+ });
236
+ }
237
+ }
169
238
  return errors;
170
239
  };
171
240
  exports.validateProviderRestrictionInput = validateProviderRestrictionInput;
@@ -39,7 +39,18 @@ export type ProviderRestrictionInputError = {
39
39
  field: string;
40
40
  message: string;
41
41
  };
42
- export declare const validateProviderRestrictionInput: (input: ProviderRestrictionInput) => ProviderRestrictionInputError[];
42
+ export type ValidateProviderRestrictionOptions = {
43
+ universe?: Array<string | number>;
44
+ otherRules?: ProviderRestrictionRule[];
45
+ valueNoun?: string;
46
+ };
47
+ export declare const sampleDatesForRestrictionRules: (rules: Array<Pick<ProviderRestrictionRule, "start_date" | "end_date">>) => string[];
48
+ export declare const restrictionLeavesNoAllowedValues: (input: {
49
+ payeeId: number;
50
+ rules: ProviderRestrictionRule[] | undefined;
51
+ universe: Array<string | number>;
52
+ }) => boolean;
53
+ export declare const validateProviderRestrictionInput: (input: ProviderRestrictionInput, options?: ValidateProviderRestrictionOptions) => ProviderRestrictionInputError[];
43
54
  export declare const checkProviderRestrictionValue: (input: {
44
55
  payeeId: number;
45
56
  date: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zauru-sdk/common",
3
- "version": "6.11.0",
3
+ "version": "6.11.1",
4
4
  "description": "Funciones de uso común en el desarrollo de webapps en Zauru",
5
5
  "main": "./dist/esm/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -26,5 +26,5 @@
26
26
  "moment": "^2.30.1",
27
27
  "moment-timezone": "^0.5.45"
28
28
  },
29
- "gitHead": "d8141f1d30edee6edb8d2777d0e12e2f4f1b2726"
29
+ "gitHead": "04f79cf77f767191f4be6358d9b23a3a0f1641b0"
30
30
  }
@@ -8,6 +8,7 @@ import {
8
8
  isRestrictionValueAllowed,
9
9
  matchReceptionTypeCode,
10
10
  resolveAllowedRestrictionValues,
11
+ restrictionLeavesNoAllowedValues,
11
12
  validateProviderRestrictionInput,
12
13
  } from "./providerRestrictions.js";
13
14
 
@@ -274,4 +275,86 @@ describe("validateProviderRestrictionInput", () => {
274
275
  true,
275
276
  );
276
277
  });
278
+
279
+ it("When exclude removes every value in the universe, it should reject it", () => {
280
+ const errors = validateProviderRestrictionInput(
281
+ {
282
+ payee_id: 1,
283
+ mode: "exclude",
284
+ values: ["N", "1"],
285
+ },
286
+ { universe: ["N", "1"], valueNoun: "tipos de recepción" },
287
+ );
288
+ assert.equal(
289
+ errors.some((error) => error.message.includes("sin tipos de recepción")),
290
+ true,
291
+ );
292
+ });
293
+
294
+ it("When exclude leaves at least one value, it should allow it", () => {
295
+ const errors = validateProviderRestrictionInput(
296
+ {
297
+ payee_id: 1,
298
+ mode: "exclude",
299
+ values: ["1"],
300
+ },
301
+ { universe: ["N", "1"] },
302
+ );
303
+ assert.deepEqual(errors, []);
304
+ });
305
+
306
+ it("When an overlapping exclude would empty an include, it should reject it", () => {
307
+ const errors = validateProviderRestrictionInput(
308
+ {
309
+ payee_id: 10,
310
+ mode: "exclude",
311
+ values: ["Normal"],
312
+ start_date: "2026-10-27",
313
+ end_date: "2026-11-02",
314
+ motivo: "Auditoría",
315
+ },
316
+ {
317
+ universe: ["Normal", "Certificado 1"],
318
+ otherRules: [
319
+ rule({
320
+ payee_id: 10,
321
+ mode: "include",
322
+ values: ["Normal"],
323
+ }),
324
+ ],
325
+ },
326
+ );
327
+ assert.equal(
328
+ errors.some((error) => error.field === "values"),
329
+ true,
330
+ );
331
+ });
332
+ });
333
+
334
+ describe("restrictionLeavesNoAllowedValues", () => {
335
+ it("When two dated excludes only empty the overlap, it should detect that day", () => {
336
+ assert.equal(
337
+ restrictionLeavesNoAllowedValues({
338
+ payeeId: 3,
339
+ universe: ["A", "B"],
340
+ rules: [
341
+ rule({
342
+ payee_id: 3,
343
+ mode: "exclude",
344
+ values: ["A"],
345
+ start_date: "2026-10-01",
346
+ end_date: "2026-10-20",
347
+ }),
348
+ rule({
349
+ payee_id: 3,
350
+ mode: "exclude",
351
+ values: ["B"],
352
+ start_date: "2026-10-10",
353
+ end_date: "2026-10-15",
354
+ }),
355
+ ],
356
+ }),
357
+ true,
358
+ );
359
+ });
277
360
  });
@@ -196,8 +196,65 @@ export type ProviderRestrictionInputError = {
196
196
  message: string;
197
197
  };
198
198
 
199
+ export type ValidateProviderRestrictionOptions = {
200
+ universe?: Array<string | number>;
201
+ otherRules?: ProviderRestrictionRule[];
202
+ valueNoun?: string;
203
+ };
204
+
205
+ const shiftYmd = (ymd: string, days: number): string => {
206
+ const [year, month, day] = ymd.split("-").map(Number);
207
+ const utc = new Date(Date.UTC(year, month - 1, day + days));
208
+ const nextYear = utc.getUTCFullYear();
209
+ const nextMonth = String(utc.getUTCMonth() + 1).padStart(2, "0");
210
+ const nextDay = String(utc.getUTCDate()).padStart(2, "0");
211
+ return `${nextYear}-${nextMonth}-${nextDay}`;
212
+ };
213
+
214
+ export const sampleDatesForRestrictionRules = (
215
+ rules: Array<Pick<ProviderRestrictionRule, "start_date" | "end_date">>,
216
+ ): string[] => {
217
+ const dates = new Set<string>(["1970-01-01", "2999-12-31"]);
218
+ for (const rule of rules) {
219
+ const start = normalizeRestrictionValue(rule.start_date);
220
+ const end = normalizeRestrictionValue(rule.end_date);
221
+ if (DATE_RE.test(start)) {
222
+ dates.add(start);
223
+ dates.add(shiftYmd(start, -1));
224
+ }
225
+ if (DATE_RE.test(end)) {
226
+ dates.add(end);
227
+ dates.add(shiftYmd(end, 1));
228
+ }
229
+ }
230
+ return [...dates].sort();
231
+ };
232
+
233
+ export const restrictionLeavesNoAllowedValues = (input: {
234
+ payeeId: number;
235
+ rules: ProviderRestrictionRule[] | undefined;
236
+ universe: Array<string | number>;
237
+ }): boolean => {
238
+ const universe = input.universe
239
+ .map(normalizeRestrictionValue)
240
+ .filter(Boolean);
241
+ if (universe.length === 0) {
242
+ return false;
243
+ }
244
+ return sampleDatesForRestrictionRules(input.rules ?? []).some(
245
+ (date) =>
246
+ resolveAllowedRestrictionValues({
247
+ payeeId: input.payeeId,
248
+ date,
249
+ rules: input.rules,
250
+ universe,
251
+ }).length === 0,
252
+ );
253
+ };
254
+
199
255
  export const validateProviderRestrictionInput = (
200
256
  input: ProviderRestrictionInput,
257
+ options?: ValidateProviderRestrictionOptions,
201
258
  ): ProviderRestrictionInputError[] => {
202
259
  const errors: ProviderRestrictionInputError[] = [];
203
260
  if (!Number(input.payee_id) || Number(input.payee_id) < 1) {
@@ -245,6 +302,44 @@ export const validateProviderRestrictionInput = (
245
302
  }
246
303
  }
247
304
 
305
+ const universe = (options?.universe ?? [])
306
+ .map(normalizeRestrictionValue)
307
+ .filter(Boolean);
308
+ if (
309
+ universe.length > 0 &&
310
+ Number(input.payee_id) > 0 &&
311
+ isProviderRestrictionMode(input.mode) &&
312
+ normalizeProviderRestrictionValues(input.values).length > 0
313
+ ) {
314
+ const proposed: ProviderRestrictionRule = {
315
+ payee_id: Number(input.payee_id),
316
+ mode: input.mode,
317
+ values: normalizeProviderRestrictionValues(input.values),
318
+ start_date: start,
319
+ end_date: end,
320
+ motivo: normalizeRestrictionValue(input.motivo),
321
+ };
322
+ const otherRules = (options?.otherRules ?? []).filter(
323
+ (rule) =>
324
+ Number(rule.payee_id) === Number(input.payee_id) &&
325
+ !normalizeRestrictionValue(rule.fechaEliminacion),
326
+ );
327
+ if (
328
+ restrictionLeavesNoAllowedValues({
329
+ payeeId: Number(input.payee_id),
330
+ rules: [...otherRules, proposed],
331
+ universe,
332
+ })
333
+ ) {
334
+ const noun =
335
+ normalizeRestrictionValue(options?.valueNoun) || "opciones";
336
+ errors.push({
337
+ field: "values",
338
+ message: `Esta regla dejaría al proveedor sin ${noun} para escoger. Debe quedar al menos uno.`,
339
+ });
340
+ }
341
+ }
342
+
248
343
  return errors;
249
344
  };
250
345