@open-mercato/core 0.4.5-develop-3ce83a8b24 → 0.4.5-develop-539cff4960

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 (184) hide show
  1. package/dist/generated/entities/catalog_product/index.js +16 -0
  2. package/dist/generated/entities/catalog_product/index.js.map +2 -2
  3. package/dist/generated/entities/catalog_product_unit_conversion/index.js +27 -0
  4. package/dist/generated/entities/catalog_product_unit_conversion/index.js.map +7 -0
  5. package/dist/generated/entities/sales_credit_memo_line/index.js +7 -1
  6. package/dist/generated/entities/sales_credit_memo_line/index.js.map +2 -2
  7. package/dist/generated/entities/sales_invoice_line/index.js +7 -1
  8. package/dist/generated/entities/sales_invoice_line/index.js.map +2 -2
  9. package/dist/generated/entities/sales_order_line/index.js +6 -0
  10. package/dist/generated/entities/sales_order_line/index.js.map +2 -2
  11. package/dist/generated/entities/sales_quote_line/index.js +6 -0
  12. package/dist/generated/entities/sales_quote_line/index.js.map +2 -2
  13. package/dist/generated/entities.ids.generated.js +1 -0
  14. package/dist/generated/entities.ids.generated.js.map +2 -2
  15. package/dist/generated/entity-fields-registry.js +2 -0
  16. package/dist/generated/entity-fields-registry.js.map +2 -2
  17. package/dist/modules/catalog/api/prices/route.js +123 -8
  18. package/dist/modules/catalog/api/prices/route.js.map +2 -2
  19. package/dist/modules/catalog/api/product-unit-conversions/route.js +194 -0
  20. package/dist/modules/catalog/api/product-unit-conversions/route.js.map +7 -0
  21. package/dist/modules/catalog/api/products/route.js +351 -201
  22. package/dist/modules/catalog/api/products/route.js.map +2 -2
  23. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +1267 -497
  24. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  25. package/dist/modules/catalog/backend/catalog/products/create/page.js +733 -210
  26. package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
  27. package/dist/modules/catalog/commands/index.js +1 -0
  28. package/dist/modules/catalog/commands/index.js.map +2 -2
  29. package/dist/modules/catalog/commands/productUnitConversions.js +503 -0
  30. package/dist/modules/catalog/commands/productUnitConversions.js.map +7 -0
  31. package/dist/modules/catalog/commands/products.js +355 -73
  32. package/dist/modules/catalog/commands/products.js.map +2 -2
  33. package/dist/modules/catalog/commands/shared.js +18 -4
  34. package/dist/modules/catalog/commands/shared.js.map +2 -2
  35. package/dist/modules/catalog/components/products/ProductUomSection.js +591 -0
  36. package/dist/modules/catalog/components/products/ProductUomSection.js.map +7 -0
  37. package/dist/modules/catalog/components/products/productForm.js +66 -5
  38. package/dist/modules/catalog/components/products/productForm.js.map +2 -2
  39. package/dist/modules/catalog/components/products/productFormUtils.js +68 -0
  40. package/dist/modules/catalog/components/products/productFormUtils.js.map +7 -0
  41. package/dist/modules/catalog/data/entities.js +86 -0
  42. package/dist/modules/catalog/data/entities.js.map +2 -2
  43. package/dist/modules/catalog/data/validators.js +65 -3
  44. package/dist/modules/catalog/data/validators.js.map +2 -2
  45. package/dist/modules/catalog/events.js +3 -0
  46. package/dist/modules/catalog/events.js.map +2 -2
  47. package/dist/modules/catalog/lib/unitCodes.js +7 -0
  48. package/dist/modules/catalog/lib/unitCodes.js.map +7 -0
  49. package/dist/modules/catalog/lib/unitResolution.js +53 -0
  50. package/dist/modules/catalog/lib/unitResolution.js.map +7 -0
  51. package/dist/modules/catalog/migrations/Migration20260218225422.js +19 -0
  52. package/dist/modules/catalog/migrations/Migration20260218225422.js.map +7 -0
  53. package/dist/modules/catalog/migrations/Migration20260219084500.js +27 -0
  54. package/dist/modules/catalog/migrations/Migration20260219084500.js.map +7 -0
  55. package/dist/modules/catalog/search.js +69 -1
  56. package/dist/modules/catalog/search.js.map +2 -2
  57. package/dist/modules/catalog/seed/examples.js +91 -42
  58. package/dist/modules/catalog/seed/examples.js.map +2 -2
  59. package/dist/modules/dashboards/seed/analytics.js +3 -0
  60. package/dist/modules/dashboards/seed/analytics.js.map +2 -2
  61. package/dist/modules/sales/api/order-lines/route.js +98 -15
  62. package/dist/modules/sales/api/order-lines/route.js.map +2 -2
  63. package/dist/modules/sales/api/quote-lines/route.js +101 -14
  64. package/dist/modules/sales/api/quote-lines/route.js.map +2 -2
  65. package/dist/modules/sales/api/quotes/public/[token]/route.js +87 -12
  66. package/dist/modules/sales/api/quotes/public/[token]/route.js.map +2 -2
  67. package/dist/modules/sales/commands/documents.js +1424 -260
  68. package/dist/modules/sales/commands/documents.js.map +3 -3
  69. package/dist/modules/sales/commands/shared.js +6 -2
  70. package/dist/modules/sales/commands/shared.js.map +2 -2
  71. package/dist/modules/sales/components/documents/ItemsSection.js +216 -86
  72. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  73. package/dist/modules/sales/components/documents/LineItemDialog.js +913 -241
  74. package/dist/modules/sales/components/documents/LineItemDialog.js.map +3 -3
  75. package/dist/modules/sales/components/documents/ShipmentsSection.js +15 -3
  76. package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
  77. package/dist/modules/sales/data/entities.js +59 -3
  78. package/dist/modules/sales/data/entities.js.map +2 -2
  79. package/dist/modules/sales/data/validators.js +35 -0
  80. package/dist/modules/sales/data/validators.js.map +2 -2
  81. package/dist/modules/sales/frontend/quote/[token]/page.js +15 -1
  82. package/dist/modules/sales/frontend/quote/[token]/page.js.map +2 -2
  83. package/dist/modules/sales/migrations/Migration20260218225423.js +31 -0
  84. package/dist/modules/sales/migrations/Migration20260218225423.js.map +7 -0
  85. package/dist/modules/sales/migrations/Migration20260219084501.js +71 -0
  86. package/dist/modules/sales/migrations/Migration20260219084501.js.map +7 -0
  87. package/dist/modules/sales/search.js +28 -0
  88. package/dist/modules/sales/search.js.map +2 -2
  89. package/dist/modules/sales/seed/examples.js +14 -1
  90. package/dist/modules/sales/seed/examples.js.map +2 -2
  91. package/dist/modules/sales/widgets/injection/document-history/widget.client.js +1 -1
  92. package/dist/modules/sales/widgets/injection/document-history/widget.client.js.map +2 -2
  93. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +28 -15
  94. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  95. package/dist/modules/staff/translations.js +9 -0
  96. package/dist/modules/staff/translations.js.map +7 -0
  97. package/dist/modules/translations/components/TranslationDrawerAction.js +97 -0
  98. package/dist/modules/translations/components/TranslationDrawerAction.js.map +7 -0
  99. package/dist/modules/translations/lib/extract-record-id.js +31 -2
  100. package/dist/modules/translations/lib/extract-record-id.js.map +2 -2
  101. package/dist/modules/translations/lib/resolve-field-list.js +3 -0
  102. package/dist/modules/translations/lib/resolve-field-list.js.map +2 -2
  103. package/dist/modules/translations/widgets/injection/translation-manager/widget.client.js +105 -36
  104. package/dist/modules/translations/widgets/injection/translation-manager/widget.client.js.map +2 -2
  105. package/dist/modules/translations/widgets/injection-table.js +18 -29
  106. package/dist/modules/translations/widgets/injection-table.js.map +2 -2
  107. package/generated/entities/catalog_product/index.ts +8 -0
  108. package/generated/entities/catalog_product_unit_conversion/index.ts +12 -0
  109. package/generated/entities/sales_credit_memo_line/index.ts +3 -0
  110. package/generated/entities/sales_invoice_line/index.ts +3 -0
  111. package/generated/entities/sales_order_line/index.ts +3 -0
  112. package/generated/entities/sales_quote_line/index.ts +3 -0
  113. package/generated/entities.ids.generated.ts +1 -0
  114. package/generated/entity-fields-registry.ts +2 -0
  115. package/package.json +2 -2
  116. package/src/modules/auth/i18n/de.json +1 -1
  117. package/src/modules/auth/i18n/en.json +1 -1
  118. package/src/modules/auth/i18n/es.json +1 -1
  119. package/src/modules/auth/i18n/pl.json +1 -1
  120. package/src/modules/catalog/api/prices/route.ts +213 -81
  121. package/src/modules/catalog/api/product-unit-conversions/route.ts +195 -0
  122. package/src/modules/catalog/api/products/route.ts +638 -402
  123. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +2085 -1072
  124. package/src/modules/catalog/backend/catalog/products/create/page.tsx +1288 -593
  125. package/src/modules/catalog/commands/index.ts +1 -0
  126. package/src/modules/catalog/commands/productUnitConversions.ts +626 -0
  127. package/src/modules/catalog/commands/products.ts +1151 -693
  128. package/src/modules/catalog/commands/shared.ts +19 -5
  129. package/src/modules/catalog/components/products/ProductUomSection.tsx +745 -0
  130. package/src/modules/catalog/components/products/productForm.ts +369 -256
  131. package/src/modules/catalog/components/products/productFormUtils.ts +82 -0
  132. package/src/modules/catalog/data/entities.ts +82 -1
  133. package/src/modules/catalog/data/validators.ts +118 -34
  134. package/src/modules/catalog/events.ts +3 -0
  135. package/src/modules/catalog/i18n/de.json +56 -0
  136. package/src/modules/catalog/i18n/en.json +56 -0
  137. package/src/modules/catalog/i18n/es.json +56 -0
  138. package/src/modules/catalog/i18n/pl.json +56 -0
  139. package/src/modules/catalog/lib/unitCodes.ts +1 -0
  140. package/src/modules/catalog/lib/unitResolution.ts +62 -0
  141. package/src/modules/catalog/migrations/.snapshot-open-mercato.json +245 -0
  142. package/src/modules/catalog/migrations/Migration20260218225422.ts +21 -0
  143. package/src/modules/catalog/migrations/Migration20260219084500.ts +26 -0
  144. package/src/modules/catalog/search.ts +73 -1
  145. package/src/modules/catalog/seed/examples.ts +552 -479
  146. package/src/modules/dashboards/i18n/de.json +1 -1
  147. package/src/modules/dashboards/i18n/en.json +1 -1
  148. package/src/modules/dashboards/i18n/es.json +1 -1
  149. package/src/modules/dashboards/i18n/pl.json +1 -1
  150. package/src/modules/dashboards/seed/analytics.ts +3 -0
  151. package/src/modules/sales/api/order-lines/route.ts +158 -68
  152. package/src/modules/sales/api/quote-lines/route.ts +161 -67
  153. package/src/modules/sales/api/quotes/public/[token]/route.ts +122 -36
  154. package/src/modules/sales/commands/documents.ts +4250 -2424
  155. package/src/modules/sales/commands/shared.ts +7 -2
  156. package/src/modules/sales/components/documents/ItemsSection.tsx +580 -310
  157. package/src/modules/sales/components/documents/LineItemDialog.tsx +1988 -833
  158. package/src/modules/sales/components/documents/ShipmentsSection.tsx +17 -3
  159. package/src/modules/sales/components/documents/lineItemTypes.ts +6 -0
  160. package/src/modules/sales/data/entities.ts +53 -0
  161. package/src/modules/sales/data/validators.ts +36 -0
  162. package/src/modules/sales/frontend/quote/[token]/page.tsx +25 -1
  163. package/src/modules/sales/i18n/de.json +23 -3
  164. package/src/modules/sales/i18n/en.json +23 -3
  165. package/src/modules/sales/i18n/es.json +23 -3
  166. package/src/modules/sales/i18n/pl.json +23 -3
  167. package/src/modules/sales/lib/types.ts +30 -0
  168. package/src/modules/sales/migrations/.snapshot-open-mercato.json +172 -0
  169. package/src/modules/sales/migrations/Migration20260218225423.ts +37 -0
  170. package/src/modules/sales/migrations/Migration20260219084501.ts +73 -0
  171. package/src/modules/sales/search.ts +28 -0
  172. package/src/modules/sales/seed/examples.ts +20 -1
  173. package/src/modules/sales/widgets/injection/document-history/widget.client.tsx +1 -1
  174. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +8 -0
  175. package/src/modules/staff/translations.ts +5 -0
  176. package/src/modules/translations/components/TranslationDrawerAction.tsx +107 -0
  177. package/src/modules/translations/lib/extract-record-id.ts +47 -3
  178. package/src/modules/translations/lib/resolve-field-list.ts +4 -0
  179. package/src/modules/translations/widgets/injection/translation-manager/widget.client.tsx +108 -36
  180. package/src/modules/translations/widgets/injection-table.ts +19 -33
  181. package/src/modules/workflows/i18n/de.json +4 -4
  182. package/src/modules/workflows/i18n/en.json +4 -4
  183. package/src/modules/workflows/i18n/es.json +4 -4
  184. package/src/modules/workflows/i18n/pl.json +4 -4
@@ -96,6 +96,7 @@
96
96
  "dashboards.analytics.widgets.topProducts.empty": "Keine Produktverkaufsdaten für diesen Zeitraum",
97
97
  "dashboards.analytics.widgets.topProducts.error": "Produktdaten konnten nicht geladen werden",
98
98
  "dashboards.analytics.widgets.topProducts.title": "Top-Produkte nach Umsatz",
99
+ "dashboards.widgets.effective": "Aktive Widgets:",
99
100
  "dashboards.widgets.error.load": "Widget-Konfiguration konnte nicht geladen werden.",
100
101
  "dashboards.widgets.error.save": "Dashboard-Widget-Einstellungen konnten nicht gespeichert werden.",
101
102
  "dashboards.widgets.flash.saved": "Dashboard-Widgets aktualisiert",
@@ -103,7 +104,6 @@
103
104
  "dashboards.widgets.mode.hint": "Dieser Benutzer erbt derzeit Widgets von seinen Rollen. Wechseln Sie zu \"Überschreiben\", um anzupassen.",
104
105
  "dashboards.widgets.mode.inherit": "Von Rollen übernehmen",
105
106
  "dashboards.widgets.mode.override": "Für diesen Benutzer überschreiben",
106
- "dashboards.widgets.effective": "Aktive Widgets:",
107
107
  "dashboards.widgets.reset": "Zurücksetzen",
108
108
  "dashboards.widgets.save": "Widgets speichern",
109
109
  "dashboards.widgets.saving": "Speichern…"
@@ -96,6 +96,7 @@
96
96
  "dashboards.analytics.widgets.topProducts.empty": "No product sales data for this period",
97
97
  "dashboards.analytics.widgets.topProducts.error": "Failed to load top products data",
98
98
  "dashboards.analytics.widgets.topProducts.title": "Top Products by Revenue",
99
+ "dashboards.widgets.effective": "Effective widgets:",
99
100
  "dashboards.widgets.error.load": "Unable to load widget configuration.",
100
101
  "dashboards.widgets.error.save": "Unable to save dashboard widget preferences.",
101
102
  "dashboards.widgets.flash.saved": "Dashboard widgets updated",
@@ -103,7 +104,6 @@
103
104
  "dashboards.widgets.mode.hint": "This user currently inherits widgets from their assigned roles. Switch to override to customize.",
104
105
  "dashboards.widgets.mode.inherit": "Inherit from roles",
105
106
  "dashboards.widgets.mode.override": "Override for this user",
106
- "dashboards.widgets.effective": "Effective widgets:",
107
107
  "dashboards.widgets.reset": "Reset",
108
108
  "dashboards.widgets.save": "Save widgets",
109
109
  "dashboards.widgets.saving": "Saving…"
@@ -96,6 +96,7 @@
96
96
  "dashboards.analytics.widgets.topProducts.empty": "No hay datos de ventas de productos para este período",
97
97
  "dashboards.analytics.widgets.topProducts.error": "No se pudieron cargar los datos de productos",
98
98
  "dashboards.analytics.widgets.topProducts.title": "Productos principales por ingresos",
99
+ "dashboards.widgets.effective": "Widgets efectivos:",
99
100
  "dashboards.widgets.error.load": "No se pudo cargar la configuración de widgets.",
100
101
  "dashboards.widgets.error.save": "No se pudieron guardar las preferencias de los widgets del panel.",
101
102
  "dashboards.widgets.flash.saved": "Widgets del panel actualizados",
@@ -103,7 +104,6 @@
103
104
  "dashboards.widgets.mode.hint": "Este usuario hereda los widgets de sus roles asignados. Cambia a sobrescribir para personalizar.",
104
105
  "dashboards.widgets.mode.inherit": "Heredar de los roles",
105
106
  "dashboards.widgets.mode.override": "Sobrescribir para este usuario",
106
- "dashboards.widgets.effective": "Widgets efectivos:",
107
107
  "dashboards.widgets.reset": "Restablecer",
108
108
  "dashboards.widgets.save": "Guardar widgets",
109
109
  "dashboards.widgets.saving": "Guardando…"
@@ -96,6 +96,7 @@
96
96
  "dashboards.analytics.widgets.topProducts.empty": "Brak danych o sprzedaży produktów dla tego okresu",
97
97
  "dashboards.analytics.widgets.topProducts.error": "Nie udało się załadować danych o produktach",
98
98
  "dashboards.analytics.widgets.topProducts.title": "Najlepsze produkty wg przychodu",
99
+ "dashboards.widgets.effective": "Aktywne widżety:",
99
100
  "dashboards.widgets.error.load": "Nie udało się wczytać konfiguracji widżetów.",
100
101
  "dashboards.widgets.error.save": "Nie udało się zapisać preferencji widżetów pulpitu.",
101
102
  "dashboards.widgets.flash.saved": "Widżety pulpitu zostały zaktualizowane",
@@ -103,7 +104,6 @@
103
104
  "dashboards.widgets.mode.hint": "Ten użytkownik dziedziczy widżety z przypisanych ról. Wybierz nadpisanie, aby je dostosować.",
104
105
  "dashboards.widgets.mode.inherit": "Dziedzicz po rolach",
105
106
  "dashboards.widgets.mode.override": "Nadpisz dla tego użytkownika",
106
- "dashboards.widgets.effective": "Aktywne widżety:",
107
107
  "dashboards.widgets.reset": "Resetuj",
108
108
  "dashboards.widgets.save": "Zapisz widżety",
109
109
  "dashboards.widgets.saving": "Zapisywanie…"
@@ -325,6 +325,9 @@ export async function seedAnalyticsData(
325
325
  kind: 'product',
326
326
  name: lineData.product.title,
327
327
  quantity: toAmount(lineData.quantity),
328
+ normalizedQuantity: toAmount(lineData.quantity),
329
+ normalizedUnit: null,
330
+ uomSnapshot: null,
328
331
  currencyCode: 'USD',
329
332
  unitPriceNet: toAmount(lineData.unitPriceNet),
330
333
  unitPriceGross: toAmount(lineData.unitPriceGross),
@@ -1,50 +1,69 @@
1
- import { z } from 'zod'
2
- import { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'
3
- import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
4
- import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
5
- import { buildCustomFieldFiltersFromQuery, extractAllCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields'
6
- import { SalesOrderLine } from '../../data/entities'
7
- import { orderLineCreateSchema } from '../../data/validators'
8
- import { createPagedListResponseSchema, createSalesCrudOpenApi, defaultOkResponseSchema } from '../openapi'
9
- import { withScopedPayload } from '../utils'
10
- import { E } from '#generated/entities.ids.generated'
11
- import * as F from '#generated/entities/sales_order_line'
1
+ import { z } from "zod";
2
+ import { makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
3
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
4
+ import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
5
+ import {
6
+ buildCustomFieldFiltersFromQuery,
7
+ extractAllCustomFieldEntries,
8
+ } from "@open-mercato/shared/lib/crud/custom-fields";
9
+ import { SalesOrderLine } from "../../data/entities";
10
+ import { orderLineCreateSchema } from "../../data/validators";
11
+ import {
12
+ createPagedListResponseSchema,
13
+ createSalesCrudOpenApi,
14
+ defaultOkResponseSchema,
15
+ } from "../openapi";
16
+ import { withScopedPayload } from "../utils";
17
+ import { E } from "#generated/entities.ids.generated";
18
+ import * as F from "#generated/entities/sales_order_line";
19
+ import { canonicalizeUnitCode, REFERENCE_UNIT_CODES } from "@open-mercato/shared/lib/units/unitCodes";
12
20
 
13
- const rawBodySchema = z.object({}).passthrough()
14
- const resolveRawBody = (raw: unknown) => (raw && typeof raw === 'object' && 'body' in raw ? (raw as any).body : raw)
21
+ const rawBodySchema = z.object({}).passthrough();
22
+ const resolveRawBody = (raw: unknown): Record<string, unknown> => {
23
+ if (!raw || typeof raw !== "object") return {};
24
+ if ("body" in raw) {
25
+ const payload = raw as { body?: unknown };
26
+ if (payload.body && typeof payload.body === "object") {
27
+ return payload.body as Record<string, unknown>;
28
+ }
29
+ }
30
+ return raw as Record<string, unknown>;
31
+ };
15
32
 
16
33
  const listSchema = z
17
34
  .object({
18
35
  page: z.coerce.number().min(1).default(1),
19
- pageSize: z.coerce.number().min(1).max(200).default(50),
36
+ pageSize: z.coerce.number().min(1).max(100).default(50),
20
37
  id: z.string().uuid().optional(),
21
38
  orderId: z.string().uuid().optional(),
22
39
  sortField: z.string().optional(),
23
- sortDir: z.enum(['asc', 'desc']).optional(),
40
+ sortDir: z.enum(["asc", "desc"]).optional(),
24
41
  })
25
- .passthrough()
42
+ .passthrough();
26
43
 
27
44
  const routeMetadata = {
28
- GET: { requireAuth: true, requireFeatures: ['sales.orders.view'] },
29
- POST: { requireAuth: true, requireFeatures: ['sales.orders.manage'] },
30
- PUT: { requireAuth: true, requireFeatures: ['sales.orders.manage'] },
31
- DELETE: { requireAuth: true, requireFeatures: ['sales.orders.manage'] },
32
- }
45
+ GET: { requireAuth: true, requireFeatures: ["sales.orders.view"] },
46
+ POST: { requireAuth: true, requireFeatures: ["sales.orders.manage"] },
47
+ PUT: { requireAuth: true, requireFeatures: ["sales.orders.manage"] },
48
+ DELETE: { requireAuth: true, requireFeatures: ["sales.orders.manage"] },
49
+ };
33
50
 
34
- const upsertSchema = orderLineCreateSchema.extend({ id: z.string().uuid().optional() })
51
+ const upsertSchema = orderLineCreateSchema.extend({
52
+ id: z.string().uuid().optional(),
53
+ });
35
54
  const deleteSchema = z.object({
36
55
  id: z.string().uuid(),
37
56
  orderId: z.string().uuid(),
38
- })
57
+ });
39
58
 
40
59
  const crud = makeCrudRoute({
41
60
  metadata: routeMetadata,
42
61
  orm: {
43
62
  entity: SalesOrderLine,
44
- idField: 'id',
45
- orgField: 'organizationId',
46
- tenantField: 'tenantId',
47
- softDeleteField: 'deletedAt',
63
+ idField: "id",
64
+ orgField: "organizationId",
65
+ tenantField: "tenantId",
66
+ softDeleteField: "deletedAt",
48
67
  },
49
68
  indexer: {
50
69
  entityType: E.sales.sales_order_line,
@@ -54,7 +73,7 @@ const crud = makeCrudRoute({
54
73
  entityId: E.sales.sales_order_line,
55
74
  fields: [
56
75
  F.id,
57
- 'order_id',
76
+ "order_id",
58
77
  F.line_number,
59
78
  F.kind,
60
79
  F.status_entry_id,
@@ -69,6 +88,9 @@ const crud = makeCrudRoute({
69
88
  F.tenant_id,
70
89
  F.quantity,
71
90
  F.quantity_unit,
91
+ F.normalized_quantity,
92
+ F.normalized_unit,
93
+ F.uom_snapshot,
72
94
  F.currency_code,
73
95
  F.unit_price_net,
74
96
  F.unit_price_gross,
@@ -92,75 +114,134 @@ const crud = makeCrudRoute({
92
114
  lineNumber: F.line_number,
93
115
  },
94
116
  buildFilters: async (query, ctx) => {
95
- const filters: Record<string, unknown> = {}
96
- if (query.id) filters.id = { $eq: query.id }
97
- if (query.orderId) filters.order_id = { $eq: query.orderId }
117
+ const filters: Record<string, unknown> = {};
118
+ if (query.id) filters.id = { $eq: query.id };
119
+ if (query.orderId) filters.order_id = { $eq: query.orderId };
98
120
  try {
99
- const em = ctx.container.resolve('em')
121
+ const em = ctx.container.resolve("em");
100
122
  const cfFilters = await buildCustomFieldFiltersFromQuery({
101
123
  entityId: E.sales.sales_order_line,
102
124
  query,
103
125
  em,
104
126
  tenantId: ctx.auth?.tenantId ?? null,
105
- })
106
- Object.assign(filters, cfFilters)
127
+ });
128
+ Object.assign(filters, cfFilters);
107
129
  } catch {
108
130
  // ignore
109
131
  }
110
- return filters
132
+ return filters;
111
133
  },
112
- transformItem: (item: any) => {
113
- if (!item) return item
114
- const normalized = { ...item }
115
- const cfEntries = extractAllCustomFieldEntries(item)
134
+ transformItem: (item: Record<string, unknown> | null | undefined) => {
135
+ if (!item) return item;
136
+ const normalized = { ...item };
137
+ const cfEntries = extractAllCustomFieldEntries(item);
116
138
  for (const key of Object.keys(normalized)) {
117
- if (key.startsWith('cf:')) delete normalized[key]
139
+ if (key.startsWith("cf:")) delete normalized[key];
118
140
  }
119
- return { ...normalized, ...cfEntries }
141
+ const quantityUnit = canonicalizeUnitCode(
142
+ normalized["quantity_unit"] ?? normalized["quantityUnit"],
143
+ );
144
+ const normalizedUnit =
145
+ canonicalizeUnitCode(
146
+ normalized["normalized_unit"] ?? normalized["normalizedUnit"],
147
+ ) ?? quantityUnit;
148
+ return {
149
+ ...normalized,
150
+ quantity_unit: quantityUnit,
151
+ normalized_unit: normalizedUnit,
152
+ ...cfEntries,
153
+ };
120
154
  },
121
155
  },
122
156
  actions: {
123
157
  create: {
124
- commandId: 'sales.orders.lines.upsert',
158
+ commandId: "sales.orders.lines.upsert",
125
159
  schema: rawBodySchema,
126
160
  mapInput: async ({ raw, ctx }) => {
127
- console.log('CREATE order line raw input:', raw)
128
- const { translate } = await resolveTranslations()
129
- const payload = upsertSchema.parse(withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate))
130
- return { body: payload }
161
+ const { translate } = await resolveTranslations();
162
+ const payload = upsertSchema.parse(
163
+ withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),
164
+ );
165
+ return { body: payload };
131
166
  },
132
- response: ({ result }) => ({ id: result?.lineId ?? null, orderId: result?.orderId ?? null }),
167
+ response: ({ result }) => ({
168
+ id: result?.lineId ?? null,
169
+ orderId: result?.orderId ?? null,
170
+ }),
133
171
  status: 201,
134
172
  },
135
173
  update: {
136
- commandId: 'sales.orders.lines.upsert',
174
+ commandId: "sales.orders.lines.upsert",
137
175
  schema: rawBodySchema,
138
176
  mapInput: async ({ raw, ctx }) => {
139
- const { translate } = await resolveTranslations()
140
- const payload = upsertSchema.parse(withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate))
141
- return { body: payload }
177
+ const { translate } = await resolveTranslations();
178
+ const payload = upsertSchema.parse(
179
+ withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),
180
+ );
181
+ return { body: payload };
142
182
  },
143
- response: ({ result }) => ({ id: result?.lineId ?? null, orderId: result?.orderId ?? null }),
183
+ response: ({ result }) => ({
184
+ id: result?.lineId ?? null,
185
+ orderId: result?.orderId ?? null,
186
+ }),
144
187
  },
145
188
  delete: {
146
- commandId: 'sales.orders.lines.delete',
189
+ commandId: "sales.orders.lines.delete",
147
190
  schema: rawBodySchema,
148
191
  mapInput: async ({ raw, ctx }) => {
149
- const { translate } = await resolveTranslations()
150
- const payload = deleteSchema.parse(withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate))
192
+ const { translate } = await resolveTranslations();
193
+ const payload = deleteSchema.parse(
194
+ withScopedPayload(resolveRawBody(raw) ?? {}, ctx, translate),
195
+ );
151
196
  if (!payload.id || !payload.orderId) {
152
- throw new CrudHttpError(400, { error: translate('sales.documents.detail.error', 'Document not found or inaccessible.') })
197
+ throw new CrudHttpError(400, {
198
+ error: translate(
199
+ "sales.documents.detail.error",
200
+ "Document not found or inaccessible.",
201
+ ),
202
+ });
153
203
  }
154
- return { body: payload }
204
+ return { body: payload };
155
205
  },
156
206
  response: () => ({ ok: true }),
157
207
  },
158
208
  },
159
- })
209
+ });
160
210
 
161
- const { GET, POST, PUT, DELETE } = crud
211
+ const { GET, POST, PUT, DELETE } = crud;
162
212
 
163
- export { GET, POST, PUT, DELETE }
213
+ export { GET, POST, PUT, DELETE };
214
+
215
+ const uomSnapshotOpenApiSchema = z
216
+ .object({
217
+ version: z.literal(1),
218
+ productId: z.string().nullable(),
219
+ productVariantId: z.string().nullable(),
220
+ baseUnitCode: z.string().nullable(),
221
+ enteredUnitCode: z.string().nullable(),
222
+ enteredQuantity: z.string(),
223
+ toBaseFactor: z.string(),
224
+ normalizedQuantity: z.string(),
225
+ rounding: z.object({
226
+ mode: z.enum(["half_up", "down", "up"]),
227
+ scale: z.number().int(),
228
+ }),
229
+ source: z.object({
230
+ conversionId: z.string().nullable(),
231
+ resolvedAt: z.string(),
232
+ }),
233
+ unitPriceReference: z
234
+ .object({
235
+ enabled: z.boolean(),
236
+ referenceUnitCode: z.enum(REFERENCE_UNIT_CODES).nullable(),
237
+ baseQuantity: z.string().nullable(),
238
+ grossPerReference: z.string().nullable().optional(),
239
+ netPerReference: z.string().nullable().optional(),
240
+ })
241
+ .optional(),
242
+ })
243
+ .nullable()
244
+ .optional();
164
245
 
165
246
  const orderLineSchema = z.object({
166
247
  id: z.string().uuid(),
@@ -177,6 +258,9 @@ const orderLineSchema = z.object({
177
258
  comment: z.string().nullable().optional(),
178
259
  quantity: z.number(),
179
260
  quantity_unit: z.string().nullable().optional(),
261
+ normalized_quantity: z.number(),
262
+ normalized_unit: z.string().nullable().optional(),
263
+ uom_snapshot: uomSnapshotOpenApiSchema,
180
264
  currency_code: z.string(),
181
265
  unit_price_net: z.number(),
182
266
  unit_price_gross: z.number(),
@@ -193,25 +277,31 @@ const orderLineSchema = z.object({
193
277
  custom_field_set_id: z.string().uuid().nullable().optional(),
194
278
  created_at: z.string(),
195
279
  updated_at: z.string(),
196
- })
280
+ });
197
281
 
198
282
  export const openApi = createSalesCrudOpenApi({
199
- resourceName: 'Order line',
283
+ resourceName: "Order line",
200
284
  querySchema: listSchema,
201
285
  listResponseSchema: createPagedListResponseSchema(orderLineSchema),
202
286
  create: {
203
287
  schema: upsertSchema,
204
- responseSchema: z.object({ id: z.string().uuid().nullable(), orderId: z.string().uuid().nullable() }),
205
- description: 'Creates an order line and recalculates totals.',
288
+ responseSchema: z.object({
289
+ id: z.string().uuid().nullable(),
290
+ orderId: z.string().uuid().nullable(),
291
+ }),
292
+ description: "Creates an order line and recalculates totals.",
206
293
  },
207
294
  update: {
208
295
  schema: upsertSchema,
209
- responseSchema: z.object({ id: z.string().uuid().nullable(), orderId: z.string().uuid().nullable() }),
210
- description: 'Updates an order line and recalculates totals.',
296
+ responseSchema: z.object({
297
+ id: z.string().uuid().nullable(),
298
+ orderId: z.string().uuid().nullable(),
299
+ }),
300
+ description: "Updates an order line and recalculates totals.",
211
301
  },
212
302
  del: {
213
303
  schema: deleteSchema,
214
304
  responseSchema: defaultOkResponseSchema,
215
- description: 'Deletes an order line and recalculates totals.',
305
+ description: "Deletes an order line and recalculates totals.",
216
306
  },
217
- })
307
+ });