@open-mercato/core 0.6.1-develop.3174.1.c3822bcdf0 → 0.6.1-develop.3187.1.4c96d17341

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.
@@ -66,6 +66,7 @@ import {
66
66
  CustomerPersonProfile,
67
67
  } from "../../customers/data/entities";
68
68
  import {
69
+ enforceReturnAdjustmentSign,
69
70
  quoteCreateSchema,
70
71
  quoteLineCreateSchema,
71
72
  quoteAdjustmentCreateSchema,
@@ -6299,18 +6300,22 @@ const quoteLineDeleteSchema = z.object({
6299
6300
  quoteId: z.string().uuid(),
6300
6301
  });
6301
6302
 
6302
- const orderAdjustmentUpsertSchema = orderAdjustmentCreateSchema.extend({
6303
- id: z.string().uuid().optional(),
6304
- });
6303
+ const orderAdjustmentUpsertSchema = orderAdjustmentCreateSchema
6304
+ .extend({
6305
+ id: z.string().uuid().optional(),
6306
+ })
6307
+ .superRefine(enforceReturnAdjustmentSign);
6305
6308
 
6306
6309
  const orderAdjustmentDeleteSchema = z.object({
6307
6310
  id: z.string().uuid(),
6308
6311
  orderId: z.string().uuid(),
6309
6312
  });
6310
6313
 
6311
- const quoteAdjustmentUpsertSchema = quoteAdjustmentCreateSchema.extend({
6312
- id: z.string().uuid().optional(),
6313
- });
6314
+ const quoteAdjustmentUpsertSchema = quoteAdjustmentCreateSchema
6315
+ .extend({
6316
+ id: z.string().uuid().optional(),
6317
+ })
6318
+ .superRefine(enforceReturnAdjustmentSign);
6314
6319
 
6315
6320
  const quoteAdjustmentDeleteSchema = z.object({
6316
6321
  id: z.string().uuid(),
@@ -419,6 +419,41 @@ export const quoteLineUpdateSchema = z
419
419
  })
420
420
  .merge(quoteLineCreateSchema.partial())
421
421
 
422
+ // A `return` adjustment must never increase the grand total. The calculation
423
+ // engine is defensively normalized to negative, but the API edge also rejects
424
+ // positive amounts so bad input is caught before it reaches storage. See #1705.
425
+ export const RETURN_ADJUSTMENT_POSITIVE_NET_MESSAGE =
426
+ 'Return adjustments must use a non-positive amountNet (returns reduce the total).'
427
+ export const RETURN_ADJUSTMENT_POSITIVE_GROSS_MESSAGE =
428
+ 'Return adjustments must use a non-positive amountGross (returns reduce the total).'
429
+
430
+ type AdjustmentSignInput = {
431
+ kind?: string
432
+ amountNet?: number
433
+ amountGross?: number
434
+ }
435
+
436
+ export const enforceReturnAdjustmentSign = (
437
+ value: AdjustmentSignInput,
438
+ ctx: z.RefinementCtx
439
+ ) => {
440
+ if (value.kind !== 'return') return
441
+ if (typeof value.amountNet === 'number' && value.amountNet > 0) {
442
+ ctx.addIssue({
443
+ code: z.ZodIssueCode.custom,
444
+ message: RETURN_ADJUSTMENT_POSITIVE_NET_MESSAGE,
445
+ path: ['amountNet'],
446
+ })
447
+ }
448
+ if (typeof value.amountGross === 'number' && value.amountGross > 0) {
449
+ ctx.addIssue({
450
+ code: z.ZodIssueCode.custom,
451
+ message: RETURN_ADJUSTMENT_POSITIVE_GROSS_MESSAGE,
452
+ path: ['amountGross'],
453
+ })
454
+ }
455
+ }
456
+
422
457
  export const orderAdjustmentCreateSchema = scoped.extend({
423
458
  orderId: uuid(),
424
459
  orderLineId: uuid().optional(),
@@ -190,13 +190,17 @@ function buildBaseDocumentResult(params: {
190
190
  }
191
191
  }
192
192
 
193
- // Line-scoped and any other return (credit) adjustments reduce grand total
193
+ // Line-scoped and any other return (credit) adjustments reduce grand total.
194
+ // Sign is normalized to negative regardless of the stored sign so a positive
195
+ // amountNet / amountGross can never inflate totals (issue #1705).
194
196
  for (const adj of resolvedAdjustments) {
195
197
  if (adj.kind !== 'return') continue
196
198
  const net = toNumber(adj.amountNet, toNumber(adj.amountGross))
197
199
  const gross = toNumber(adj.amountGross, net)
198
- subtotalNet = Math.max(subtotalNet + net, 0)
199
- subtotalGross = Math.max(subtotalGross + gross, 0)
200
+ const netDelta = -Math.abs(net)
201
+ const grossDelta = -Math.abs(gross)
202
+ subtotalNet = Math.max(subtotalNet + netDelta, 0)
203
+ subtotalGross = Math.max(subtotalGross + grossDelta, 0)
200
204
  }
201
205
 
202
206
  const grandTotalNet = round(subtotalNet)