@open-mercato/core 0.4.8-develop-d16e2f51dc → 0.4.8-develop-4b58cde65d

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 (85) hide show
  1. package/dist/generated/entities/sales_return/index.js +31 -0
  2. package/dist/generated/entities/sales_return/index.js.map +7 -0
  3. package/dist/generated/entities/sales_return_line/index.js +29 -0
  4. package/dist/generated/entities/sales_return_line/index.js.map +7 -0
  5. package/dist/generated/entities.ids.generated.js +2 -0
  6. package/dist/generated/entities.ids.generated.js.map +2 -2
  7. package/dist/generated/entity-fields-registry.js +4 -0
  8. package/dist/generated/entity-fields-registry.js.map +2 -2
  9. package/dist/modules/sales/acl.js +2 -0
  10. package/dist/modules/sales/acl.js.map +2 -2
  11. package/dist/modules/sales/api/document-history/route.js +20 -2
  12. package/dist/modules/sales/api/document-history/route.js.map +2 -2
  13. package/dist/modules/sales/api/documents/factory.js +34 -0
  14. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  15. package/dist/modules/sales/api/returns/[id]/route.js +147 -0
  16. package/dist/modules/sales/api/returns/[id]/route.js.map +7 -0
  17. package/dist/modules/sales/api/returns/route.js +158 -0
  18. package/dist/modules/sales/api/returns/route.js.map +7 -0
  19. package/dist/modules/sales/backend/sales/documents/[id]/page.js +20 -1
  20. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  21. package/dist/modules/sales/commands/index.js +1 -0
  22. package/dist/modules/sales/commands/index.js.map +2 -2
  23. package/dist/modules/sales/commands/returns.js +467 -0
  24. package/dist/modules/sales/commands/returns.js.map +7 -0
  25. package/dist/modules/sales/components/documents/ReturnDialog.js +176 -0
  26. package/dist/modules/sales/components/documents/ReturnDialog.js.map +7 -0
  27. package/dist/modules/sales/components/documents/ReturnsSection.js +188 -0
  28. package/dist/modules/sales/components/documents/ReturnsSection.js.map +7 -0
  29. package/dist/modules/sales/data/entities.js +115 -1
  30. package/dist/modules/sales/data/entities.js.map +2 -2
  31. package/dist/modules/sales/data/validators.js +13 -0
  32. package/dist/modules/sales/data/validators.js.map +2 -2
  33. package/dist/modules/sales/events.js +4 -0
  34. package/dist/modules/sales/events.js.map +2 -2
  35. package/dist/modules/sales/lib/calculations.js +7 -0
  36. package/dist/modules/sales/lib/calculations.js.map +2 -2
  37. package/dist/modules/sales/lib/dictionaries.js +1 -0
  38. package/dist/modules/sales/lib/dictionaries.js.map +2 -2
  39. package/dist/modules/sales/lib/documentNumberTokens.js +2 -0
  40. package/dist/modules/sales/lib/documentNumberTokens.js.map +2 -2
  41. package/dist/modules/sales/lib/historyHelpers.js +15 -7
  42. package/dist/modules/sales/lib/historyHelpers.js.map +2 -2
  43. package/dist/modules/sales/lib/makeSalesLineRoute.js +42 -37
  44. package/dist/modules/sales/lib/makeSalesLineRoute.js.map +2 -2
  45. package/dist/modules/sales/migrations/Migration20260309073310.js +23 -0
  46. package/dist/modules/sales/migrations/Migration20260309073310.js.map +7 -0
  47. package/dist/modules/sales/services/salesDocumentNumberGenerator.js +8 -6
  48. package/dist/modules/sales/services/salesDocumentNumberGenerator.js.map +2 -2
  49. package/dist/modules/sales/setup.js +1 -1
  50. package/dist/modules/sales/setup.js.map +2 -2
  51. package/dist/modules/sales/widgets/injection/document-history/widget.client.js +25 -16
  52. package/dist/modules/sales/widgets/injection/document-history/widget.client.js.map +2 -2
  53. package/generated/entities/sales_return/index.ts +14 -0
  54. package/generated/entities/sales_return_line/index.ts +13 -0
  55. package/generated/entities.ids.generated.ts +2 -0
  56. package/generated/entity-fields-registry.ts +4 -0
  57. package/package.json +3 -3
  58. package/src/modules/sales/AGENTS.md +1 -0
  59. package/src/modules/sales/acl.ts +2 -0
  60. package/src/modules/sales/api/document-history/route.ts +25 -1
  61. package/src/modules/sales/api/documents/factory.ts +35 -0
  62. package/src/modules/sales/api/returns/[id]/route.ts +156 -0
  63. package/src/modules/sales/api/returns/route.ts +171 -0
  64. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +18 -0
  65. package/src/modules/sales/commands/index.ts +1 -0
  66. package/src/modules/sales/commands/returns.ts +540 -0
  67. package/src/modules/sales/components/documents/ReturnDialog.tsx +216 -0
  68. package/src/modules/sales/components/documents/ReturnsSection.tsx +270 -0
  69. package/src/modules/sales/data/entities.ts +99 -3
  70. package/src/modules/sales/data/validators.ts +16 -0
  71. package/src/modules/sales/events.ts +5 -0
  72. package/src/modules/sales/i18n/de.json +32 -0
  73. package/src/modules/sales/i18n/en.json +32 -0
  74. package/src/modules/sales/i18n/es.json +32 -0
  75. package/src/modules/sales/i18n/pl.json +32 -0
  76. package/src/modules/sales/lib/calculations.ts +9 -0
  77. package/src/modules/sales/lib/dictionaries.ts +1 -0
  78. package/src/modules/sales/lib/documentNumberTokens.ts +2 -1
  79. package/src/modules/sales/lib/historyHelpers.ts +20 -9
  80. package/src/modules/sales/lib/makeSalesLineRoute.ts +42 -37
  81. package/src/modules/sales/migrations/.snapshot-open-mercato.json +398 -0
  82. package/src/modules/sales/migrations/Migration20260309073310.ts +26 -0
  83. package/src/modules/sales/services/salesDocumentNumberGenerator.ts +15 -4
  84. package/src/modules/sales/setup.ts +1 -1
  85. package/src/modules/sales/widgets/injection/document-history/widget.client.tsx +26 -17
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sales/lib/documentNumberTokens.ts"],
4
- "sourcesContent": ["export type SalesDocumentNumberKind = 'quote' | 'order'\n\nexport const DEFAULT_ORDER_NUMBER_FORMAT = 'ORDER-{yyyy}{mm}{dd}-{seq:5}'\nexport const DEFAULT_QUOTE_NUMBER_FORMAT = 'QUOTE-{yyyy}{mm}{dd}-{seq:5}'\n\nexport const DOCUMENT_NUMBER_TOKENS: Array<{ token: string; description: string }> = [\n { token: '{yyyy}', description: '4-digit year, e.g. 2025' },\n { token: '{yy}', description: '2-digit year, e.g. 25' },\n { token: '{mm}', description: 'Month with leading zero, e.g. 04' },\n { token: '{dd}', description: 'Day with leading zero, e.g. 09' },\n { token: '{hh}', description: 'Hour in 24h format, e.g. 17' },\n {\n token: '{seq}',\n description: 'Sequence number scoped per organization and document type. Use {seq:5} to pad with zeros.',\n },\n {\n token: '{rand}',\n description: 'Random numeric block (default 4 digits). Use {rand:6} to control length.',\n },\n {\n token: '{nanoid}',\n description: 'Nano ID (default 12 chars). Use {nanoid:8} for a shorter version.',\n },\n { token: '{guid}', description: 'GUID / UUID v4' },\n { token: '{kind}', description: 'Document kind, e.g. order or quote' },\n]\n"],
5
- "mappings": "AAEO,MAAM,8BAA8B;AACpC,MAAM,8BAA8B;AAEpC,MAAM,yBAAwE;AAAA,EACnF,EAAE,OAAO,UAAU,aAAa,0BAA0B;AAAA,EAC1D,EAAE,OAAO,QAAQ,aAAa,wBAAwB;AAAA,EACtD,EAAE,OAAO,QAAQ,aAAa,mCAAmC;AAAA,EACjE,EAAE,OAAO,QAAQ,aAAa,iCAAiC;AAAA,EAC/D,EAAE,OAAO,QAAQ,aAAa,8BAA8B;AAAA,EAC5D;AAAA,IACE,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,EAAE,OAAO,UAAU,aAAa,iBAAiB;AAAA,EACjD,EAAE,OAAO,UAAU,aAAa,qCAAqC;AACvE;",
4
+ "sourcesContent": ["export type SalesDocumentNumberKind = 'quote' | 'order' | 'return'\n\nexport const DEFAULT_ORDER_NUMBER_FORMAT = 'ORDER-{yyyy}{mm}{dd}-{seq:5}'\nexport const DEFAULT_QUOTE_NUMBER_FORMAT = 'QUOTE-{yyyy}{mm}{dd}-{seq:5}'\nexport const DEFAULT_RETURN_NUMBER_FORMAT = 'RET-{yyyy}{mm}{dd}-{seq:5}'\n\nexport const DOCUMENT_NUMBER_TOKENS: Array<{ token: string; description: string }> = [\n { token: '{yyyy}', description: '4-digit year, e.g. 2025' },\n { token: '{yy}', description: '2-digit year, e.g. 25' },\n { token: '{mm}', description: 'Month with leading zero, e.g. 04' },\n { token: '{dd}', description: 'Day with leading zero, e.g. 09' },\n { token: '{hh}', description: 'Hour in 24h format, e.g. 17' },\n {\n token: '{seq}',\n description: 'Sequence number scoped per organization and document type. Use {seq:5} to pad with zeros.',\n },\n {\n token: '{rand}',\n description: 'Random numeric block (default 4 digits). Use {rand:6} to control length.',\n },\n {\n token: '{nanoid}',\n description: 'Nano ID (default 12 chars). Use {nanoid:8} for a shorter version.',\n },\n { token: '{guid}', description: 'GUID / UUID v4' },\n { token: '{kind}', description: 'Document kind, e.g. order or quote' },\n]\n"],
5
+ "mappings": "AAEO,MAAM,8BAA8B;AACpC,MAAM,8BAA8B;AACpC,MAAM,+BAA+B;AAErC,MAAM,yBAAwE;AAAA,EACnF,EAAE,OAAO,UAAU,aAAa,0BAA0B;AAAA,EAC1D,EAAE,OAAO,QAAQ,aAAa,wBAAwB;AAAA,EACtD,EAAE,OAAO,QAAQ,aAAa,mCAAmC;AAAA,EACjE,EAAE,OAAO,QAAQ,aAAa,iCAAiC;AAAA,EAC/D,EAAE,OAAO,QAAQ,aAAa,8BAA8B;AAAA,EAC5D;AAAA,IACE,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA,EAAE,OAAO,UAAU,aAAa,iBAAiB;AAAA,EACjD,EAAE,OAAO,UAAU,aAAa,qCAAqC;AACvE;",
6
6
  "names": []
7
7
  }
@@ -4,7 +4,10 @@ function extractStatusFromSnapshot(snapshot) {
4
4
  if (typeof s.status === "string") return s.status;
5
5
  if (s.order && typeof s.order === "object") {
6
6
  const order = s.order;
7
- if (typeof order.status === "string") return order.status;
7
+ const orderStatus = typeof order.status === "string" ? order.status : null;
8
+ const fulfillmentStatus = typeof order.fulfillment_status === "string" ? order.fulfillment_status : null;
9
+ const paymentStatus = typeof order.payment_status === "string" ? order.payment_status : null;
10
+ return orderStatus ?? fulfillmentStatus ?? paymentStatus ?? null;
8
11
  }
9
12
  if (s.quote && typeof s.quote === "object") {
10
13
  const quote = s.quote;
@@ -13,11 +16,13 @@ function extractStatusFromSnapshot(snapshot) {
13
16
  return null;
14
17
  }
15
18
  function detectStatusChange(log) {
16
- if (!log.snapshotBefore) return null;
17
- const before = extractStatusFromSnapshot(log.snapshotBefore);
18
19
  const after = extractStatusFromSnapshot(log.snapshotAfter);
20
+ const before = log.snapshotBefore ? extractStatusFromSnapshot(log.snapshotBefore) : null;
21
+ if (!log.snapshotBefore) {
22
+ return { statusFrom: null, statusTo: after ?? null };
23
+ }
19
24
  if (before !== after && (before !== null || after !== null)) {
20
- return { statusFrom: before, statusTo: after };
25
+ return { statusFrom: before, statusTo: after ?? null };
21
26
  }
22
27
  return null;
23
28
  }
@@ -27,9 +32,12 @@ function normalizeActionLogToHistoryEntry(log, kind, displayUsers) {
27
32
  let action = log.actionLabel || log.commandId;
28
33
  let metadata = { documentKind: kind, commandId: log.commandId };
29
34
  if (statusChange) {
30
- entryKind = "status";
31
- action = statusChange.statusTo ?? "unknown";
32
- metadata = { ...metadata, statusFrom: statusChange.statusFrom, statusTo: statusChange.statusTo };
35
+ const hasStatusValues = statusChange.statusFrom != null || statusChange.statusTo != null;
36
+ if (hasStatusValues) {
37
+ entryKind = "status";
38
+ action = statusChange.statusTo ?? "unknown";
39
+ metadata = { ...metadata, statusFrom: statusChange.statusFrom, statusTo: statusChange.statusTo };
40
+ }
33
41
  }
34
42
  const actorLabel = log.actorUserId ? displayUsers?.[log.actorUserId] ?? log.actorUserId : "system";
35
43
  return {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sales/lib/historyHelpers.ts"],
4
- "sourcesContent": ["import type { ActionLog } from '@open-mercato/core/modules/audit_logs/data/entities'\nimport type { SalesNote } from '../data/entities'\n\nexport type HistoryEntry = {\n id: string\n occurredAt: string\n kind: 'status' | 'action' | 'comment'\n action: string\n actor: { id: string | null; label: string }\n source: 'action_log' | 'note'\n metadata?: {\n statusFrom?: string | null\n statusTo?: string | null\n documentKind?: 'order' | 'quote'\n commandId?: string\n }\n}\n\nfunction extractStatusFromSnapshot(snapshot: unknown): string | null {\n if (!snapshot || typeof snapshot !== 'object') return null\n const s = snapshot as Record<string, unknown>\n // Dedicated logStatusChange entries: { status: \"value\" }\n if (typeof s.status === 'string') return s.status\n // Regular update snapshots: { order: { status: \"value\", ... } }\n if (s.order && typeof s.order === 'object') {\n const order = s.order as Record<string, unknown>\n if (typeof order.status === 'string') return order.status\n }\n // Regular update snapshots: { quote: { status: \"value\", ... } }\n if (s.quote && typeof s.quote === 'object') {\n const quote = s.quote as Record<string, unknown>\n if (typeof quote.status === 'string') return quote.status\n }\n return null\n}\n\nexport function detectStatusChange(log: ActionLog): {\n statusFrom: string | null\n statusTo: string | null\n} | null {\n // Skip creations \u2014 no previous state means no transition\n if (!log.snapshotBefore) return null\n const before = extractStatusFromSnapshot(log.snapshotBefore)\n const after = extractStatusFromSnapshot(log.snapshotAfter)\n if (before !== after && (before !== null || after !== null)) {\n return { statusFrom: before, statusTo: after }\n }\n return null\n}\n\n/**\n * Normalizes ActionLog to HistoryEntry (status/action/comment).\n */\nexport function normalizeActionLogToHistoryEntry(\n log: ActionLog,\n kind: 'order' | 'quote',\n displayUsers?: Record<string, string>,\n): HistoryEntry {\n const statusChange = detectStatusChange(log)\n let entryKind: 'status' | 'action' = 'action'\n let action = log.actionLabel || log.commandId\n let metadata: HistoryEntry['metadata'] = { documentKind: kind, commandId: log.commandId }\n if (statusChange) {\n entryKind = 'status'\n action = statusChange.statusTo ?? 'unknown'\n metadata = { ...metadata, statusFrom: statusChange.statusFrom, statusTo: statusChange.statusTo }\n }\n const actorLabel = log.actorUserId\n ? (displayUsers?.[log.actorUserId] ?? log.actorUserId)\n : 'system'\n return {\n id: log.id,\n occurredAt: log.createdAt.toISOString(),\n kind: entryKind,\n action,\n actor: { id: log.actorUserId, label: actorLabel },\n source: 'action_log',\n metadata,\n }\n}\n\nexport function normalizeNoteToHistoryEntry(\n note: SalesNote,\n kind: 'order' | 'quote',\n displayUsers?: Record<string, string>,\n): HistoryEntry {\n const actorLabel = note.authorUserId\n ? (displayUsers?.[note.authorUserId] ?? note.authorUserId)\n : 'system'\n return {\n id: note.id,\n occurredAt: note.createdAt.toISOString(),\n kind: 'comment',\n action: note.body,\n actor: { id: note.authorUserId ?? null, label: actorLabel },\n source: 'note',\n metadata: { documentKind: kind },\n }\n}\n\nexport type HistoryBuilderInput = {\n actionLogs: ActionLog[]\n notes?: SalesNote[]\n kind: 'order' | 'quote'\n displayUsers?: Record<string, string>\n}\n\nexport function buildHistoryEntries(input: HistoryBuilderInput): HistoryEntry[] {\n const logEntries = input.actionLogs.map((log) =>\n normalizeActionLogToHistoryEntry(log, input.kind, input.displayUsers)\n )\n const noteEntries = (input.notes ?? []).map((note) =>\n normalizeNoteToHistoryEntry(note, input.kind, input.displayUsers)\n )\n return [...logEntries, ...noteEntries].sort(\n (a, b) => new Date(b.occurredAt).getTime() - new Date(a.occurredAt).getTime()\n )\n}\n"],
5
- "mappings": "AAkBA,SAAS,0BAA0B,UAAkC;AACnE,MAAI,CAAC,YAAY,OAAO,aAAa,SAAU,QAAO;AACtD,QAAM,IAAI;AAEV,MAAI,OAAO,EAAE,WAAW,SAAU,QAAO,EAAE;AAE3C,MAAI,EAAE,SAAS,OAAO,EAAE,UAAU,UAAU;AAC1C,UAAM,QAAQ,EAAE;AAChB,QAAI,OAAO,MAAM,WAAW,SAAU,QAAO,MAAM;AAAA,EACrD;AAEA,MAAI,EAAE,SAAS,OAAO,EAAE,UAAU,UAAU;AAC1C,UAAM,QAAQ,EAAE;AAChB,QAAI,OAAO,MAAM,WAAW,SAAU,QAAO,MAAM;AAAA,EACrD;AACA,SAAO;AACT;AAEO,SAAS,mBAAmB,KAG1B;AAEP,MAAI,CAAC,IAAI,eAAgB,QAAO;AAChC,QAAM,SAAS,0BAA0B,IAAI,cAAc;AAC3D,QAAM,QAAQ,0BAA0B,IAAI,aAAa;AACzD,MAAI,WAAW,UAAU,WAAW,QAAQ,UAAU,OAAO;AAC3D,WAAO,EAAE,YAAY,QAAQ,UAAU,MAAM;AAAA,EAC/C;AACA,SAAO;AACT;AAKO,SAAS,iCACd,KACA,MACA,cACc;AACd,QAAM,eAAe,mBAAmB,GAAG;AAC3C,MAAI,YAAiC;AACrC,MAAI,SAAS,IAAI,eAAe,IAAI;AACpC,MAAI,WAAqC,EAAE,cAAc,MAAM,WAAW,IAAI,UAAU;AACxF,MAAI,cAAc;AAChB,gBAAY;AACZ,aAAS,aAAa,YAAY;AAClC,eAAW,EAAE,GAAG,UAAU,YAAY,aAAa,YAAY,UAAU,aAAa,SAAS;AAAA,EACjG;AACA,QAAM,aAAa,IAAI,cAClB,eAAe,IAAI,WAAW,KAAK,IAAI,cACxC;AACJ,SAAO;AAAA,IACL,IAAI,IAAI;AAAA,IACR,YAAY,IAAI,UAAU,YAAY;AAAA,IACtC,MAAM;AAAA,IACN;AAAA,IACA,OAAO,EAAE,IAAI,IAAI,aAAa,OAAO,WAAW;AAAA,IAChD,QAAQ;AAAA,IACR;AAAA,EACF;AACF;AAEO,SAAS,4BACd,MACA,MACA,cACc;AACd,QAAM,aAAa,KAAK,eACnB,eAAe,KAAK,YAAY,KAAK,KAAK,eAC3C;AACJ,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,YAAY,KAAK,UAAU,YAAY;AAAA,IACvC,MAAM;AAAA,IACN,QAAQ,KAAK;AAAA,IACb,OAAO,EAAE,IAAI,KAAK,gBAAgB,MAAM,OAAO,WAAW;AAAA,IAC1D,QAAQ;AAAA,IACR,UAAU,EAAE,cAAc,KAAK;AAAA,EACjC;AACF;AASO,SAAS,oBAAoB,OAA4C;AAC9E,QAAM,aAAa,MAAM,WAAW;AAAA,IAAI,CAAC,QACvC,iCAAiC,KAAK,MAAM,MAAM,MAAM,YAAY;AAAA,EACtE;AACA,QAAM,eAAe,MAAM,SAAS,CAAC,GAAG;AAAA,IAAI,CAAC,SAC3C,4BAA4B,MAAM,MAAM,MAAM,MAAM,YAAY;AAAA,EAClE;AACA,SAAO,CAAC,GAAG,YAAY,GAAG,WAAW,EAAE;AAAA,IACrC,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,UAAU,EAAE,QAAQ,IAAI,IAAI,KAAK,EAAE,UAAU,EAAE,QAAQ;AAAA,EAC9E;AACF;",
4
+ "sourcesContent": ["import type { ActionLog } from '@open-mercato/core/modules/audit_logs/data/entities'\nimport type { SalesNote } from '../data/entities'\n\nexport type HistoryEntry = {\n id: string\n occurredAt: string\n kind: 'status' | 'action' | 'comment'\n action: string\n actor: { id: string | null; label: string }\n source: 'action_log' | 'note'\n metadata?: {\n statusFrom?: string | null\n statusTo?: string | null\n documentKind?: 'order' | 'quote'\n commandId?: string\n }\n}\n\nfunction extractStatusFromSnapshot(snapshot: unknown): string | null {\n if (!snapshot || typeof snapshot !== 'object') return null\n const s = snapshot as Record<string, unknown>\n // Dedicated logStatusChange entries: { status: \"value\" }\n if (typeof s.status === 'string') return s.status\n // Regular update snapshots: { order: { status, fulfillment_status, payment_status, ... } }\n if (s.order && typeof s.order === 'object') {\n const order = s.order as Record<string, unknown>\n const orderStatus = typeof order.status === 'string' ? order.status : null\n const fulfillmentStatus = typeof order.fulfillment_status === 'string' ? order.fulfillment_status : null\n const paymentStatus = typeof order.payment_status === 'string' ? order.payment_status : null\n return orderStatus ?? fulfillmentStatus ?? paymentStatus ?? null\n }\n // Regular update snapshots: { quote: { status: \"value\", ... } }\n if (s.quote && typeof s.quote === 'object') {\n const quote = s.quote as Record<string, unknown>\n if (typeof quote.status === 'string') return quote.status\n }\n return null\n}\n\nexport function detectStatusChange(log: ActionLog): {\n statusFrom: string | null\n statusTo: string | null\n} | null {\n const after = extractStatusFromSnapshot(log.snapshotAfter)\n const before = log.snapshotBefore\n ? extractStatusFromSnapshot(log.snapshotBefore)\n : null\n // Creation (no snapshotBefore): always one status entry (initial value or \"created\")\n if (!log.snapshotBefore) {\n return { statusFrom: null, statusTo: after ?? null }\n }\n if (before !== after && (before !== null || after !== null)) {\n return { statusFrom: before, statusTo: after ?? null }\n }\n return null\n}\n\n/**\n * Normalizes ActionLog to HistoryEntry (status/action/comment).\n */\nexport function normalizeActionLogToHistoryEntry(\n log: ActionLog,\n kind: 'order' | 'quote',\n displayUsers?: Record<string, string>,\n): HistoryEntry {\n const statusChange = detectStatusChange(log)\n let entryKind: 'status' | 'action' = 'action'\n let action = log.actionLabel || log.commandId\n let metadata: HistoryEntry['metadata'] = { documentKind: kind, commandId: log.commandId }\n if (statusChange) {\n const hasStatusValues = statusChange.statusFrom != null || statusChange.statusTo != null\n if (hasStatusValues) {\n entryKind = 'status'\n action = statusChange.statusTo ?? 'unknown'\n metadata = { ...metadata, statusFrom: statusChange.statusFrom, statusTo: statusChange.statusTo }\n }\n // When both are null (e.g. Create return/shipment/payment with non-document snapshot), keep as action and use actionLabel\n }\n const actorLabel = log.actorUserId\n ? (displayUsers?.[log.actorUserId] ?? log.actorUserId)\n : 'system'\n return {\n id: log.id,\n occurredAt: log.createdAt.toISOString(),\n kind: entryKind,\n action,\n actor: { id: log.actorUserId, label: actorLabel },\n source: 'action_log',\n metadata,\n }\n}\n\nexport function normalizeNoteToHistoryEntry(\n note: SalesNote,\n kind: 'order' | 'quote',\n displayUsers?: Record<string, string>,\n): HistoryEntry {\n const actorLabel = note.authorUserId\n ? (displayUsers?.[note.authorUserId] ?? note.authorUserId)\n : 'system'\n return {\n id: note.id,\n occurredAt: note.createdAt.toISOString(),\n kind: 'comment',\n action: note.body,\n actor: { id: note.authorUserId ?? null, label: actorLabel },\n source: 'note',\n metadata: { documentKind: kind },\n }\n}\n\nexport type HistoryBuilderInput = {\n actionLogs: ActionLog[]\n notes?: SalesNote[]\n kind: 'order' | 'quote'\n displayUsers?: Record<string, string>\n}\n\nexport function buildHistoryEntries(input: HistoryBuilderInput): HistoryEntry[] {\n const logEntries = input.actionLogs.map((log) =>\n normalizeActionLogToHistoryEntry(log, input.kind, input.displayUsers)\n )\n const noteEntries = (input.notes ?? []).map((note) =>\n normalizeNoteToHistoryEntry(note, input.kind, input.displayUsers)\n )\n return [...logEntries, ...noteEntries].sort(\n (a, b) => new Date(b.occurredAt).getTime() - new Date(a.occurredAt).getTime()\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -122,43 +122,48 @@ function makeSalesLineRoute(config) {
122
122
  list: {
123
123
  schema: listSchema,
124
124
  entityId,
125
- fields: [
126
- F.id,
127
- parentFkColumn,
128
- F.line_number,
129
- F.kind,
130
- F.status_entry_id,
131
- F.status,
132
- F.product_id,
133
- F.product_variant_id,
134
- F.catalog_snapshot,
135
- F.name,
136
- F.description,
137
- F.comment,
138
- F.organization_id,
139
- F.tenant_id,
140
- F.quantity,
141
- F.quantity_unit,
142
- F.normalized_quantity,
143
- F.normalized_unit,
144
- F.uom_snapshot,
145
- F.currency_code,
146
- F.unit_price_net,
147
- F.unit_price_gross,
148
- F.discount_amount,
149
- F.discount_percent,
150
- F.tax_rate,
151
- F.tax_amount,
152
- F.total_net_amount,
153
- F.total_gross_amount,
154
- F.configuration,
155
- F.promotion_code,
156
- F.promotion_snapshot,
157
- F.metadata,
158
- F.custom_field_set_id,
159
- F.created_at,
160
- F.updated_at
161
- ],
125
+ fields: (() => {
126
+ const fields = [
127
+ F.id,
128
+ parentFkColumn,
129
+ F.line_number,
130
+ F.kind,
131
+ F.status_entry_id,
132
+ F.status,
133
+ F.product_id,
134
+ F.product_variant_id,
135
+ F.catalog_snapshot,
136
+ F.name,
137
+ F.description,
138
+ F.comment,
139
+ F.organization_id,
140
+ F.tenant_id,
141
+ F.quantity,
142
+ F.quantity_unit,
143
+ F.normalized_quantity,
144
+ F.normalized_unit,
145
+ F.uom_snapshot,
146
+ F.currency_code,
147
+ F.unit_price_net,
148
+ F.unit_price_gross,
149
+ F.discount_amount,
150
+ F.discount_percent,
151
+ F.tax_rate,
152
+ F.tax_amount,
153
+ F.total_net_amount,
154
+ F.total_gross_amount,
155
+ F.configuration,
156
+ F.promotion_code,
157
+ F.promotion_snapshot,
158
+ F.metadata,
159
+ F.custom_field_set_id,
160
+ F.created_at,
161
+ F.updated_at
162
+ ];
163
+ const returnedQuantity = F["returned_quantity"];
164
+ if (typeof returnedQuantity === "string") fields.push(returnedQuantity);
165
+ return fields;
166
+ })(),
162
167
  sortFieldMap: {
163
168
  createdAt: F.created_at,
164
169
  updatedAt: F.updated_at,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sales/lib/makeSalesLineRoute.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\nimport { makeCrudRoute, type CrudCtx } from '@open-mercato/shared/lib/crud/factory'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n buildCustomFieldFiltersFromQuery,\n extractAllCustomFieldEntries,\n} from '@open-mercato/shared/lib/crud/custom-fields'\nimport {\n canonicalizeUnitCode,\n REFERENCE_UNIT_CODES,\n} from '@open-mercato/shared/lib/units/unitCodes'\nimport {\n createPagedListResponseSchema,\n createSalesCrudOpenApi,\n defaultOkResponseSchema,\n} from '../api/openapi'\nimport { withScopedPayload } from '../api/utils'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- MikroORM entity class constructor\ntype EntityClass = new (...args: any[]) => unknown\n\ninterface SalesLineRouteConfig {\n entity: EntityClass\n entityId: string\n fieldConstants: Record<string, string>\n parentFkColumn: string\n parentFkParam: string\n createSchema: z.ZodObject<z.ZodRawShape>\n features: { view: string; manage: string }\n commandPrefix: string\n openApi: {\n resourceName: string\n description: string\n }\n}\n\nconst rawBodySchema = z.object({}).passthrough()\n\nfunction resolveRawBody(raw: unknown): Record<string, unknown> {\n if (!raw || typeof raw !== 'object') return {}\n if ('body' in raw) {\n const payload = raw as { body?: unknown }\n if (payload.body && typeof payload.body === 'object') {\n return payload.body as Record<string, unknown>\n }\n }\n return raw as Record<string, unknown>\n}\n\nfunction transformItem(item: Record<string, unknown> | null | undefined) {\n if (!item) return item\n const normalized = { ...item }\n const cfEntries = extractAllCustomFieldEntries(item)\n for (const key of Object.keys(normalized)) {\n if (key.startsWith('cf:')) delete normalized[key]\n }\n const quantityUnit = canonicalizeUnitCode(\n normalized['quantity_unit'] ?? normalized['quantityUnit'],\n )\n const normalizedUnit =\n canonicalizeUnitCode(\n normalized['normalized_unit'] ?? normalized['normalizedUnit'],\n ) ?? quantityUnit\n return {\n ...normalized,\n quantity_unit: quantityUnit,\n normalized_unit: normalizedUnit,\n ...cfEntries,\n }\n}\n\nconst uomSnapshotOpenApiSchema = z\n .object({\n version: z.literal(1),\n productId: z.string().nullable(),\n productVariantId: z.string().nullable(),\n baseUnitCode: z.string().nullable(),\n enteredUnitCode: z.string().nullable(),\n enteredQuantity: z.string(),\n toBaseFactor: z.string(),\n normalizedQuantity: z.string(),\n rounding: z.object({\n mode: z.enum(['half_up', 'down', 'up']),\n scale: z.number().int(),\n }),\n source: z.object({\n conversionId: z.string().nullable(),\n resolvedAt: z.string(),\n }),\n unitPriceReference: z\n .object({\n enabled: z.boolean(),\n referenceUnitCode: z.enum(REFERENCE_UNIT_CODES).nullable(),\n baseQuantity: z.string().nullable(),\n grossPerReference: z.string().nullable().optional(),\n netPerReference: z.string().nullable().optional(),\n })\n .optional(),\n })\n .nullable()\n .optional()\n\nexport function makeSalesLineRoute(config: SalesLineRouteConfig) {\n const {\n entity,\n entityId,\n fieldConstants: F,\n parentFkColumn,\n parentFkParam,\n createSchema,\n features,\n commandPrefix,\n } = config\n\n const listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n id: z.string().uuid().optional(),\n [parentFkParam]: z.string().uuid().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\n const upsertSchema = createSchema.extend({\n id: z.string().uuid().optional(),\n })\n\n const deleteSchema = z.object({\n id: z.string().uuid(),\n [parentFkParam]: z.string().uuid(),\n })\n\n const routeMetadata = {\n GET: { requireAuth: true, requireFeatures: [features.view] },\n POST: { requireAuth: true, requireFeatures: [features.manage] },\n PUT: { requireAuth: true, requireFeatures: [features.manage] },\n DELETE: { requireAuth: true, requireFeatures: [features.manage] },\n }\n\n const crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: {\n entityType: entityId,\n },\n enrichers: {\n entityId,\n },\n list: {\n schema: listSchema,\n entityId,\n fields: [\n F.id,\n parentFkColumn,\n F.line_number,\n F.kind,\n F.status_entry_id,\n F.status,\n F.product_id,\n F.product_variant_id,\n F.catalog_snapshot,\n F.name,\n F.description,\n F.comment,\n F.organization_id,\n F.tenant_id,\n F.quantity,\n F.quantity_unit,\n F.normalized_quantity,\n F.normalized_unit,\n F.uom_snapshot,\n F.currency_code,\n F.unit_price_net,\n F.unit_price_gross,\n F.discount_amount,\n F.discount_percent,\n F.tax_rate,\n F.tax_amount,\n F.total_net_amount,\n F.total_gross_amount,\n F.configuration,\n F.promotion_code,\n F.promotion_snapshot,\n F.metadata,\n F.custom_field_set_id,\n F.created_at,\n F.updated_at,\n ],\n sortFieldMap: {\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n lineNumber: F.line_number,\n },\n buildFilters: async (query: Record<string, unknown>, ctx: CrudCtx) => {\n const filters: Record<string, unknown> = {}\n if (query.id) filters.id = { $eq: query.id }\n if (query[parentFkParam]) filters[parentFkColumn] = { $eq: query[parentFkParam] }\n try {\n const em = ctx.container.resolve('em')\n const cfFilters = await buildCustomFieldFiltersFromQuery({\n entityId,\n query,\n em,\n tenantId: ctx.auth?.tenantId ?? null,\n })\n Object.assign(filters, cfFilters)\n } catch {\n // ignore\n }\n return filters\n },\n transformItem,\n },\n actions: {\n create: {\n commandId: `${commandPrefix}.upsert`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }: { raw: unknown; ctx: CrudCtx }) => {\n const { translate } = await resolveTranslations()\n const payload = upsertSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n )\n return { body: payload }\n },\n response: ({ result }: { result: Record<string, unknown> | null }) => ({\n id: result?.lineId ?? null,\n [parentFkParam]: result?.[parentFkParam] ?? null,\n }),\n status: 201,\n },\n update: {\n commandId: `${commandPrefix}.upsert`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }: { raw: unknown; ctx: CrudCtx }) => {\n const { translate } = await resolveTranslations()\n const payload = upsertSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n )\n return { body: payload }\n },\n response: ({ result }: { result: Record<string, unknown> | null }) => ({\n id: result?.lineId ?? null,\n [parentFkParam]: result?.[parentFkParam] ?? null,\n }),\n },\n delete: {\n commandId: `${commandPrefix}.delete`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }: { raw: unknown; ctx: CrudCtx }) => {\n const { translate } = await resolveTranslations()\n const payload = deleteSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n )\n if (!payload.id || !payload[parentFkParam]) {\n throw new CrudHttpError(400, {\n error: translate(\n 'sales.documents.detail.error',\n 'Document not found or inaccessible.',\n ),\n })\n }\n return { body: payload }\n },\n response: () => ({ ok: true }),\n },\n },\n })\n\n const lineItemSchema = z.object({\n id: z.string().uuid(),\n [parentFkColumn]: z.string().uuid(),\n line_number: z.number(),\n kind: z.string(),\n status_entry_id: z.string().uuid().nullable().optional(),\n status: z.string().nullable().optional(),\n product_id: z.string().uuid().nullable().optional(),\n product_variant_id: z.string().uuid().nullable().optional(),\n catalog_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n name: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n comment: z.string().nullable().optional(),\n quantity: z.number(),\n quantity_unit: z.string().nullable().optional(),\n normalized_quantity: z.number(),\n normalized_unit: z.string().nullable().optional(),\n uom_snapshot: uomSnapshotOpenApiSchema,\n currency_code: z.string(),\n unit_price_net: z.number(),\n unit_price_gross: z.number(),\n discount_amount: z.number(),\n discount_percent: z.number(),\n tax_rate: z.number(),\n tax_amount: z.number(),\n total_net_amount: z.number(),\n total_gross_amount: z.number(),\n configuration: z.record(z.string(), z.unknown()).nullable().optional(),\n promotion_code: z.string().nullable().optional(),\n promotion_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n custom_field_set_id: z.string().uuid().nullable().optional(),\n created_at: z.string(),\n updated_at: z.string(),\n })\n\n const upsertResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n [parentFkParam]: z.string().uuid().nullable(),\n })\n\n const openApi = createSalesCrudOpenApi({\n resourceName: config.openApi.resourceName,\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(lineItemSchema),\n create: {\n schema: upsertSchema,\n responseSchema: upsertResponseSchema,\n description: `Creates ${config.openApi.description}.`,\n },\n update: {\n schema: upsertSchema,\n responseSchema: upsertResponseSchema,\n description: `Updates ${config.openApi.description}.`,\n },\n del: {\n schema: deleteSchema,\n responseSchema: defaultOkResponseSchema,\n description: `Deletes ${config.openApi.description}.`,\n },\n })\n\n return {\n metadata: routeMetadata,\n openApi,\n GET: crud.GET,\n POST: crud.POST,\n PUT: crud.PUT,\n DELETE: crud.DELETE,\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { z } from 'zod'\nimport { makeCrudRoute, type CrudCtx } from '@open-mercato/shared/lib/crud/factory'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n buildCustomFieldFiltersFromQuery,\n extractAllCustomFieldEntries,\n} from '@open-mercato/shared/lib/crud/custom-fields'\nimport {\n canonicalizeUnitCode,\n REFERENCE_UNIT_CODES,\n} from '@open-mercato/shared/lib/units/unitCodes'\nimport {\n createPagedListResponseSchema,\n createSalesCrudOpenApi,\n defaultOkResponseSchema,\n} from '../api/openapi'\nimport { withScopedPayload } from '../api/utils'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- MikroORM entity class constructor\ntype EntityClass = new (...args: any[]) => unknown\n\ninterface SalesLineRouteConfig {\n entity: EntityClass\n entityId: string\n fieldConstants: Record<string, string>\n parentFkColumn: string\n parentFkParam: string\n createSchema: z.ZodObject<z.ZodRawShape>\n features: { view: string; manage: string }\n commandPrefix: string\n openApi: {\n resourceName: string\n description: string\n }\n}\n\nconst rawBodySchema = z.object({}).passthrough()\n\nfunction resolveRawBody(raw: unknown): Record<string, unknown> {\n if (!raw || typeof raw !== 'object') return {}\n if ('body' in raw) {\n const payload = raw as { body?: unknown }\n if (payload.body && typeof payload.body === 'object') {\n return payload.body as Record<string, unknown>\n }\n }\n return raw as Record<string, unknown>\n}\n\nfunction transformItem(item: Record<string, unknown> | null | undefined) {\n if (!item) return item\n const normalized = { ...item }\n const cfEntries = extractAllCustomFieldEntries(item)\n for (const key of Object.keys(normalized)) {\n if (key.startsWith('cf:')) delete normalized[key]\n }\n const quantityUnit = canonicalizeUnitCode(\n normalized['quantity_unit'] ?? normalized['quantityUnit'],\n )\n const normalizedUnit =\n canonicalizeUnitCode(\n normalized['normalized_unit'] ?? normalized['normalizedUnit'],\n ) ?? quantityUnit\n return {\n ...normalized,\n quantity_unit: quantityUnit,\n normalized_unit: normalizedUnit,\n ...cfEntries,\n }\n}\n\nconst uomSnapshotOpenApiSchema = z\n .object({\n version: z.literal(1),\n productId: z.string().nullable(),\n productVariantId: z.string().nullable(),\n baseUnitCode: z.string().nullable(),\n enteredUnitCode: z.string().nullable(),\n enteredQuantity: z.string(),\n toBaseFactor: z.string(),\n normalizedQuantity: z.string(),\n rounding: z.object({\n mode: z.enum(['half_up', 'down', 'up']),\n scale: z.number().int(),\n }),\n source: z.object({\n conversionId: z.string().nullable(),\n resolvedAt: z.string(),\n }),\n unitPriceReference: z\n .object({\n enabled: z.boolean(),\n referenceUnitCode: z.enum(REFERENCE_UNIT_CODES).nullable(),\n baseQuantity: z.string().nullable(),\n grossPerReference: z.string().nullable().optional(),\n netPerReference: z.string().nullable().optional(),\n })\n .optional(),\n })\n .nullable()\n .optional()\n\nexport function makeSalesLineRoute(config: SalesLineRouteConfig) {\n const {\n entity,\n entityId,\n fieldConstants: F,\n parentFkColumn,\n parentFkParam,\n createSchema,\n features,\n commandPrefix,\n } = config\n\n const listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n id: z.string().uuid().optional(),\n [parentFkParam]: z.string().uuid().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\n const upsertSchema = createSchema.extend({\n id: z.string().uuid().optional(),\n })\n\n const deleteSchema = z.object({\n id: z.string().uuid(),\n [parentFkParam]: z.string().uuid(),\n })\n\n const routeMetadata = {\n GET: { requireAuth: true, requireFeatures: [features.view] },\n POST: { requireAuth: true, requireFeatures: [features.manage] },\n PUT: { requireAuth: true, requireFeatures: [features.manage] },\n DELETE: { requireAuth: true, requireFeatures: [features.manage] },\n }\n\n const crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n indexer: {\n entityType: entityId,\n },\n enrichers: {\n entityId,\n },\n list: {\n schema: listSchema,\n entityId,\n fields: (() => {\n const fields: string[] = [\n F.id,\n parentFkColumn,\n F.line_number,\n F.kind,\n F.status_entry_id,\n F.status,\n F.product_id,\n F.product_variant_id,\n F.catalog_snapshot,\n F.name,\n F.description,\n F.comment,\n F.organization_id,\n F.tenant_id,\n F.quantity,\n F.quantity_unit,\n F.normalized_quantity,\n F.normalized_unit,\n F.uom_snapshot,\n F.currency_code,\n F.unit_price_net,\n F.unit_price_gross,\n F.discount_amount,\n F.discount_percent,\n F.tax_rate,\n F.tax_amount,\n F.total_net_amount,\n F.total_gross_amount,\n F.configuration,\n F.promotion_code,\n F.promotion_snapshot,\n F.metadata,\n F.custom_field_set_id,\n F.created_at,\n F.updated_at,\n ]\n const returnedQuantity = F['returned_quantity']\n if (typeof returnedQuantity === 'string') fields.push(returnedQuantity)\n return fields\n })(),\n sortFieldMap: {\n createdAt: F.created_at,\n updatedAt: F.updated_at,\n lineNumber: F.line_number,\n },\n buildFilters: async (query: Record<string, unknown>, ctx: CrudCtx) => {\n const filters: Record<string, unknown> = {}\n if (query.id) filters.id = { $eq: query.id }\n if (query[parentFkParam]) filters[parentFkColumn] = { $eq: query[parentFkParam] }\n try {\n const em = ctx.container.resolve('em')\n const cfFilters = await buildCustomFieldFiltersFromQuery({\n entityId,\n query,\n em,\n tenantId: ctx.auth?.tenantId ?? null,\n })\n Object.assign(filters, cfFilters)\n } catch {\n // ignore\n }\n return filters\n },\n transformItem,\n },\n actions: {\n create: {\n commandId: `${commandPrefix}.upsert`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }: { raw: unknown; ctx: CrudCtx }) => {\n const { translate } = await resolveTranslations()\n const payload = upsertSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n )\n return { body: payload }\n },\n response: ({ result }: { result: Record<string, unknown> | null }) => ({\n id: result?.lineId ?? null,\n [parentFkParam]: result?.[parentFkParam] ?? null,\n }),\n status: 201,\n },\n update: {\n commandId: `${commandPrefix}.upsert`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }: { raw: unknown; ctx: CrudCtx }) => {\n const { translate } = await resolveTranslations()\n const payload = upsertSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n )\n return { body: payload }\n },\n response: ({ result }: { result: Record<string, unknown> | null }) => ({\n id: result?.lineId ?? null,\n [parentFkParam]: result?.[parentFkParam] ?? null,\n }),\n },\n delete: {\n commandId: `${commandPrefix}.delete`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }: { raw: unknown; ctx: CrudCtx }) => {\n const { translate } = await resolveTranslations()\n const payload = deleteSchema.parse(\n withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),\n )\n if (!payload.id || !payload[parentFkParam]) {\n throw new CrudHttpError(400, {\n error: translate(\n 'sales.documents.detail.error',\n 'Document not found or inaccessible.',\n ),\n })\n }\n return { body: payload }\n },\n response: () => ({ ok: true }),\n },\n },\n })\n\n const lineItemSchema = z.object({\n id: z.string().uuid(),\n [parentFkColumn]: z.string().uuid(),\n line_number: z.number(),\n kind: z.string(),\n status_entry_id: z.string().uuid().nullable().optional(),\n status: z.string().nullable().optional(),\n product_id: z.string().uuid().nullable().optional(),\n product_variant_id: z.string().uuid().nullable().optional(),\n catalog_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n name: z.string().nullable().optional(),\n description: z.string().nullable().optional(),\n comment: z.string().nullable().optional(),\n quantity: z.number(),\n quantity_unit: z.string().nullable().optional(),\n normalized_quantity: z.number(),\n normalized_unit: z.string().nullable().optional(),\n uom_snapshot: uomSnapshotOpenApiSchema,\n currency_code: z.string(),\n unit_price_net: z.number(),\n unit_price_gross: z.number(),\n discount_amount: z.number(),\n discount_percent: z.number(),\n tax_rate: z.number(),\n tax_amount: z.number(),\n total_net_amount: z.number(),\n total_gross_amount: z.number(),\n configuration: z.record(z.string(), z.unknown()).nullable().optional(),\n promotion_code: z.string().nullable().optional(),\n promotion_snapshot: z.record(z.string(), z.unknown()).nullable().optional(),\n metadata: z.record(z.string(), z.unknown()).nullable().optional(),\n custom_field_set_id: z.string().uuid().nullable().optional(),\n created_at: z.string(),\n updated_at: z.string(),\n })\n\n const upsertResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n [parentFkParam]: z.string().uuid().nullable(),\n })\n\n const openApi = createSalesCrudOpenApi({\n resourceName: config.openApi.resourceName,\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(lineItemSchema),\n create: {\n schema: upsertSchema,\n responseSchema: upsertResponseSchema,\n description: `Creates ${config.openApi.description}.`,\n },\n update: {\n schema: upsertSchema,\n responseSchema: upsertResponseSchema,\n description: `Updates ${config.openApi.description}.`,\n },\n del: {\n schema: deleteSchema,\n responseSchema: defaultOkResponseSchema,\n description: `Deletes ${config.openApi.description}.`,\n },\n })\n\n return {\n metadata: routeMetadata,\n openApi,\n GET: crud.GET,\n POST: crud.POST,\n PUT: crud.PUT,\n DELETE: crud.DELETE,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,23 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+ class Migration20260309073310 extends Migration {
3
+ async up() {
4
+ this.addSql(`create table "sales_returns" ("id" uuid not null default gen_random_uuid(), "order_id" uuid not null, "organization_id" uuid not null, "tenant_id" uuid not null, "return_number" text not null, "status_entry_id" uuid null, "status" text null, "reason" text null, "notes" text null, "returned_at" timestamptz null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, constraint "sales_returns_pkey" primary key ("id"));`);
5
+ this.addSql(`create index "sales_returns_status_idx" on "sales_returns" ("organization_id", "tenant_id", "status");`);
6
+ this.addSql(`create index "sales_returns_scope_idx" on "sales_returns" ("order_id", "organization_id", "tenant_id");`);
7
+ this.addSql(`alter table "sales_returns" add constraint "sales_returns_number_unique" unique ("organization_id", "tenant_id", "return_number");`);
8
+ this.addSql(`create table "sales_return_lines" ("id" uuid not null default gen_random_uuid(), "return_id" uuid not null, "order_line_id" uuid not null, "organization_id" uuid not null, "tenant_id" uuid not null, "quantity_returned" numeric(18,4) not null default '0', "unit_price_net" numeric(18,4) not null default '0', "unit_price_gross" numeric(18,4) not null default '0', "total_net_amount" numeric(18,4) not null default '0', "total_gross_amount" numeric(18,4) not null default '0', "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, constraint "sales_return_lines_pkey" primary key ("id"));`);
9
+ this.addSql(`create index "sales_return_lines_order_line_idx" on "sales_return_lines" ("order_line_id", "organization_id", "tenant_id");`);
10
+ this.addSql(`create index "sales_return_lines_return_idx" on "sales_return_lines" ("return_id", "organization_id", "tenant_id");`);
11
+ this.addSql(`alter table "sales_returns" add constraint "sales_returns_order_id_foreign" foreign key ("order_id") references "sales_orders" ("id") on update cascade;`);
12
+ this.addSql(`alter table "sales_return_lines" add constraint "sales_return_lines_return_id_foreign" foreign key ("return_id") references "sales_returns" ("id") on update cascade;`);
13
+ this.addSql(`alter table "sales_return_lines" add constraint "sales_return_lines_order_line_id_foreign" foreign key ("order_line_id") references "sales_order_lines" ("id") on update cascade;`);
14
+ }
15
+ async down() {
16
+ this.addSql(`drop table if exists "sales_return_lines" cascade;`);
17
+ this.addSql(`drop table if exists "sales_returns" cascade;`);
18
+ }
19
+ }
20
+ export {
21
+ Migration20260309073310
22
+ };
23
+ //# sourceMappingURL=Migration20260309073310.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/sales/migrations/Migration20260309073310.ts"],
4
+ "sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260309073310 extends Migration {\n\n override async up(): Promise<void> {\n this.addSql(`create table \"sales_returns\" (\"id\" uuid not null default gen_random_uuid(), \"order_id\" uuid not null, \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"return_number\" text not null, \"status_entry_id\" uuid null, \"status\" text null, \"reason\" text null, \"notes\" text null, \"returned_at\" timestamptz null, \"created_at\" timestamptz not null, \"updated_at\" timestamptz not null, \"deleted_at\" timestamptz null, constraint \"sales_returns_pkey\" primary key (\"id\"));`);\n this.addSql(`create index \"sales_returns_status_idx\" on \"sales_returns\" (\"organization_id\", \"tenant_id\", \"status\");`);\n this.addSql(`create index \"sales_returns_scope_idx\" on \"sales_returns\" (\"order_id\", \"organization_id\", \"tenant_id\");`);\n this.addSql(`alter table \"sales_returns\" add constraint \"sales_returns_number_unique\" unique (\"organization_id\", \"tenant_id\", \"return_number\");`);\n\n this.addSql(`create table \"sales_return_lines\" (\"id\" uuid not null default gen_random_uuid(), \"return_id\" uuid not null, \"order_line_id\" uuid not null, \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"quantity_returned\" numeric(18,4) not null default '0', \"unit_price_net\" numeric(18,4) not null default '0', \"unit_price_gross\" numeric(18,4) not null default '0', \"total_net_amount\" numeric(18,4) not null default '0', \"total_gross_amount\" numeric(18,4) not null default '0', \"created_at\" timestamptz not null, \"updated_at\" timestamptz not null, \"deleted_at\" timestamptz null, constraint \"sales_return_lines_pkey\" primary key (\"id\"));`);\n this.addSql(`create index \"sales_return_lines_order_line_idx\" on \"sales_return_lines\" (\"order_line_id\", \"organization_id\", \"tenant_id\");`);\n this.addSql(`create index \"sales_return_lines_return_idx\" on \"sales_return_lines\" (\"return_id\", \"organization_id\", \"tenant_id\");`);\n\n this.addSql(`alter table \"sales_returns\" add constraint \"sales_returns_order_id_foreign\" foreign key (\"order_id\") references \"sales_orders\" (\"id\") on update cascade;`);\n\n this.addSql(`alter table \"sales_return_lines\" add constraint \"sales_return_lines_return_id_foreign\" foreign key (\"return_id\") references \"sales_returns\" (\"id\") on update cascade;`);\n this.addSql(`alter table \"sales_return_lines\" add constraint \"sales_return_lines_order_line_id_foreign\" foreign key (\"order_line_id\") references \"sales_order_lines\" (\"id\") on update cascade;`);\n }\n\n override async down(): Promise<void> {\n this.addSql(`drop table if exists \"sales_return_lines\" cascade;`);\n this.addSql(`drop table if exists \"sales_returns\" cascade;`);\n }\n\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,gCAAgC,UAAU;AAAA,EAErD,MAAe,KAAoB;AACjC,SAAK,OAAO,odAAod;AAChe,SAAK,OAAO,wGAAwG;AACpH,SAAK,OAAO,yGAAyG;AACrH,SAAK,OAAO,oIAAoI;AAEhJ,SAAK,OAAO,2nBAA2nB;AACvoB,SAAK,OAAO,6HAA6H;AACzI,SAAK,OAAO,qHAAqH;AAEjI,SAAK,OAAO,0JAA0J;AAEtK,SAAK,OAAO,uKAAuK;AACnL,SAAK,OAAO,mLAAmL;AAAA,EACjM;AAAA,EAEA,MAAe,OAAsB;AACnC,SAAK,OAAO,oDAAoD;AAChE,SAAK,OAAO,+CAA+C;AAAA,EAC7D;AAEF;",
6
+ "names": []
7
+ }
@@ -2,7 +2,8 @@ import { randomBytes, randomUUID } from "crypto";
2
2
  import { SalesDocumentSequence, SalesSettings } from "../data/entities.js";
3
3
  import {
4
4
  DEFAULT_ORDER_NUMBER_FORMAT,
5
- DEFAULT_QUOTE_NUMBER_FORMAT
5
+ DEFAULT_QUOTE_NUMBER_FORMAT,
6
+ DEFAULT_RETURN_NUMBER_FORMAT
6
7
  } from "../lib/documentNumberTokens.js";
7
8
  const MAX_SEQUENCE = 1e9;
8
9
  const DEFAULT_SEQUENCE_START = 1;
@@ -42,11 +43,12 @@ class SalesDocumentNumberGenerator {
42
43
  };
43
44
  }
44
45
  async peekSequences(scope) {
45
- const [order, quote] = await Promise.all([
46
+ const [order, quote, salesReturn] = await Promise.all([
46
47
  this.peekNextSequence("order", scope),
47
- this.peekNextSequence("quote", scope)
48
+ this.peekNextSequence("quote", scope),
49
+ this.peekNextSequence("return", scope)
48
50
  ]);
49
- return { order, quote };
51
+ return { order, quote, return: salesReturn };
50
52
  }
51
53
  async setNextSequence(kind, scope, nextValue) {
52
54
  const next = Math.min(Math.max(Math.floor(nextValue), DEFAULT_SEQUENCE_START), MAX_SEQUENCE);
@@ -63,7 +65,7 @@ class SalesDocumentNumberGenerator {
63
65
  }
64
66
  async generate(params) {
65
67
  const settings = await this.getSettings(params);
66
- const format = params.format?.trim() || (params.kind === "order" ? settings.orderNumberFormat : settings.quoteNumberFormat);
68
+ const format = params.format?.trim() || (params.kind === "order" ? settings.orderNumberFormat : params.kind === "quote" ? settings.quoteNumberFormat : DEFAULT_RETURN_NUMBER_FORMAT);
67
69
  const sequence = await this.claimSequence(params.kind, params);
68
70
  const number = this.formatNumber(format, {
69
71
  kind: params.kind,
@@ -100,7 +102,7 @@ class SalesDocumentNumberGenerator {
100
102
  return Math.min(value, MAX_SEQUENCE);
101
103
  }
102
104
  formatNumber(template, context) {
103
- const source = template?.trim() || (context.kind === "order" ? DEFAULT_ORDER_NUMBER_FORMAT : DEFAULT_QUOTE_NUMBER_FORMAT);
105
+ const source = template?.trim() || (context.kind === "order" ? DEFAULT_ORDER_NUMBER_FORMAT : context.kind === "quote" ? DEFAULT_QUOTE_NUMBER_FORMAT : DEFAULT_RETURN_NUMBER_FORMAT);
104
106
  const now = context.date;
105
107
  return source.replace(/\{([a-zA-Z]+)(?::([^}]+))?\}/g, (match, rawToken, rawArg) => {
106
108
  const token = rawToken.toLowerCase();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sales/services/salesDocumentNumberGenerator.ts"],
4
- "sourcesContent": ["import { randomBytes, randomUUID } from 'crypto'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { SalesDocumentSequence, SalesSettings } from '../data/entities'\nimport {\n DEFAULT_ORDER_NUMBER_FORMAT,\n DEFAULT_QUOTE_NUMBER_FORMAT,\n type SalesDocumentNumberKind,\n} from '../lib/documentNumberTokens'\n\ntype Scope = {\n organizationId: string\n tenantId: string\n}\n\ntype GenerateParams = Scope & {\n kind: SalesDocumentNumberKind\n format?: string | null\n}\n\ntype SettingsSnapshot = {\n orderNumberFormat: string\n quoteNumberFormat: string\n}\n\ntype SequenceSnapshot = {\n order: number\n quote: number\n}\n\nconst MAX_SEQUENCE = 1_000_000_000\nconst DEFAULT_SEQUENCE_START = 1\n\nconst createNanoId = (size = 12) => {\n const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'\n const maxValid = 256 - (256 % alphabet.length) // 248 for alphabet.length=62\n let id = ''\n while (id.length < size) {\n const byte = randomBytes(1)[0]\n if (byte >= maxValid) continue // rejection sampling to avoid bias\n id += alphabet[byte % alphabet.length]\n }\n return id\n}\n\nconst generateRandomDigits = (size = 4) => {\n const length = Math.max(1, Math.min(size, 12))\n const digits: string[] = []\n while (digits.length < length) {\n const byte = randomBytes(1)[0]\n if (byte >= 250) continue // rejection sampling: only use 0-249 which divides evenly by 10\n digits.push((byte % 10).toString())\n }\n return digits.join('')\n}\n\nexport class SalesDocumentNumberGenerator {\n constructor(private readonly em: EntityManager) {}\n\n async getSettings(scope: Scope): Promise<SettingsSnapshot> {\n const record = await this.em.findOne(SalesSettings, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n return {\n orderNumberFormat: record?.orderNumberFormat?.trim() || DEFAULT_ORDER_NUMBER_FORMAT,\n quoteNumberFormat: record?.quoteNumberFormat?.trim() || DEFAULT_QUOTE_NUMBER_FORMAT,\n }\n }\n\n async peekSequences(scope: Scope): Promise<SequenceSnapshot> {\n const [order, quote] = await Promise.all([\n this.peekNextSequence('order', scope),\n this.peekNextSequence('quote', scope),\n ])\n return { order, quote }\n }\n\n async setNextSequence(kind: SalesDocumentNumberKind, scope: Scope, nextValue: number): Promise<void> {\n const next = Math.min(Math.max(Math.floor(nextValue), DEFAULT_SEQUENCE_START), MAX_SEQUENCE)\n const baseValue = next - 1\n await this.em.getConnection().execute(\n `\n insert into sales_document_sequences (id, organization_id, tenant_id, document_kind, current_value, created_at, updated_at)\n values (gen_random_uuid(), ?, ?, ?, ?, now(), now())\n on conflict (organization_id, tenant_id, document_kind)\n do update set current_value = ?, updated_at = now()\n `,\n [scope.organizationId, scope.tenantId, kind, baseValue, baseValue]\n )\n }\n\n async generate(params: GenerateParams): Promise<{ number: string; format: string; sequence: number }> {\n const settings = await this.getSettings(params)\n const format =\n params.format?.trim() ||\n (params.kind === 'order' ? settings.orderNumberFormat : settings.quoteNumberFormat)\n const sequence = await this.claimSequence(params.kind, params)\n const number = this.formatNumber(format, {\n kind: params.kind,\n sequence,\n date: new Date(),\n guid: randomUUID(),\n })\n return { number, format, sequence }\n }\n\n private async peekNextSequence(kind: SalesDocumentNumberKind, scope: Scope): Promise<number> {\n const record = await this.em.findOne(SalesDocumentSequence, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n documentKind: kind,\n })\n if (record && typeof record.currentValue === 'number') {\n return Math.min(record.currentValue + 1, MAX_SEQUENCE)\n }\n return DEFAULT_SEQUENCE_START\n }\n\n private async claimSequence(kind: SalesDocumentNumberKind, scope: Scope): Promise<number> {\n const rows = await this.em.getConnection().execute<{ current_value: string }[]>(\n `\n insert into sales_document_sequences (id, organization_id, tenant_id, document_kind, current_value, created_at, updated_at)\n values (gen_random_uuid(), ?, ?, ?, ?, now(), now())\n on conflict (organization_id, tenant_id, document_kind)\n do update set current_value = sales_document_sequences.current_value + 1, updated_at = now()\n returning current_value\n `,\n [scope.organizationId, scope.tenantId, kind, DEFAULT_SEQUENCE_START]\n )\n const value = Number(rows?.[0]?.current_value ?? DEFAULT_SEQUENCE_START)\n if (!Number.isFinite(value) || value < DEFAULT_SEQUENCE_START) return DEFAULT_SEQUENCE_START\n return Math.min(value, MAX_SEQUENCE)\n }\n\n private formatNumber(\n template: string,\n context: { kind: SalesDocumentNumberKind; sequence: number; date: Date; guid?: string | null }\n ): string {\n const source =\n template?.trim() ||\n (context.kind === 'order' ? DEFAULT_ORDER_NUMBER_FORMAT : DEFAULT_QUOTE_NUMBER_FORMAT)\n const now = context.date\n return source.replace(/\\{([a-zA-Z]+)(?::([^}]+))?\\}/g, (match, rawToken, rawArg) => {\n const token = rawToken.toLowerCase()\n const arg = typeof rawArg === 'string' ? rawArg.trim() : ''\n switch (token) {\n case 'yyyy':\n return String(now.getFullYear())\n case 'yy':\n return String(now.getFullYear()).slice(-2)\n case 'mm':\n return String(now.getMonth() + 1).padStart(2, '0')\n case 'dd':\n return String(now.getDate()).padStart(2, '0')\n case 'hh':\n return String(now.getHours()).padStart(2, '0')\n case 'seq': {\n const requested = parseInt(arg || '', 10)\n const width = Number.isFinite(requested) && requested > 0 ? Math.min(requested, 12) : undefined\n return width ? String(context.sequence).padStart(width, '0') : String(context.sequence)\n }\n case 'rand': {\n const requested = parseInt(arg || '', 10)\n const length = Number.isFinite(requested) && requested > 0 ? requested : 4\n return generateRandomDigits(length)\n }\n case 'guid':\n return context.guid || randomUUID()\n case 'nanoid': {\n const requested = parseInt(arg || '', 10)\n const size =\n Number.isFinite(requested) && requested > 0 ? Math.min(requested, 32) : 12\n return createNanoId(size)\n }\n case 'kind':\n return context.kind\n default:\n return match\n }\n })\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { randomBytes, randomUUID } from 'crypto'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { SalesDocumentSequence, SalesSettings } from '../data/entities'\nimport {\n DEFAULT_ORDER_NUMBER_FORMAT,\n DEFAULT_QUOTE_NUMBER_FORMAT,\n DEFAULT_RETURN_NUMBER_FORMAT,\n type SalesDocumentNumberKind,\n} from '../lib/documentNumberTokens'\n\ntype Scope = {\n organizationId: string\n tenantId: string\n}\n\ntype GenerateParams = Scope & {\n kind: SalesDocumentNumberKind\n format?: string | null\n}\n\ntype SettingsSnapshot = {\n orderNumberFormat: string\n quoteNumberFormat: string\n}\n\ntype SequenceSnapshot = {\n order: number\n quote: number\n return: number\n}\n\nconst MAX_SEQUENCE = 1_000_000_000\nconst DEFAULT_SEQUENCE_START = 1\n\nconst createNanoId = (size = 12) => {\n const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'\n const maxValid = 256 - (256 % alphabet.length) // 248 for alphabet.length=62\n let id = ''\n while (id.length < size) {\n const byte = randomBytes(1)[0]\n if (byte >= maxValid) continue // rejection sampling to avoid bias\n id += alphabet[byte % alphabet.length]\n }\n return id\n}\n\nconst generateRandomDigits = (size = 4) => {\n const length = Math.max(1, Math.min(size, 12))\n const digits: string[] = []\n while (digits.length < length) {\n const byte = randomBytes(1)[0]\n if (byte >= 250) continue // rejection sampling: only use 0-249 which divides evenly by 10\n digits.push((byte % 10).toString())\n }\n return digits.join('')\n}\n\nexport class SalesDocumentNumberGenerator {\n constructor(private readonly em: EntityManager) {}\n\n async getSettings(scope: Scope): Promise<SettingsSnapshot> {\n const record = await this.em.findOne(SalesSettings, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n return {\n orderNumberFormat: record?.orderNumberFormat?.trim() || DEFAULT_ORDER_NUMBER_FORMAT,\n quoteNumberFormat: record?.quoteNumberFormat?.trim() || DEFAULT_QUOTE_NUMBER_FORMAT,\n }\n }\n\n async peekSequences(scope: Scope): Promise<SequenceSnapshot> {\n const [order, quote, salesReturn] = await Promise.all([\n this.peekNextSequence('order', scope),\n this.peekNextSequence('quote', scope),\n this.peekNextSequence('return', scope),\n ])\n return { order, quote, return: salesReturn }\n }\n\n async setNextSequence(kind: SalesDocumentNumberKind, scope: Scope, nextValue: number): Promise<void> {\n const next = Math.min(Math.max(Math.floor(nextValue), DEFAULT_SEQUENCE_START), MAX_SEQUENCE)\n const baseValue = next - 1\n await this.em.getConnection().execute(\n `\n insert into sales_document_sequences (id, organization_id, tenant_id, document_kind, current_value, created_at, updated_at)\n values (gen_random_uuid(), ?, ?, ?, ?, now(), now())\n on conflict (organization_id, tenant_id, document_kind)\n do update set current_value = ?, updated_at = now()\n `,\n [scope.organizationId, scope.tenantId, kind, baseValue, baseValue]\n )\n }\n\n async generate(params: GenerateParams): Promise<{ number: string; format: string; sequence: number }> {\n const settings = await this.getSettings(params)\n const format =\n params.format?.trim() ||\n (params.kind === 'order'\n ? settings.orderNumberFormat\n : params.kind === 'quote'\n ? settings.quoteNumberFormat\n : DEFAULT_RETURN_NUMBER_FORMAT)\n const sequence = await this.claimSequence(params.kind, params)\n const number = this.formatNumber(format, {\n kind: params.kind,\n sequence,\n date: new Date(),\n guid: randomUUID(),\n })\n return { number, format, sequence }\n }\n\n private async peekNextSequence(kind: SalesDocumentNumberKind, scope: Scope): Promise<number> {\n const record = await this.em.findOne(SalesDocumentSequence, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n documentKind: kind,\n })\n if (record && typeof record.currentValue === 'number') {\n return Math.min(record.currentValue + 1, MAX_SEQUENCE)\n }\n return DEFAULT_SEQUENCE_START\n }\n\n private async claimSequence(kind: SalesDocumentNumberKind, scope: Scope): Promise<number> {\n const rows = await this.em.getConnection().execute<{ current_value: string }[]>(\n `\n insert into sales_document_sequences (id, organization_id, tenant_id, document_kind, current_value, created_at, updated_at)\n values (gen_random_uuid(), ?, ?, ?, ?, now(), now())\n on conflict (organization_id, tenant_id, document_kind)\n do update set current_value = sales_document_sequences.current_value + 1, updated_at = now()\n returning current_value\n `,\n [scope.organizationId, scope.tenantId, kind, DEFAULT_SEQUENCE_START]\n )\n const value = Number(rows?.[0]?.current_value ?? DEFAULT_SEQUENCE_START)\n if (!Number.isFinite(value) || value < DEFAULT_SEQUENCE_START) return DEFAULT_SEQUENCE_START\n return Math.min(value, MAX_SEQUENCE)\n }\n\n private formatNumber(\n template: string,\n context: { kind: SalesDocumentNumberKind; sequence: number; date: Date; guid?: string | null }\n ): string {\n const source =\n template?.trim() ||\n (context.kind === 'order'\n ? DEFAULT_ORDER_NUMBER_FORMAT\n : context.kind === 'quote'\n ? DEFAULT_QUOTE_NUMBER_FORMAT\n : DEFAULT_RETURN_NUMBER_FORMAT)\n const now = context.date\n return source.replace(/\\{([a-zA-Z]+)(?::([^}]+))?\\}/g, (match, rawToken, rawArg) => {\n const token = rawToken.toLowerCase()\n const arg = typeof rawArg === 'string' ? rawArg.trim() : ''\n switch (token) {\n case 'yyyy':\n return String(now.getFullYear())\n case 'yy':\n return String(now.getFullYear()).slice(-2)\n case 'mm':\n return String(now.getMonth() + 1).padStart(2, '0')\n case 'dd':\n return String(now.getDate()).padStart(2, '0')\n case 'hh':\n return String(now.getHours()).padStart(2, '0')\n case 'seq': {\n const requested = parseInt(arg || '', 10)\n const width = Number.isFinite(requested) && requested > 0 ? Math.min(requested, 12) : undefined\n return width ? String(context.sequence).padStart(width, '0') : String(context.sequence)\n }\n case 'rand': {\n const requested = parseInt(arg || '', 10)\n const length = Number.isFinite(requested) && requested > 0 ? requested : 4\n return generateRandomDigits(length)\n }\n case 'guid':\n return context.guid || randomUUID()\n case 'nanoid': {\n const requested = parseInt(arg || '', 10)\n const size =\n Number.isFinite(requested) && requested > 0 ? Math.min(requested, 32) : 12\n return createNanoId(size)\n }\n case 'kind':\n return context.kind\n default:\n return match\n }\n })\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -57,7 +57,7 @@ const setup = {
57
57
  })
58
58
  );
59
59
  }
60
- for (const kind of ["order", "quote"]) {
60
+ for (const kind of ["order", "quote", "return"]) {
61
61
  const seq = await em.findOne(SalesDocumentSequence, {
62
62
  tenantId,
63
63
  organizationId,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/sales/setup.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\nimport { SalesSettings, SalesDocumentSequence, SalesTaxRate } from './data/entities'\nimport { DEFAULT_ORDER_NUMBER_FORMAT, DEFAULT_QUOTE_NUMBER_FORMAT } from './lib/documentNumberTokens'\nimport { seedSalesStatusDictionaries, seedSalesAdjustmentKinds } from './lib/dictionaries'\nimport { ensureExampleShippingMethods, ensureExamplePaymentMethods } from './seed/examples-data'\nimport { seedSalesExamples } from './seed/examples'\n\ntype SeedScope = { tenantId: string; organizationId: string }\n\nconst DEFAULT_TAX_RATES = [\n { code: 'vat-23', name: '23% VAT', rate: '23' },\n { code: 'vat-0', name: '0% VAT', rate: '0' },\n] as const\n\nasync function seedSalesTaxRates(em: EntityManager, scope: SeedScope): Promise<void> {\n await em.transactional(async (tem) => {\n const existing = await tem.find(SalesTaxRate, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n const existingCodes = new Set(existing.map((rate) => rate.code))\n const hasDefault = existing.some((rate) => rate.isDefault)\n const now = new Date()\n let isFirst = !hasDefault\n\n for (const seed of DEFAULT_TAX_RATES) {\n if (existingCodes.has(seed.code)) continue\n tem.persist(\n tem.create(SalesTaxRate, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n code: seed.code,\n name: seed.name,\n rate: seed.rate,\n priority: 0,\n isCompound: false,\n isDefault: isFirst,\n createdAt: now,\n updatedAt: now,\n })\n )\n isFirst = false\n }\n })\n}\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n admin: ['sales.*', 'sales.documents.number.edit'],\n employee: ['sales.*'],\n },\n\n async onTenantCreated({ em, tenantId, organizationId }) {\n const exists = await em.findOne(SalesSettings, { tenantId, organizationId })\n if (!exists) {\n em.persist(\n em.create(SalesSettings, {\n tenantId,\n organizationId,\n orderNumberFormat: DEFAULT_ORDER_NUMBER_FORMAT,\n quoteNumberFormat: DEFAULT_QUOTE_NUMBER_FORMAT,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n )\n }\n\n for (const kind of ['order', 'quote'] as const) {\n const seq = await em.findOne(SalesDocumentSequence, {\n tenantId,\n organizationId,\n documentKind: kind,\n })\n if (!seq) {\n em.persist(\n em.create(SalesDocumentSequence, {\n tenantId,\n organizationId,\n documentKind: kind,\n currentValue: 0,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n )\n }\n }\n\n await em.flush()\n },\n\n async seedDefaults({ em, tenantId, organizationId }) {\n const scope = { tenantId, organizationId }\n await seedSalesTaxRates(em, scope)\n await seedSalesStatusDictionaries(em, scope)\n await seedSalesAdjustmentKinds(em, scope)\n await ensureExampleShippingMethods(em, scope)\n await ensureExamplePaymentMethods(em, scope)\n },\n\n async seedExamples({ em, container, tenantId, organizationId }) {\n const scope = { tenantId, organizationId }\n await seedSalesExamples(em, container, scope)\n },\n}\n\nexport default setup\n"],
5
- "mappings": "AAEA,SAAS,eAAe,uBAAuB,oBAAoB;AACnE,SAAS,6BAA6B,mCAAmC;AACzE,SAAS,6BAA6B,gCAAgC;AACtE,SAAS,8BAA8B,mCAAmC;AAC1E,SAAS,yBAAyB;AAIlC,MAAM,oBAAoB;AAAA,EACxB,EAAE,MAAM,UAAU,MAAM,WAAW,MAAM,KAAK;AAAA,EAC9C,EAAE,MAAM,SAAS,MAAM,UAAU,MAAM,IAAI;AAC7C;AAEA,eAAe,kBAAkB,IAAmB,OAAiC;AACnF,QAAM,GAAG,cAAc,OAAO,QAAQ;AACpC,UAAM,WAAW,MAAM,IAAI,KAAK,cAAc;AAAA,MAC5C,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,WAAW;AAAA,IACb,CAAC;AACD,UAAM,gBAAgB,IAAI,IAAI,SAAS,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC;AAC/D,UAAM,aAAa,SAAS,KAAK,CAAC,SAAS,KAAK,SAAS;AACzD,UAAM,MAAM,oBAAI,KAAK;AACrB,QAAI,UAAU,CAAC;AAEf,eAAW,QAAQ,mBAAmB;AACpC,UAAI,cAAc,IAAI,KAAK,IAAI,EAAG;AAClC,UAAI;AAAA,QACF,IAAI,OAAO,cAAc;AAAA,UACvB,UAAU,MAAM;AAAA,UAChB,gBAAgB,MAAM;AAAA,UACtB,MAAM,KAAK;AAAA,UACX,MAAM,KAAK;AAAA,UACX,MAAM,KAAK;AAAA,UACX,UAAU;AAAA,UACV,YAAY;AAAA,UACZ,WAAW;AAAA,UACX,WAAW;AAAA,UACX,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AACA,gBAAU;AAAA,IACZ;AAAA,EACF,CAAC;AACH;AAEO,MAAM,QAA2B;AAAA,EACtC,qBAAqB;AAAA,IACnB,OAAO,CAAC,WAAW,6BAA6B;AAAA,IAChD,UAAU,CAAC,SAAS;AAAA,EACtB;AAAA,EAEA,MAAM,gBAAgB,EAAE,IAAI,UAAU,eAAe,GAAG;AACtD,UAAM,SAAS,MAAM,GAAG,QAAQ,eAAe,EAAE,UAAU,eAAe,CAAC;AAC3E,QAAI,CAAC,QAAQ;AACX,SAAG;AAAA,QACD,GAAG,OAAO,eAAe;AAAA,UACvB;AAAA,UACA;AAAA,UACA,mBAAmB;AAAA,UACnB,mBAAmB;AAAA,UACnB,WAAW,oBAAI,KAAK;AAAA,UACpB,WAAW,oBAAI,KAAK;AAAA,QACtB,CAAC;AAAA,MACH;AAAA,IACF;AAEA,eAAW,QAAQ,CAAC,SAAS,OAAO,GAAY;AAC9C,YAAM,MAAM,MAAM,GAAG,QAAQ,uBAAuB;AAAA,QAClD;AAAA,QACA;AAAA,QACA,cAAc;AAAA,MAChB,CAAC;AACD,UAAI,CAAC,KAAK;AACR,WAAG;AAAA,UACD,GAAG,OAAO,uBAAuB;AAAA,YAC/B;AAAA,YACA;AAAA,YACA,cAAc;AAAA,YACd,cAAc;AAAA,YACd,WAAW,oBAAI,KAAK;AAAA,YACpB,WAAW,oBAAI,KAAK;AAAA,UACtB,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,UAAM,GAAG,MAAM;AAAA,EACjB;AAAA,EAEA,MAAM,aAAa,EAAE,IAAI,UAAU,eAAe,GAAG;AACnD,UAAM,QAAQ,EAAE,UAAU,eAAe;AACzC,UAAM,kBAAkB,IAAI,KAAK;AACjC,UAAM,4BAA4B,IAAI,KAAK;AAC3C,UAAM,yBAAyB,IAAI,KAAK;AACxC,UAAM,6BAA6B,IAAI,KAAK;AAC5C,UAAM,4BAA4B,IAAI,KAAK;AAAA,EAC7C;AAAA,EAEA,MAAM,aAAa,EAAE,IAAI,WAAW,UAAU,eAAe,GAAG;AAC9D,UAAM,QAAQ,EAAE,UAAU,eAAe;AACzC,UAAM,kBAAkB,IAAI,WAAW,KAAK;AAAA,EAC9C;AACF;AAEA,IAAO,gBAAQ;",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'\nimport { SalesSettings, SalesDocumentSequence, SalesTaxRate } from './data/entities'\nimport { DEFAULT_ORDER_NUMBER_FORMAT, DEFAULT_QUOTE_NUMBER_FORMAT } from './lib/documentNumberTokens'\nimport { seedSalesStatusDictionaries, seedSalesAdjustmentKinds } from './lib/dictionaries'\nimport { ensureExampleShippingMethods, ensureExamplePaymentMethods } from './seed/examples-data'\nimport { seedSalesExamples } from './seed/examples'\n\ntype SeedScope = { tenantId: string; organizationId: string }\n\nconst DEFAULT_TAX_RATES = [\n { code: 'vat-23', name: '23% VAT', rate: '23' },\n { code: 'vat-0', name: '0% VAT', rate: '0' },\n] as const\n\nasync function seedSalesTaxRates(em: EntityManager, scope: SeedScope): Promise<void> {\n await em.transactional(async (tem) => {\n const existing = await tem.find(SalesTaxRate, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n const existingCodes = new Set(existing.map((rate) => rate.code))\n const hasDefault = existing.some((rate) => rate.isDefault)\n const now = new Date()\n let isFirst = !hasDefault\n\n for (const seed of DEFAULT_TAX_RATES) {\n if (existingCodes.has(seed.code)) continue\n tem.persist(\n tem.create(SalesTaxRate, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n code: seed.code,\n name: seed.name,\n rate: seed.rate,\n priority: 0,\n isCompound: false,\n isDefault: isFirst,\n createdAt: now,\n updatedAt: now,\n })\n )\n isFirst = false\n }\n })\n}\n\nexport const setup: ModuleSetupConfig = {\n defaultRoleFeatures: {\n admin: ['sales.*', 'sales.documents.number.edit'],\n employee: ['sales.*'],\n },\n\n async onTenantCreated({ em, tenantId, organizationId }) {\n const exists = await em.findOne(SalesSettings, { tenantId, organizationId })\n if (!exists) {\n em.persist(\n em.create(SalesSettings, {\n tenantId,\n organizationId,\n orderNumberFormat: DEFAULT_ORDER_NUMBER_FORMAT,\n quoteNumberFormat: DEFAULT_QUOTE_NUMBER_FORMAT,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n )\n }\n\n for (const kind of ['order', 'quote', 'return'] as const) {\n const seq = await em.findOne(SalesDocumentSequence, {\n tenantId,\n organizationId,\n documentKind: kind,\n })\n if (!seq) {\n em.persist(\n em.create(SalesDocumentSequence, {\n tenantId,\n organizationId,\n documentKind: kind,\n currentValue: 0,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n )\n }\n }\n\n await em.flush()\n },\n\n async seedDefaults({ em, tenantId, organizationId }) {\n const scope = { tenantId, organizationId }\n await seedSalesTaxRates(em, scope)\n await seedSalesStatusDictionaries(em, scope)\n await seedSalesAdjustmentKinds(em, scope)\n await ensureExampleShippingMethods(em, scope)\n await ensureExamplePaymentMethods(em, scope)\n },\n\n async seedExamples({ em, container, tenantId, organizationId }) {\n const scope = { tenantId, organizationId }\n await seedSalesExamples(em, container, scope)\n },\n}\n\nexport default setup\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -182,23 +182,32 @@ const DocumentHistoryWidget = ({ context }) => {
182
182
  const [statusMap, setStatusMap] = React.useState({});
183
183
  const [filter, setFilter] = React.useState("all");
184
184
  React.useEffect(() => {
185
- apiCall("/api/sales/order-statuses?pageSize=100").then((res) => {
186
- if (res.ok && Array.isArray(res.result?.items)) {
187
- const map = {};
188
- for (const item of res.result.items) {
189
- if (!item || typeof item !== "object") continue;
190
- const d = item;
191
- const value = typeof d.value === "string" ? d.value : null;
192
- if (!value) continue;
193
- map[value] = {
194
- value,
195
- label: typeof d.label === "string" && d.label.length ? d.label : value,
196
- color: typeof d.color === "string" && d.color.length ? d.color : null,
197
- icon: typeof d.icon === "string" && d.icon.length ? d.icon : null
198
- };
199
- }
200
- setStatusMap(map);
185
+ const urls = [
186
+ "/api/sales/order-statuses?pageSize=100",
187
+ "/api/sales/shipment-statuses?pageSize=100",
188
+ "/api/sales/payment-statuses?pageSize=100"
189
+ ];
190
+ const map = {};
191
+ const merge = (items) => {
192
+ if (!Array.isArray(items)) return;
193
+ for (const item of items) {
194
+ if (!item || typeof item !== "object") continue;
195
+ const d = item;
196
+ const value = typeof d.value === "string" ? d.value : null;
197
+ if (!value) continue;
198
+ map[value] = {
199
+ value,
200
+ label: typeof d.label === "string" && d.label.length ? d.label : value,
201
+ color: typeof d.color === "string" && d.color.length ? d.color : null,
202
+ icon: typeof d.icon === "string" && d.icon.length ? d.icon : null
203
+ };
204
+ }
205
+ };
206
+ Promise.all(urls.map((url) => apiCall(url))).then((responses) => {
207
+ for (const res of responses) {
208
+ if (res.ok && Array.isArray(res.result?.items)) merge(res.result.items);
201
209
  }
210
+ setStatusMap(map);
202
211
  }).catch(() => {
203
212
  });
204
213
  }, []);