@open-mercato/core 0.6.6-develop.6309.1.983aeec27a → 0.6.6-develop.6314.1.c7b8291aa2

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.
Files changed (30) hide show
  1. package/dist/helpers/integration/salesFixtures.js +10 -0
  2. package/dist/helpers/integration/salesFixtures.js.map +2 -2
  3. package/dist/modules/customers/components/DictionarySettings.js +92 -37
  4. package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
  5. package/dist/modules/customers/components/formConfig.js +7 -1
  6. package/dist/modules/customers/components/formConfig.js.map +2 -2
  7. package/dist/modules/customers/lib/dictionaries.js +10 -0
  8. package/dist/modules/customers/lib/dictionaries.js.map +2 -2
  9. package/dist/modules/sales/commands/returns.js +38 -3
  10. package/dist/modules/sales/commands/returns.js.map +2 -2
  11. package/dist/modules/sales/components/documents/ReturnDialog.js.map +2 -2
  12. package/dist/modules/sales/components/documents/ReturnsSection.js +18 -5
  13. package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
  14. package/dist/modules/sales/lib/returnQuantity.js +29 -2
  15. package/dist/modules/sales/lib/returnQuantity.js.map +2 -2
  16. package/dist/modules/workflows/lib/event-trigger-service.js.map +2 -2
  17. package/package.json +7 -7
  18. package/src/helpers/integration/salesFixtures.ts +21 -0
  19. package/src/modules/customers/components/DictionarySettings.tsx +60 -2
  20. package/src/modules/customers/components/formConfig.tsx +8 -2
  21. package/src/modules/customers/lib/dictionaries.ts +10 -0
  22. package/src/modules/sales/commands/returns.ts +44 -3
  23. package/src/modules/sales/components/documents/ReturnDialog.tsx +1 -0
  24. package/src/modules/sales/components/documents/ReturnsSection.tsx +18 -5
  25. package/src/modules/sales/i18n/de.json +1 -0
  26. package/src/modules/sales/i18n/en.json +1 -0
  27. package/src/modules/sales/i18n/es.json +1 -0
  28. package/src/modules/sales/i18n/pl.json +1 -0
  29. package/src/modules/sales/lib/returnQuantity.ts +48 -1
  30. package/src/modules/workflows/lib/event-trigger-service.ts +3 -0
@@ -17,6 +17,7 @@ import {
17
17
  emitSalesDocumentTotalsRefresh,
18
18
  subscribeSalesDocumentTotalsRefresh,
19
19
  } from '@open-mercato/core/modules/sales/lib/frontend/documentTotalsEvents'
20
+ import { sumShippedQuantityByLine } from '@open-mercato/core/modules/sales/lib/returnQuantity'
20
21
  import { formatMoney, normalizeNumber } from './lineItemUtils'
21
22
  import { ReturnDialog, type ReturnOrderLine } from './ReturnDialog'
22
23
  import { ReturnEditDialog, type ReturnEditRecord } from './ReturnEditDialog'
@@ -60,14 +61,25 @@ export function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }
60
61
 
61
62
  const loadLines = React.useCallback(async () => {
62
63
  const params = new URLSearchParams({ page: '1', pageSize: '100', orderId })
63
- const response = await apiCall<{ items?: Array<Record<string, unknown>> }>(
64
- `/api/sales/order-lines?${params.toString()}`,
65
- undefined,
66
- { fallback: { items: [] } },
64
+ const shipmentParams = new URLSearchParams({ page: '1', pageSize: '200', orderId })
65
+ const [response, shipmentsResponse] = await Promise.all([
66
+ apiCall<{ items?: Array<Record<string, unknown>> }>(
67
+ `/api/sales/order-lines?${params.toString()}`,
68
+ undefined,
69
+ { fallback: { items: [] } },
70
+ ),
71
+ apiCall<{ items?: Array<Record<string, unknown>> }>(
72
+ `/api/sales/shipments?${shipmentParams.toString()}`,
73
+ undefined,
74
+ { fallback: { items: [] } },
75
+ ),
76
+ ])
77
+ const shippedByLine = sumShippedQuantityByLine(
78
+ Array.isArray(shipmentsResponse.result?.items) ? shipmentsResponse.result?.items : [],
67
79
  )
68
80
  const items = Array.isArray(response.result?.items) ? response.result?.items ?? [] : []
69
81
  const mapped: ReturnOrderLine[] = items
70
- .map((item) => {
82
+ .map((item): ReturnOrderLine | null => {
71
83
  const map = item as Record<string, unknown>
72
84
  const id = typeof map.id === 'string' ? map.id : null
73
85
  if (!id) return null
@@ -110,6 +122,7 @@ export function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }
110
122
  lineNumber,
111
123
  quantity: Number.isFinite(quantity) ? quantity : 0,
112
124
  returnedQuantity: Number.isFinite(returnedQuantity) ? returnedQuantity : 0,
125
+ shippedQuantity: shippedByLine.get(id) ?? 0,
113
126
  }
114
127
  })
115
128
  .filter((entry): entry is ReturnOrderLine => Boolean(entry?.id))
@@ -1326,6 +1326,7 @@
1326
1326
  "sales.returns.qty.increase": "Menge erhöhen",
1327
1327
  "sales.returns.quantity": "Menge",
1328
1328
  "sales.returns.quantityExceeded": "Es kann nicht mehr als die verbleibende Menge zurückgegeben werden.",
1329
+ "sales.returns.quantityExceedsShipped": "Es kann nicht mehr als die versandte Menge zurückgegeben werden. Versenden Sie die Artikel, bevor Sie eine Rücksendung erfassen.",
1329
1330
  "sales.returns.reason": "Grund",
1330
1331
  "sales.returns.reason.placeholder": "Optional",
1331
1332
  "sales.returns.returnNumber": "Rückgabe",
@@ -1326,6 +1326,7 @@
1326
1326
  "sales.returns.qty.increase": "Increase quantity",
1327
1327
  "sales.returns.quantity": "Quantity",
1328
1328
  "sales.returns.quantityExceeded": "Cannot return more than the remaining quantity.",
1329
+ "sales.returns.quantityExceedsShipped": "Cannot return more than the shipped quantity. Ship the items before recording a return.",
1329
1330
  "sales.returns.reason": "Reason",
1330
1331
  "sales.returns.reason.placeholder": "Optional",
1331
1332
  "sales.returns.returnNumber": "Return",
@@ -1326,6 +1326,7 @@
1326
1326
  "sales.returns.qty.increase": "Aumentar cantidad",
1327
1327
  "sales.returns.quantity": "Cantidad",
1328
1328
  "sales.returns.quantityExceeded": "No se puede devolver más que la cantidad restante.",
1329
+ "sales.returns.quantityExceedsShipped": "No se puede devolver más que la cantidad enviada. Envíe los artículos antes de registrar una devolución.",
1329
1330
  "sales.returns.reason": "Motivo",
1330
1331
  "sales.returns.reason.placeholder": "Opcional",
1331
1332
  "sales.returns.returnNumber": "Devolución",
@@ -1326,6 +1326,7 @@
1326
1326
  "sales.returns.qty.increase": "Zwiększ ilość",
1327
1327
  "sales.returns.quantity": "Ilość",
1328
1328
  "sales.returns.quantityExceeded": "Nie można zwrócić więcej niż pozostała ilość.",
1329
+ "sales.returns.quantityExceedsShipped": "Nie można zwrócić więcej niż wysłana ilość. Wyślij artykuły, zanim zarejestrujesz zwrot.",
1329
1330
  "sales.returns.reason": "Powód",
1330
1331
  "sales.returns.reason.placeholder": "Opcjonalny",
1331
1332
  "sales.returns.returnNumber": "Zwrot",
@@ -1,10 +1,57 @@
1
1
  export type ReturnAvailableInput = {
2
2
  quantity: number
3
3
  returnedQuantity: number
4
+ shippedQuantity?: number
4
5
  }
5
6
 
6
7
  export function computeAvailableReturnQuantity(line: ReturnAvailableInput): number {
7
- const raw = line.quantity - line.returnedQuantity
8
+ const cap =
9
+ typeof line.shippedQuantity === 'number' && Number.isFinite(line.shippedQuantity)
10
+ ? Math.min(line.quantity, line.shippedQuantity)
11
+ : line.quantity
12
+ const raw = cap - line.returnedQuantity
8
13
  if (!Number.isFinite(raw) || raw <= 0) return 0
9
14
  return Math.max(0, Math.floor(raw + 1e-6))
10
15
  }
16
+
17
+ function coerceQuantity(value: unknown): number {
18
+ if (typeof value === 'number' && Number.isFinite(value)) return value
19
+ if (typeof value === 'string' && value.trim().length) {
20
+ const parsed = Number(value)
21
+ if (Number.isFinite(parsed)) return parsed
22
+ }
23
+ return 0
24
+ }
25
+
26
+ /**
27
+ * Aggregate shipped quantity per order line from the `/api/sales/shipments`
28
+ * list payload. Mirrors the server-side `loadShippedQuantityByLine` cap so the
29
+ * Return dialog's "Available" figure matches what `sales.returns.create`
30
+ * enforces. Each shipment exposes its lines under `items` (either the
31
+ * persisted snapshot or DB-grouped rows), both carrying `orderLineId` and
32
+ * `quantity`.
33
+ */
34
+ export function sumShippedQuantityByLine(
35
+ shipments: ReadonlyArray<Record<string, unknown>> | null | undefined,
36
+ ): Map<string, number> {
37
+ const shippedByLine = new Map<string, number>()
38
+ if (!Array.isArray(shipments)) return shippedByLine
39
+ for (const shipment of shipments) {
40
+ if (!shipment || typeof shipment !== 'object') continue
41
+ const lineItems = (shipment as Record<string, unknown>).items
42
+ if (!Array.isArray(lineItems)) continue
43
+ for (const lineItem of lineItems) {
44
+ if (!lineItem || typeof lineItem !== 'object') continue
45
+ const record = lineItem as Record<string, unknown>
46
+ const orderLineId =
47
+ typeof record.orderLineId === 'string'
48
+ ? record.orderLineId
49
+ : typeof record.order_line_id === 'string'
50
+ ? record.order_line_id
51
+ : null
52
+ if (!orderLineId) continue
53
+ shippedByLine.set(orderLineId, (shippedByLine.get(orderLineId) ?? 0) + coerceQuantity(record.quantity))
54
+ }
55
+ }
56
+ return shippedByLine
57
+ }
@@ -123,6 +123,9 @@ function isSafeWorkflowRegexPattern(pattern: string): boolean {
123
123
  let lastClosedGroup: RegexGroupFrame | null = null
124
124
  let lastAtomWasQuantified = false
125
125
 
126
+ // Hand-written single-pass lexer: `index` is intentionally advanced inside the
127
+ // loop body (escape pairs, `(?:` prefixes, multi-char quantifiers, lazy `?`) on
128
+ // top of the `index += 1` update clause. Do not "simplify" these in-body writes.
126
129
  for (let index = 0; index < pattern.length; index += 1) {
127
130
  const char = pattern[index]
128
131