hvp-shared 6.47.0 → 6.48.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.
@@ -14,3 +14,4 @@ export * from './job-category.constants';
14
14
  export * from './special-collaborators.constants';
15
15
  export * from './commissions.constants';
16
16
  export * from './hr.enums';
17
+ export * from './qvet-cash-flow.enums';
@@ -30,3 +30,4 @@ __exportStar(require("./job-category.constants"), exports);
30
30
  __exportStar(require("./special-collaborators.constants"), exports);
31
31
  __exportStar(require("./commissions.constants"), exports);
32
32
  __exportStar(require("./hr.enums"), exports);
33
+ __exportStar(require("./qvet-cash-flow.enums"), exports);
@@ -128,8 +128,8 @@ export declare function calculateRecommendedPricePVP(params: {
128
128
  unitPurchasePriceBI: number;
129
129
  vatSalePercent: number;
130
130
  }): number | null;
131
- /** Cost-based multiplier for floor price (PHARM, PHARM_FRAC, COST_ML, COST) */
132
- export declare const MIN_PRICE_COST_FACTOR = 0.25;
131
+ /** Cost-based multiplier for floor price (PHARM, PHARM_FRAC, COST_ML, COST) — cost + 25% margin */
132
+ export declare const MIN_PRICE_COST_FACTOR = 1.25;
133
133
  /** Cost-based multiplier for floor price (EXT_COST) */
134
134
  export declare const MIN_PRICE_EXT_COST_FACTOR = 1;
135
135
  /** Fraction of salePricePVP for recommended minimum price (COMP_SVC) */
@@ -218,9 +218,11 @@ function getDefaultPricePolicy(section, saleUnit, conversionFactor) {
218
218
  // =============================================================================
219
219
  // ROUNDING HELPER
220
220
  // =============================================================================
221
- /** Round to nearest multiple of 5 */
221
+ /** Round to nearest multiple of 5 (minimum 5 for positive values) */
222
222
  function roundToNearest5(value) {
223
- return Math.round(value / 5) * 5;
223
+ if (value <= 0)
224
+ return 0;
225
+ return Math.max(5, Math.round(value / 5) * 5);
224
226
  }
225
227
  /**
226
228
  * Calculate the recommended price PVP (con IVA) for a catalog item.
@@ -243,8 +245,8 @@ function calculateRecommendedPricePVP(params) {
243
245
  // =============================================================================
244
246
  // FLOOR PRICE CALCULATION
245
247
  // =============================================================================
246
- /** Cost-based multiplier for floor price (PHARM, PHARM_FRAC, COST_ML, COST) */
247
- exports.MIN_PRICE_COST_FACTOR = 0.25;
248
+ /** Cost-based multiplier for floor price (PHARM, PHARM_FRAC, COST_ML, COST) — cost + 25% margin */
249
+ exports.MIN_PRICE_COST_FACTOR = 1.25;
248
250
  /** Cost-based multiplier for floor price (EXT_COST) */
249
251
  exports.MIN_PRICE_EXT_COST_FACTOR = 1.0;
250
252
  /** Fraction of salePricePVP for recommended minimum price (COMP_SVC) */
@@ -319,17 +319,17 @@ describe('calculateFloorPrices', () => {
319
319
  const defaults = { unitPurchasePriceBI: 100, vatSalePercent: 16, salePricePVP: 500 };
320
320
  describe('cost-based policies (PHARM, PHARM_FRAC, COST_ML, COST)', () => {
321
321
  const costPolicies = [pricing_constants_1.PricePolicy.PHARM, pricing_constants_1.PricePolicy.PHARM_FRAC, pricing_constants_1.PricePolicy.COST_ML, pricing_constants_1.PricePolicy.COST];
322
- it.each(costPolicies)('%s: both = cost × 0.25 × (1+VAT/100), round5', (policy) => {
323
- // $100 * 0.25 * 1.16 = $29 → round5 = $30
322
+ it.each(costPolicies)('%s: both = cost × 1.25 × (1+VAT/100), round5', (policy) => {
323
+ // $100 * 1.25 * 1.16 = $145 → round5 = $145
324
324
  const result = (0, pricing_constants_1.calculateFloorPrices)({ ...defaults, pricePolicy: policy });
325
- expect(result.recommendedMinimumPrice).toBe(30);
326
- expect(result.floorPrice).toBe(30);
325
+ expect(result.recommendedMinimumPrice).toBe(145);
326
+ expect(result.floorPrice).toBe(145);
327
327
  });
328
328
  it('should round to nearest 5', () => {
329
- // $80 * 0.25 * 1.16 = $23.20 → round5 = $25
329
+ // $80 * 1.25 * 1.16 = $116 → round5 = $115
330
330
  const result = (0, pricing_constants_1.calculateFloorPrices)({ ...defaults, pricePolicy: pricing_constants_1.PricePolicy.PHARM, unitPurchasePriceBI: 80 });
331
- expect(result.recommendedMinimumPrice).toBe(25);
332
- expect(result.floorPrice).toBe(25);
331
+ expect(result.recommendedMinimumPrice).toBe(115);
332
+ expect(result.floorPrice).toBe(115);
333
333
  });
334
334
  it('should return null when cost is 0', () => {
335
335
  const result = (0, pricing_constants_1.calculateFloorPrices)({ ...defaults, pricePolicy: pricing_constants_1.PricePolicy.PHARM, unitPurchasePriceBI: 0 });
@@ -342,14 +342,20 @@ describe('calculateFloorPrices', () => {
342
342
  expect(result.floorPrice).toBeNull();
343
343
  });
344
344
  it('should handle 0% VAT', () => {
345
- // $100 * 0.25 * 1.0 = $25 → $25
345
+ // $100 * 1.25 * 1.0 = $125 → $125
346
346
  const result = (0, pricing_constants_1.calculateFloorPrices)({ ...defaults, pricePolicy: pricing_constants_1.PricePolicy.COST, vatSalePercent: 0 });
347
- expect(result.recommendedMinimumPrice).toBe(25);
348
- expect(result.floorPrice).toBe(25);
347
+ expect(result.recommendedMinimumPrice).toBe(125);
348
+ expect(result.floorPrice).toBe(125);
349
+ });
350
+ it('should never return less than 5 for positive cost', () => {
351
+ // $1 * 1.25 * 1.0 = $1.25 → round5 = 0, but min is 5
352
+ const result = (0, pricing_constants_1.calculateFloorPrices)({ ...defaults, pricePolicy: pricing_constants_1.PricePolicy.PHARM, unitPurchasePriceBI: 1, vatSalePercent: 0 });
353
+ expect(result.recommendedMinimumPrice).toBe(5);
354
+ expect(result.floorPrice).toBe(5);
349
355
  });
350
356
  });
351
357
  describe('EXT_COST', () => {
352
- it('both = cost × 1.0 × (1+VAT/100), round5', () => {
358
+ it('both = cost × 1.0 × (1+VAT/100), round5 (at-cost recovery)', () => {
353
359
  // $100 * 1.0 * 1.16 = $116 → round5 = $115
354
360
  const result = (0, pricing_constants_1.calculateFloorPrices)({ ...defaults, pricePolicy: pricing_constants_1.PricePolicy.EXT_COST });
355
361
  expect(result.recommendedMinimumPrice).toBe(115);
@@ -366,6 +372,12 @@ describe('calculateFloorPrices', () => {
366
372
  expect(result.recommendedMinimumPrice).toBe(230);
367
373
  expect(result.floorPrice).toBe(230);
368
374
  });
375
+ it('should never return less than 5 for positive cost', () => {
376
+ // $2 * 1.0 * 1.0 = $2 → round5 = 0, but min is 5
377
+ const result = (0, pricing_constants_1.calculateFloorPrices)({ ...defaults, pricePolicy: pricing_constants_1.PricePolicy.EXT_COST, unitPurchasePriceBI: 2, vatSalePercent: 0 });
378
+ expect(result.recommendedMinimumPrice).toBe(5);
379
+ expect(result.floorPrice).toBe(5);
380
+ });
369
381
  });
370
382
  describe('COMP_SVC', () => {
371
383
  it('recommendedMinimumPrice = salePricePVP × 0.50, floorPrice = 0', () => {
@@ -0,0 +1,45 @@
1
+ /**
2
+ * QVET Cash Flow Review Enums
3
+ *
4
+ * Used in the cash flow review workflow where collaborators and admins
5
+ * annotate QVET cash transactions (entradas/salidas de caja) with
6
+ * supplier, account type, evidence, and invoice status.
7
+ *
8
+ * DB values use QVET abbreviations (CE, SE, FA, etc.) for historical
9
+ * compatibility. Enum keys use descriptive English names.
10
+ */
11
+ /** Whether the transaction goes through formal or informal accounting */
12
+ export declare enum CashFlowAccountType {
13
+ FORMAL = "FORMAL",
14
+ INFORMAL = "INFORMAL"
15
+ }
16
+ /** Evidence status for the transaction receipt/voucher */
17
+ export declare enum CashFlowEvidence {
18
+ /** Comprobante guardado (receipt saved in Dropbox) */
19
+ SAVED = "CE",
20
+ /** Se extravió (receipt existed but was lost/not captured) */
21
+ MISSING = "SE",
22
+ /** Sin comprobantes (no receipt for this type of payment) */
23
+ NO_RECEIPT = "SC"
24
+ }
25
+ /** Invoice status for formal transactions */
26
+ export declare enum CashFlowInvoiceStatus {
27
+ /** Factura recibida */
28
+ INVOICED = "FA",
29
+ /** Proveedor factura automáticamente */
30
+ AUTO_INVOICED = "FA_AUTO",
31
+ /** Pendiente de factura */
32
+ PENDING = "PE",
33
+ /** No facturable (informales, etc.) */
34
+ NOT_APPLICABLE = "SF"
35
+ }
36
+ /** Validation result for a cash flow entry */
37
+ export declare enum CashFlowValidationStatus {
38
+ VALID = "valid",
39
+ INVALID = "invalid"
40
+ }
41
+ /**
42
+ * Invoice statuses allowed when accountType is FORMAL.
43
+ * NOT_APPLICABLE is only valid for INFORMAL accounts.
44
+ */
45
+ export declare const FORMAL_ALLOWED_INVOICE_STATUSES: readonly [CashFlowInvoiceStatus.INVOICED, CashFlowInvoiceStatus.AUTO_INVOICED, CashFlowInvoiceStatus.PENDING];
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ /**
3
+ * QVET Cash Flow Review Enums
4
+ *
5
+ * Used in the cash flow review workflow where collaborators and admins
6
+ * annotate QVET cash transactions (entradas/salidas de caja) with
7
+ * supplier, account type, evidence, and invoice status.
8
+ *
9
+ * DB values use QVET abbreviations (CE, SE, FA, etc.) for historical
10
+ * compatibility. Enum keys use descriptive English names.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.FORMAL_ALLOWED_INVOICE_STATUSES = exports.CashFlowValidationStatus = exports.CashFlowInvoiceStatus = exports.CashFlowEvidence = exports.CashFlowAccountType = void 0;
14
+ /** Whether the transaction goes through formal or informal accounting */
15
+ var CashFlowAccountType;
16
+ (function (CashFlowAccountType) {
17
+ CashFlowAccountType["FORMAL"] = "FORMAL";
18
+ CashFlowAccountType["INFORMAL"] = "INFORMAL";
19
+ })(CashFlowAccountType || (exports.CashFlowAccountType = CashFlowAccountType = {}));
20
+ /** Evidence status for the transaction receipt/voucher */
21
+ var CashFlowEvidence;
22
+ (function (CashFlowEvidence) {
23
+ /** Comprobante guardado (receipt saved in Dropbox) */
24
+ CashFlowEvidence["SAVED"] = "CE";
25
+ /** Se extravió (receipt existed but was lost/not captured) */
26
+ CashFlowEvidence["MISSING"] = "SE";
27
+ /** Sin comprobantes (no receipt for this type of payment) */
28
+ CashFlowEvidence["NO_RECEIPT"] = "SC";
29
+ })(CashFlowEvidence || (exports.CashFlowEvidence = CashFlowEvidence = {}));
30
+ /** Invoice status for formal transactions */
31
+ var CashFlowInvoiceStatus;
32
+ (function (CashFlowInvoiceStatus) {
33
+ /** Factura recibida */
34
+ CashFlowInvoiceStatus["INVOICED"] = "FA";
35
+ /** Proveedor factura automáticamente */
36
+ CashFlowInvoiceStatus["AUTO_INVOICED"] = "FA_AUTO";
37
+ /** Pendiente de factura */
38
+ CashFlowInvoiceStatus["PENDING"] = "PE";
39
+ /** No facturable (informales, etc.) */
40
+ CashFlowInvoiceStatus["NOT_APPLICABLE"] = "SF";
41
+ })(CashFlowInvoiceStatus || (exports.CashFlowInvoiceStatus = CashFlowInvoiceStatus = {}));
42
+ /** Validation result for a cash flow entry */
43
+ var CashFlowValidationStatus;
44
+ (function (CashFlowValidationStatus) {
45
+ CashFlowValidationStatus["VALID"] = "valid";
46
+ CashFlowValidationStatus["INVALID"] = "invalid";
47
+ })(CashFlowValidationStatus || (exports.CashFlowValidationStatus = CashFlowValidationStatus = {}));
48
+ /**
49
+ * Invoice statuses allowed when accountType is FORMAL.
50
+ * NOT_APPLICABLE is only valid for INFORMAL accounts.
51
+ */
52
+ exports.FORMAL_ALLOWED_INVOICE_STATUSES = [
53
+ CashFlowInvoiceStatus.INVOICED,
54
+ CashFlowInvoiceStatus.AUTO_INVOICED,
55
+ CashFlowInvoiceStatus.PENDING,
56
+ ];
@@ -23,6 +23,7 @@
23
23
  * }
24
24
  * ```
25
25
  */
26
+ import { CashFlowAccountType, CashFlowEvidence, CashFlowInvoiceStatus, CashFlowValidationStatus } from '../../constants/qvet-cash-flow.enums';
26
27
  /**
27
28
  * @deprecated No longer used. Backend uses standard ApiListSuccessResponse with meta.
28
29
  * Kept for backward compatibility during migration.
@@ -613,6 +614,50 @@ export interface QvetCashClosingsQueryParams {
613
614
  skip?: number;
614
615
  sort?: string;
615
616
  }
617
+ /**
618
+ * Review data added by collaborators/admins to a QVET cash flow entry.
619
+ *
620
+ * QVET-sourced fields (branch, amount, etc.) remain immutable.
621
+ * All editable data lives in this subdocument.
622
+ *
623
+ * @example PATCH /api/qvet/cash-flows/:id/review
624
+ */
625
+ export interface CashFlowReview {
626
+ /** Proveedor (may differ from QVET's supplier field) */
627
+ supplier?: string;
628
+ /** Tipo de cuenta assigned by collaborator */
629
+ accountType?: CashFlowAccountType;
630
+ /** Receipt/voucher evidence status */
631
+ evidence?: CashFlowEvidence;
632
+ /** Invoice status (FORMAL accounts cannot be NOT_APPLICABLE) */
633
+ invoiceStatus?: CashFlowInvoiceStatus;
634
+ /** Auto-calculated or admin-overridden validation result */
635
+ validationStatus?: CashFlowValidationStatus;
636
+ /** Tipo de cuenta overridden by admin (used for accounting export) */
637
+ adminAccountType?: CashFlowAccountType;
638
+ /** Free-text observations */
639
+ observations?: string;
640
+ /** Collaborator ID who last updated */
641
+ lastUpdatedBy?: string;
642
+ /** ISO 8601 timestamp of last update */
643
+ lastUpdatedAt?: string;
644
+ }
645
+ /**
646
+ * Request body for updating a cash flow review.
647
+ *
648
+ * @example PATCH /api/qvet/cash-flows/:id/review
649
+ */
650
+ export interface UpdateCashFlowReviewRequest {
651
+ supplier?: string;
652
+ accountType?: CashFlowAccountType;
653
+ evidence?: CashFlowEvidence;
654
+ invoiceStatus?: CashFlowInvoiceStatus;
655
+ /** Only admin can override */
656
+ validationStatus?: CashFlowValidationStatus;
657
+ /** Only admin can set */
658
+ adminAccountType?: CashFlowAccountType;
659
+ observations?: string;
660
+ }
616
661
  /**
617
662
  * Single cash flow record from QVET (Entradas/Salidas de Caja)
618
663
  * Represents cash in/out transactions outside of regular sales
@@ -634,6 +679,7 @@ export interface QvetCashFlowResponse {
634
679
  supplier: string;
635
680
  staffName: string;
636
681
  closingDate: string;
682
+ review?: CashFlowReview;
637
683
  syncedAt: string;
638
684
  }
639
685
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hvp-shared",
3
- "version": "6.47.0",
3
+ "version": "6.48.0",
4
4
  "description": "Shared types and utilities for HVP backend and frontend",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",