@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.
- package/dist/generated/entities/sales_return/index.js +31 -0
- package/dist/generated/entities/sales_return/index.js.map +7 -0
- package/dist/generated/entities/sales_return_line/index.js +29 -0
- package/dist/generated/entities/sales_return_line/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +2 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +4 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/sales/acl.js +2 -0
- package/dist/modules/sales/acl.js.map +2 -2
- package/dist/modules/sales/api/document-history/route.js +20 -2
- package/dist/modules/sales/api/document-history/route.js.map +2 -2
- package/dist/modules/sales/api/documents/factory.js +34 -0
- package/dist/modules/sales/api/documents/factory.js.map +2 -2
- package/dist/modules/sales/api/returns/[id]/route.js +147 -0
- package/dist/modules/sales/api/returns/[id]/route.js.map +7 -0
- package/dist/modules/sales/api/returns/route.js +158 -0
- package/dist/modules/sales/api/returns/route.js.map +7 -0
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +20 -1
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/commands/index.js +1 -0
- package/dist/modules/sales/commands/index.js.map +2 -2
- package/dist/modules/sales/commands/returns.js +467 -0
- package/dist/modules/sales/commands/returns.js.map +7 -0
- package/dist/modules/sales/components/documents/ReturnDialog.js +176 -0
- package/dist/modules/sales/components/documents/ReturnDialog.js.map +7 -0
- package/dist/modules/sales/components/documents/ReturnsSection.js +188 -0
- package/dist/modules/sales/components/documents/ReturnsSection.js.map +7 -0
- package/dist/modules/sales/data/entities.js +115 -1
- package/dist/modules/sales/data/entities.js.map +2 -2
- package/dist/modules/sales/data/validators.js +13 -0
- package/dist/modules/sales/data/validators.js.map +2 -2
- package/dist/modules/sales/events.js +4 -0
- package/dist/modules/sales/events.js.map +2 -2
- package/dist/modules/sales/lib/calculations.js +7 -0
- package/dist/modules/sales/lib/calculations.js.map +2 -2
- package/dist/modules/sales/lib/dictionaries.js +1 -0
- package/dist/modules/sales/lib/dictionaries.js.map +2 -2
- package/dist/modules/sales/lib/documentNumberTokens.js +2 -0
- package/dist/modules/sales/lib/documentNumberTokens.js.map +2 -2
- package/dist/modules/sales/lib/historyHelpers.js +15 -7
- package/dist/modules/sales/lib/historyHelpers.js.map +2 -2
- package/dist/modules/sales/lib/makeSalesLineRoute.js +42 -37
- package/dist/modules/sales/lib/makeSalesLineRoute.js.map +2 -2
- package/dist/modules/sales/migrations/Migration20260309073310.js +23 -0
- package/dist/modules/sales/migrations/Migration20260309073310.js.map +7 -0
- package/dist/modules/sales/services/salesDocumentNumberGenerator.js +8 -6
- package/dist/modules/sales/services/salesDocumentNumberGenerator.js.map +2 -2
- package/dist/modules/sales/setup.js +1 -1
- package/dist/modules/sales/setup.js.map +2 -2
- package/dist/modules/sales/widgets/injection/document-history/widget.client.js +25 -16
- package/dist/modules/sales/widgets/injection/document-history/widget.client.js.map +2 -2
- package/generated/entities/sales_return/index.ts +14 -0
- package/generated/entities/sales_return_line/index.ts +13 -0
- package/generated/entities.ids.generated.ts +2 -0
- package/generated/entity-fields-registry.ts +4 -0
- package/package.json +3 -3
- package/src/modules/sales/AGENTS.md +1 -0
- package/src/modules/sales/acl.ts +2 -0
- package/src/modules/sales/api/document-history/route.ts +25 -1
- package/src/modules/sales/api/documents/factory.ts +35 -0
- package/src/modules/sales/api/returns/[id]/route.ts +156 -0
- package/src/modules/sales/api/returns/route.ts +171 -0
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +18 -0
- package/src/modules/sales/commands/index.ts +1 -0
- package/src/modules/sales/commands/returns.ts +540 -0
- package/src/modules/sales/components/documents/ReturnDialog.tsx +216 -0
- package/src/modules/sales/components/documents/ReturnsSection.tsx +270 -0
- package/src/modules/sales/data/entities.ts +99 -3
- package/src/modules/sales/data/validators.ts +16 -0
- package/src/modules/sales/events.ts +5 -0
- package/src/modules/sales/i18n/de.json +32 -0
- package/src/modules/sales/i18n/en.json +32 -0
- package/src/modules/sales/i18n/es.json +32 -0
- package/src/modules/sales/i18n/pl.json +32 -0
- package/src/modules/sales/lib/calculations.ts +9 -0
- package/src/modules/sales/lib/dictionaries.ts +1 -0
- package/src/modules/sales/lib/documentNumberTokens.ts +2 -1
- package/src/modules/sales/lib/historyHelpers.ts +20 -9
- package/src/modules/sales/lib/makeSalesLineRoute.ts +42 -37
- package/src/modules/sales/migrations/.snapshot-open-mercato.json +398 -0
- package/src/modules/sales/migrations/Migration20260309073310.ts +26 -0
- package/src/modules/sales/services/salesDocumentNumberGenerator.ts +15 -4
- package/src/modules/sales/setup.ts +1 -1
- 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;
|
|
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
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
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,
|
|
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": "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,UAAM,cAAc,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;AACtE,UAAM,oBAAoB,OAAO,MAAM,uBAAuB,WAAW,MAAM,qBAAqB;AACpG,UAAM,gBAAgB,OAAO,MAAM,mBAAmB,WAAW,MAAM,iBAAiB;AACxF,WAAO,eAAe,qBAAqB,iBAAiB;AAAA,EAC9D;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;AACP,QAAM,QAAQ,0BAA0B,IAAI,aAAa;AACzD,QAAM,SAAS,IAAI,iBACf,0BAA0B,IAAI,cAAc,IAC5C;AAEJ,MAAI,CAAC,IAAI,gBAAgB;AACvB,WAAO,EAAE,YAAY,MAAM,UAAU,SAAS,KAAK;AAAA,EACrD;AACA,MAAI,WAAW,UAAU,WAAW,QAAQ,UAAU,OAAO;AAC3D,WAAO,EAAE,YAAY,QAAQ,UAAU,SAAS,KAAK;AAAA,EACvD;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,UAAM,kBAAkB,aAAa,cAAc,QAAQ,aAAa,YAAY;AACpF,QAAI,iBAAiB;AACnB,kBAAY;AACZ,eAAS,aAAa,YAAY;AAClC,iBAAW,EAAE,GAAG,UAAU,YAAY,aAAa,YAAY,UAAU,aAAa,SAAS;AAAA,IACjG;AAAA,EAEF;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;",
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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'
|
|
5
|
-
"mappings": "AAAA,SAAS,aAAa,kBAAkB;AAExC,SAAS,uBAAuB,qBAAqB;AACrD;AAAA,EACE;AAAA,EACA;AAAA,OAEK;
|
|
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": "AAAA,SAAS,aAAa,kBAAkB;AAExC,SAAS,uBAAuB,qBAAqB;AACrD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAuBP,MAAM,eAAe;AACrB,MAAM,yBAAyB;AAE/B,MAAM,eAAe,CAAC,OAAO,OAAO;AAClC,QAAM,WAAW;AACjB,QAAM,WAAW,MAAO,MAAM,SAAS;AACvC,MAAI,KAAK;AACT,SAAO,GAAG,SAAS,MAAM;AACvB,UAAM,OAAO,YAAY,CAAC,EAAE,CAAC;AAC7B,QAAI,QAAQ,SAAU;AACtB,UAAM,SAAS,OAAO,SAAS,MAAM;AAAA,EACvC;AACA,SAAO;AACT;AAEA,MAAM,uBAAuB,CAAC,OAAO,MAAM;AACzC,QAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,MAAM,EAAE,CAAC;AAC7C,QAAM,SAAmB,CAAC;AAC1B,SAAO,OAAO,SAAS,QAAQ;AAC7B,UAAM,OAAO,YAAY,CAAC,EAAE,CAAC;AAC7B,QAAI,QAAQ,IAAK;AACjB,WAAO,MAAM,OAAO,IAAI,SAAS,CAAC;AAAA,EACpC;AACA,SAAO,OAAO,KAAK,EAAE;AACvB;AAEO,MAAM,6BAA6B;AAAA,EACxC,YAA6B,IAAmB;AAAnB;AAAA,EAAoB;AAAA,EAEjD,MAAM,YAAY,OAAyC;AACzD,UAAM,SAAS,MAAM,KAAK,GAAG,QAAQ,eAAe;AAAA,MAClD,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,IACxB,CAAC;AACD,WAAO;AAAA,MACL,mBAAmB,QAAQ,mBAAmB,KAAK,KAAK;AAAA,MACxD,mBAAmB,QAAQ,mBAAmB,KAAK,KAAK;AAAA,IAC1D;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,OAAyC;AAC3D,UAAM,CAAC,OAAO,OAAO,WAAW,IAAI,MAAM,QAAQ,IAAI;AAAA,MACpD,KAAK,iBAAiB,SAAS,KAAK;AAAA,MACpC,KAAK,iBAAiB,SAAS,KAAK;AAAA,MACpC,KAAK,iBAAiB,UAAU,KAAK;AAAA,IACvC,CAAC;AACD,WAAO,EAAE,OAAO,OAAO,QAAQ,YAAY;AAAA,EAC7C;AAAA,EAEA,MAAM,gBAAgB,MAA+B,OAAc,WAAkC;AACnG,UAAM,OAAO,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,SAAS,GAAG,sBAAsB,GAAG,YAAY;AAC3F,UAAM,YAAY,OAAO;AACzB,UAAM,KAAK,GAAG,cAAc,EAAE;AAAA,MAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,CAAC,MAAM,gBAAgB,MAAM,UAAU,MAAM,WAAW,SAAS;AAAA,IACnE;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,QAAuF;AACpG,UAAM,WAAW,MAAM,KAAK,YAAY,MAAM;AAC9C,UAAM,SACJ,OAAO,QAAQ,KAAK,MACnB,OAAO,SAAS,UACb,SAAS,oBACT,OAAO,SAAS,UACd,SAAS,oBACT;AACR,UAAM,WAAW,MAAM,KAAK,cAAc,OAAO,MAAM,MAAM;AAC7D,UAAM,SAAS,KAAK,aAAa,QAAQ;AAAA,MACvC,MAAM,OAAO;AAAA,MACb;AAAA,MACA,MAAM,oBAAI,KAAK;AAAA,MACf,MAAM,WAAW;AAAA,IACnB,CAAC;AACD,WAAO,EAAE,QAAQ,QAAQ,SAAS;AAAA,EACpC;AAAA,EAEA,MAAc,iBAAiB,MAA+B,OAA+B;AAC3F,UAAM,SAAS,MAAM,KAAK,GAAG,QAAQ,uBAAuB;AAAA,MAC1D,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,cAAc;AAAA,IAChB,CAAC;AACD,QAAI,UAAU,OAAO,OAAO,iBAAiB,UAAU;AACrD,aAAO,KAAK,IAAI,OAAO,eAAe,GAAG,YAAY;AAAA,IACvD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,cAAc,MAA+B,OAA+B;AACxF,UAAM,OAAO,MAAM,KAAK,GAAG,cAAc,EAAE;AAAA,MACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOA,CAAC,MAAM,gBAAgB,MAAM,UAAU,MAAM,sBAAsB;AAAA,IACrE;AACA,UAAM,QAAQ,OAAO,OAAO,CAAC,GAAG,iBAAiB,sBAAsB;AACvE,QAAI,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,uBAAwB,QAAO;AACtE,WAAO,KAAK,IAAI,OAAO,YAAY;AAAA,EACrC;AAAA,EAEQ,aACN,UACA,SACQ;AACR,UAAM,SACJ,UAAU,KAAK,MACd,QAAQ,SAAS,UACd,8BACA,QAAQ,SAAS,UACf,8BACA;AACR,UAAM,MAAM,QAAQ;AACpB,WAAO,OAAO,QAAQ,iCAAiC,CAAC,OAAO,UAAU,WAAW;AAClF,YAAM,QAAQ,SAAS,YAAY;AACnC,YAAM,MAAM,OAAO,WAAW,WAAW,OAAO,KAAK,IAAI;AACzD,cAAQ,OAAO;AAAA,QACb,KAAK;AACH,iBAAO,OAAO,IAAI,YAAY,CAAC;AAAA,QACjC,KAAK;AACH,iBAAO,OAAO,IAAI,YAAY,CAAC,EAAE,MAAM,EAAE;AAAA,QAC3C,KAAK;AACH,iBAAO,OAAO,IAAI,SAAS,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG;AAAA,QACnD,KAAK;AACH,iBAAO,OAAO,IAAI,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG;AAAA,QAC9C,KAAK;AACH,iBAAO,OAAO,IAAI,SAAS,CAAC,EAAE,SAAS,GAAG,GAAG;AAAA,QAC/C,KAAK,OAAO;AACV,gBAAM,YAAY,SAAS,OAAO,IAAI,EAAE;AACxC,gBAAM,QAAQ,OAAO,SAAS,SAAS,KAAK,YAAY,IAAI,KAAK,IAAI,WAAW,EAAE,IAAI;AACtF,iBAAO,QAAQ,OAAO,QAAQ,QAAQ,EAAE,SAAS,OAAO,GAAG,IAAI,OAAO,QAAQ,QAAQ;AAAA,QACxF;AAAA,QACA,KAAK,QAAQ;AACX,gBAAM,YAAY,SAAS,OAAO,IAAI,EAAE;AACxC,gBAAM,SAAS,OAAO,SAAS,SAAS,KAAK,YAAY,IAAI,YAAY;AACzE,iBAAO,qBAAqB,MAAM;AAAA,QACpC;AAAA,QACA,KAAK;AACH,iBAAO,QAAQ,QAAQ,WAAW;AAAA,QACpC,KAAK,UAAU;AACb,gBAAM,YAAY,SAAS,OAAO,IAAI,EAAE;AACxC,gBAAM,OACJ,OAAO,SAAS,SAAS,KAAK,YAAY,IAAI,KAAK,IAAI,WAAW,EAAE,IAAI;AAC1E,iBAAO,aAAa,IAAI;AAAA,QAC1B;AAAA,QACA,KAAK;AACH,iBAAO,QAAQ;AAAA,QACjB;AACE,iBAAO;AAAA,MACX;AAAA,IACF,CAAC;AAAA,EACH;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -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,
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
}, []);
|