@open-mercato/core 0.4.5-develop-f4858e0ef3 → 0.4.5-develop-4849712ccb

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 (163) 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/generated/entities/catalog_product/index.ts +8 -0
  94. package/generated/entities/catalog_product_unit_conversion/index.ts +12 -0
  95. package/generated/entities/sales_credit_memo_line/index.ts +3 -0
  96. package/generated/entities/sales_invoice_line/index.ts +3 -0
  97. package/generated/entities/sales_order_line/index.ts +3 -0
  98. package/generated/entities/sales_quote_line/index.ts +3 -0
  99. package/generated/entities.ids.generated.ts +1 -0
  100. package/generated/entity-fields-registry.ts +2 -0
  101. package/package.json +2 -2
  102. package/src/modules/auth/i18n/de.json +1 -1
  103. package/src/modules/auth/i18n/en.json +1 -1
  104. package/src/modules/auth/i18n/es.json +1 -1
  105. package/src/modules/auth/i18n/pl.json +1 -1
  106. package/src/modules/catalog/api/prices/route.ts +213 -81
  107. package/src/modules/catalog/api/product-unit-conversions/route.ts +195 -0
  108. package/src/modules/catalog/api/products/route.ts +638 -402
  109. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +2085 -1072
  110. package/src/modules/catalog/backend/catalog/products/create/page.tsx +1288 -593
  111. package/src/modules/catalog/commands/index.ts +1 -0
  112. package/src/modules/catalog/commands/productUnitConversions.ts +626 -0
  113. package/src/modules/catalog/commands/products.ts +1151 -693
  114. package/src/modules/catalog/commands/shared.ts +19 -5
  115. package/src/modules/catalog/components/products/ProductUomSection.tsx +745 -0
  116. package/src/modules/catalog/components/products/productForm.ts +369 -256
  117. package/src/modules/catalog/components/products/productFormUtils.ts +82 -0
  118. package/src/modules/catalog/data/entities.ts +82 -1
  119. package/src/modules/catalog/data/validators.ts +118 -34
  120. package/src/modules/catalog/events.ts +3 -0
  121. package/src/modules/catalog/i18n/de.json +56 -0
  122. package/src/modules/catalog/i18n/en.json +56 -0
  123. package/src/modules/catalog/i18n/es.json +56 -0
  124. package/src/modules/catalog/i18n/pl.json +56 -0
  125. package/src/modules/catalog/lib/unitCodes.ts +1 -0
  126. package/src/modules/catalog/lib/unitResolution.ts +62 -0
  127. package/src/modules/catalog/migrations/.snapshot-open-mercato.json +245 -0
  128. package/src/modules/catalog/migrations/Migration20260218225422.ts +21 -0
  129. package/src/modules/catalog/migrations/Migration20260219084500.ts +26 -0
  130. package/src/modules/catalog/search.ts +73 -1
  131. package/src/modules/catalog/seed/examples.ts +552 -479
  132. package/src/modules/dashboards/i18n/de.json +1 -1
  133. package/src/modules/dashboards/i18n/en.json +1 -1
  134. package/src/modules/dashboards/i18n/es.json +1 -1
  135. package/src/modules/dashboards/i18n/pl.json +1 -1
  136. package/src/modules/dashboards/seed/analytics.ts +3 -0
  137. package/src/modules/sales/api/order-lines/route.ts +158 -68
  138. package/src/modules/sales/api/quote-lines/route.ts +161 -67
  139. package/src/modules/sales/api/quotes/public/[token]/route.ts +122 -36
  140. package/src/modules/sales/commands/documents.ts +4250 -2424
  141. package/src/modules/sales/commands/shared.ts +7 -2
  142. package/src/modules/sales/components/documents/ItemsSection.tsx +580 -310
  143. package/src/modules/sales/components/documents/LineItemDialog.tsx +1988 -833
  144. package/src/modules/sales/components/documents/ShipmentsSection.tsx +17 -3
  145. package/src/modules/sales/components/documents/lineItemTypes.ts +6 -0
  146. package/src/modules/sales/data/entities.ts +53 -0
  147. package/src/modules/sales/data/validators.ts +36 -0
  148. package/src/modules/sales/frontend/quote/[token]/page.tsx +25 -1
  149. package/src/modules/sales/i18n/de.json +23 -3
  150. package/src/modules/sales/i18n/en.json +23 -3
  151. package/src/modules/sales/i18n/es.json +23 -3
  152. package/src/modules/sales/i18n/pl.json +23 -3
  153. package/src/modules/sales/lib/types.ts +30 -0
  154. package/src/modules/sales/migrations/.snapshot-open-mercato.json +172 -0
  155. package/src/modules/sales/migrations/Migration20260218225423.ts +37 -0
  156. package/src/modules/sales/migrations/Migration20260219084501.ts +73 -0
  157. package/src/modules/sales/search.ts +28 -0
  158. package/src/modules/sales/seed/examples.ts +20 -1
  159. package/src/modules/sales/widgets/injection/document-history/widget.client.tsx +1 -1
  160. package/src/modules/workflows/i18n/de.json +4 -4
  161. package/src/modules/workflows/i18n/en.json +4 -4
  162. package/src/modules/workflows/i18n/es.json +4 -4
  163. package/src/modules/workflows/i18n/pl.json +4 -4
@@ -1,10 +1,37 @@
1
1
  import type { EventBus } from '@open-mercato/events'
2
+ import type { ReferenceUnitCode } from '@open-mercato/shared/lib/units/unitCodes'
2
3
  import type { SalesAdjustmentKind, SalesDocumentKind, SalesLineKind } from '../data/entities'
3
4
 
4
5
  export type { SalesAdjustmentKind, SalesDocumentKind, SalesLineKind }
5
6
 
6
7
  export type NumericLike = number | string
7
8
 
9
+ export type SalesLineUomSnapshot = {
10
+ version: 1
11
+ productId: string | null
12
+ productVariantId: string | null
13
+ baseUnitCode: string | null
14
+ enteredUnitCode: string | null
15
+ enteredQuantity: string
16
+ toBaseFactor: string
17
+ normalizedQuantity: string
18
+ rounding: {
19
+ mode: 'half_up' | 'down' | 'up'
20
+ scale: number
21
+ }
22
+ source: {
23
+ conversionId: string | null
24
+ resolvedAt: string
25
+ }
26
+ unitPriceReference?: {
27
+ enabled: boolean
28
+ referenceUnitCode: ReferenceUnitCode | null
29
+ baseQuantity: string | null
30
+ grossPerReference?: string | null
31
+ netPerReference?: string | null
32
+ }
33
+ }
34
+
8
35
  export type SalesLineSnapshot = {
9
36
  id?: string
10
37
  lineNumber?: number
@@ -16,6 +43,9 @@ export type SalesLineSnapshot = {
16
43
  comment?: string | null
17
44
  quantity: number
18
45
  quantityUnit?: string | null
46
+ normalizedQuantity?: number | null
47
+ normalizedUnit?: string | null
48
+ uomSnapshot?: SalesLineUomSnapshot | null
19
49
  currencyCode: string
20
50
  unitPriceNet?: number | null
21
51
  unitPriceGross?: number | null
@@ -1966,6 +1966,36 @@
1966
1966
  "nullable": true,
1967
1967
  "mappedType": "text"
1968
1968
  },
1969
+ "normalized_quantity": {
1970
+ "name": "normalized_quantity",
1971
+ "type": "numeric(18,6)",
1972
+ "unsigned": false,
1973
+ "autoincrement": false,
1974
+ "primary": false,
1975
+ "nullable": false,
1976
+ "precision": 18,
1977
+ "scale": 6,
1978
+ "default": "'0'",
1979
+ "mappedType": "decimal"
1980
+ },
1981
+ "normalized_unit": {
1982
+ "name": "normalized_unit",
1983
+ "type": "text",
1984
+ "unsigned": false,
1985
+ "autoincrement": false,
1986
+ "primary": false,
1987
+ "nullable": true,
1988
+ "mappedType": "text"
1989
+ },
1990
+ "uom_snapshot": {
1991
+ "name": "uom_snapshot",
1992
+ "type": "jsonb",
1993
+ "unsigned": false,
1994
+ "autoincrement": false,
1995
+ "primary": false,
1996
+ "nullable": true,
1997
+ "mappedType": "json"
1998
+ },
1969
1999
  "currency_code": {
1970
2000
  "name": "currency_code",
1971
2001
  "type": "text",
@@ -2150,6 +2180,19 @@
2150
2180
  "name": "sales_quote_lines",
2151
2181
  "schema": "public",
2152
2182
  "indexes": [
2183
+ {
2184
+ "keyName": "sales_quote_lines_normalized_idx",
2185
+ "columnNames": [
2186
+ "organization_id",
2187
+ "tenant_id",
2188
+ "normalized_unit",
2189
+ "normalized_quantity"
2190
+ ],
2191
+ "composite": true,
2192
+ "constraint": false,
2193
+ "primary": false,
2194
+ "unique": false
2195
+ },
2153
2196
  {
2154
2197
  "keyName": "sales_quote_lines_status_idx",
2155
2198
  "columnNames": [
@@ -3893,6 +3936,36 @@
3893
3936
  "nullable": true,
3894
3937
  "mappedType": "text"
3895
3938
  },
3939
+ "normalized_quantity": {
3940
+ "name": "normalized_quantity",
3941
+ "type": "numeric(18,6)",
3942
+ "unsigned": false,
3943
+ "autoincrement": false,
3944
+ "primary": false,
3945
+ "nullable": false,
3946
+ "precision": 18,
3947
+ "scale": 6,
3948
+ "default": "'0'",
3949
+ "mappedType": "decimal"
3950
+ },
3951
+ "normalized_unit": {
3952
+ "name": "normalized_unit",
3953
+ "type": "text",
3954
+ "unsigned": false,
3955
+ "autoincrement": false,
3956
+ "primary": false,
3957
+ "nullable": true,
3958
+ "mappedType": "text"
3959
+ },
3960
+ "uom_snapshot": {
3961
+ "name": "uom_snapshot",
3962
+ "type": "jsonb",
3963
+ "unsigned": false,
3964
+ "autoincrement": false,
3965
+ "primary": false,
3966
+ "nullable": true,
3967
+ "mappedType": "json"
3968
+ },
3896
3969
  "reserved_quantity": {
3897
3970
  "name": "reserved_quantity",
3898
3971
  "type": "numeric(18,4)",
@@ -4125,6 +4198,19 @@
4125
4198
  "name": "sales_order_lines",
4126
4199
  "schema": "public",
4127
4200
  "indexes": [
4201
+ {
4202
+ "keyName": "sales_order_lines_normalized_idx",
4203
+ "columnNames": [
4204
+ "organization_id",
4205
+ "tenant_id",
4206
+ "normalized_unit",
4207
+ "normalized_quantity"
4208
+ ],
4209
+ "composite": true,
4210
+ "constraint": false,
4211
+ "primary": false,
4212
+ "unique": false
4213
+ },
4128
4214
  {
4129
4215
  "keyName": "sales_order_lines_status_idx",
4130
4216
  "columnNames": [
@@ -5309,6 +5395,36 @@
5309
5395
  "nullable": true,
5310
5396
  "mappedType": "text"
5311
5397
  },
5398
+ "normalized_quantity": {
5399
+ "name": "normalized_quantity",
5400
+ "type": "numeric(18,6)",
5401
+ "unsigned": false,
5402
+ "autoincrement": false,
5403
+ "primary": false,
5404
+ "nullable": false,
5405
+ "precision": 18,
5406
+ "scale": 6,
5407
+ "default": "'0'",
5408
+ "mappedType": "decimal"
5409
+ },
5410
+ "normalized_unit": {
5411
+ "name": "normalized_unit",
5412
+ "type": "text",
5413
+ "unsigned": false,
5414
+ "autoincrement": false,
5415
+ "primary": false,
5416
+ "nullable": true,
5417
+ "mappedType": "text"
5418
+ },
5419
+ "uom_snapshot": {
5420
+ "name": "uom_snapshot",
5421
+ "type": "jsonb",
5422
+ "unsigned": false,
5423
+ "autoincrement": false,
5424
+ "primary": false,
5425
+ "nullable": true,
5426
+ "mappedType": "json"
5427
+ },
5312
5428
  "currency_code": {
5313
5429
  "name": "currency_code",
5314
5430
  "type": "text",
@@ -5427,6 +5543,19 @@
5427
5543
  "name": "sales_invoice_lines",
5428
5544
  "schema": "public",
5429
5545
  "indexes": [
5546
+ {
5547
+ "keyName": "sales_invoice_lines_normalized_idx",
5548
+ "columnNames": [
5549
+ "organization_id",
5550
+ "tenant_id",
5551
+ "normalized_unit",
5552
+ "normalized_quantity"
5553
+ ],
5554
+ "composite": true,
5555
+ "constraint": false,
5556
+ "primary": false,
5557
+ "unique": false
5558
+ },
5430
5559
  {
5431
5560
  "keyName": "sales_invoice_lines_scope_idx",
5432
5561
  "columnNames": [
@@ -6307,6 +6436,36 @@
6307
6436
  "nullable": true,
6308
6437
  "mappedType": "text"
6309
6438
  },
6439
+ "normalized_quantity": {
6440
+ "name": "normalized_quantity",
6441
+ "type": "numeric(18,6)",
6442
+ "unsigned": false,
6443
+ "autoincrement": false,
6444
+ "primary": false,
6445
+ "nullable": false,
6446
+ "precision": 18,
6447
+ "scale": 6,
6448
+ "default": "'0'",
6449
+ "mappedType": "decimal"
6450
+ },
6451
+ "normalized_unit": {
6452
+ "name": "normalized_unit",
6453
+ "type": "text",
6454
+ "unsigned": false,
6455
+ "autoincrement": false,
6456
+ "primary": false,
6457
+ "nullable": true,
6458
+ "mappedType": "text"
6459
+ },
6460
+ "uom_snapshot": {
6461
+ "name": "uom_snapshot",
6462
+ "type": "jsonb",
6463
+ "unsigned": false,
6464
+ "autoincrement": false,
6465
+ "primary": false,
6466
+ "nullable": true,
6467
+ "mappedType": "json"
6468
+ },
6310
6469
  "currency_code": {
6311
6470
  "name": "currency_code",
6312
6471
  "type": "text",
@@ -6401,6 +6560,19 @@
6401
6560
  "name": "sales_credit_memo_lines",
6402
6561
  "schema": "public",
6403
6562
  "indexes": [
6563
+ {
6564
+ "keyName": "sales_credit_memo_lines_normalized_idx",
6565
+ "columnNames": [
6566
+ "organization_id",
6567
+ "tenant_id",
6568
+ "normalized_unit",
6569
+ "normalized_quantity"
6570
+ ],
6571
+ "composite": true,
6572
+ "constraint": false,
6573
+ "primary": false,
6574
+ "unique": false
6575
+ },
6404
6576
  {
6405
6577
  "keyName": "sales_credit_memo_lines_scope_idx",
6406
6578
  "columnNames": [
@@ -0,0 +1,37 @@
1
+ import { Migration } from '@mikro-orm/migrations';
2
+
3
+ export class Migration20260218225423 extends Migration {
4
+
5
+ override async up(): Promise<void> {
6
+ this.addSql(`alter table "sales_quote_lines" add column "normalized_quantity" numeric(18,6) not null default '0', add column "normalized_unit" text null, add column "uom_snapshot" jsonb null;`);
7
+ this.addSql(`update "sales_quote_lines" set "normalized_quantity" = coalesce("quantity", '0'), "normalized_unit" = "quantity_unit" where "uom_snapshot" is null;`);
8
+ this.addSql(`create index "sales_quote_lines_normalized_idx" on "sales_quote_lines" ("organization_id", "tenant_id", "normalized_unit", "normalized_quantity");`);
9
+
10
+ this.addSql(`alter table "sales_order_lines" add column "normalized_quantity" numeric(18,6) not null default '0', add column "normalized_unit" text null, add column "uom_snapshot" jsonb null;`);
11
+ this.addSql(`update "sales_order_lines" set "normalized_quantity" = coalesce("quantity", '0'), "normalized_unit" = "quantity_unit" where "uom_snapshot" is null;`);
12
+ this.addSql(`create index "sales_order_lines_normalized_idx" on "sales_order_lines" ("organization_id", "tenant_id", "normalized_unit", "normalized_quantity");`);
13
+
14
+ this.addSql(`alter table "sales_invoice_lines" add column "normalized_quantity" numeric(18,6) not null default '0', add column "normalized_unit" text null, add column "uom_snapshot" jsonb null;`);
15
+ this.addSql(`update "sales_invoice_lines" set "normalized_quantity" = coalesce("quantity", '0'), "normalized_unit" = "quantity_unit" where "uom_snapshot" is null;`);
16
+ this.addSql(`create index "sales_invoice_lines_normalized_idx" on "sales_invoice_lines" ("organization_id", "tenant_id", "normalized_unit", "normalized_quantity");`);
17
+
18
+ this.addSql(`alter table "sales_credit_memo_lines" add column "normalized_quantity" numeric(18,6) not null default '0', add column "normalized_unit" text null, add column "uom_snapshot" jsonb null;`);
19
+ this.addSql(`update "sales_credit_memo_lines" set "normalized_quantity" = coalesce("quantity", '0'), "normalized_unit" = "quantity_unit" where "uom_snapshot" is null;`);
20
+ this.addSql(`create index "sales_credit_memo_lines_normalized_idx" on "sales_credit_memo_lines" ("organization_id", "tenant_id", "normalized_unit", "normalized_quantity");`);
21
+ }
22
+
23
+ override async down(): Promise<void> {
24
+ this.addSql(`drop index "sales_quote_lines_normalized_idx";`);
25
+ this.addSql(`alter table "sales_quote_lines" drop column "normalized_quantity", drop column "normalized_unit", drop column "uom_snapshot";`);
26
+
27
+ this.addSql(`drop index "sales_order_lines_normalized_idx";`);
28
+ this.addSql(`alter table "sales_order_lines" drop column "normalized_quantity", drop column "normalized_unit", drop column "uom_snapshot";`);
29
+
30
+ this.addSql(`drop index "sales_invoice_lines_normalized_idx";`);
31
+ this.addSql(`alter table "sales_invoice_lines" drop column "normalized_quantity", drop column "normalized_unit", drop column "uom_snapshot";`);
32
+
33
+ this.addSql(`drop index "sales_credit_memo_lines_normalized_idx";`);
34
+ this.addSql(`alter table "sales_credit_memo_lines" drop column "normalized_quantity", drop column "normalized_unit", drop column "uom_snapshot";`);
35
+ }
36
+
37
+ }
@@ -0,0 +1,73 @@
1
+ import { Migration } from "@mikro-orm/migrations";
2
+
3
+ export class Migration20260219084501 extends Migration {
4
+ override async up(): Promise<void> {
5
+ this.addSql(
6
+ `update "sales_quote_lines" set "quantity_unit" = 'pc' where lower(btrim(coalesce("quantity_unit", ''))) = 'qty';`,
7
+ );
8
+ this.addSql(
9
+ `update "sales_quote_lines" set "normalized_unit" = 'pc' where lower(btrim(coalesce("normalized_unit", ''))) = 'qty';`,
10
+ );
11
+ this.addSql(
12
+ `update "sales_quote_lines" set "uom_snapshot" = jsonb_set("uom_snapshot", '{enteredUnitCode}', '"pc"'::jsonb, true) where "uom_snapshot" is not null and lower(coalesce("uom_snapshot"->>'enteredUnitCode', '')) = 'qty';`,
13
+ );
14
+ this.addSql(
15
+ `update "sales_quote_lines" set "uom_snapshot" = jsonb_set("uom_snapshot", '{baseUnitCode}', '"pc"'::jsonb, true) where "uom_snapshot" is not null and lower(coalesce("uom_snapshot"->>'baseUnitCode', '')) = 'qty';`,
16
+ );
17
+ this.addSql(
18
+ `update "sales_quote_lines" set "uom_snapshot" = jsonb_set("uom_snapshot", '{unitPriceReference,referenceUnitCode}', '"pc"'::jsonb, true) where "uom_snapshot" is not null and lower(coalesce("uom_snapshot"#>>'{unitPriceReference,referenceUnitCode}', '')) = 'qty';`,
19
+ );
20
+
21
+ this.addSql(
22
+ `update "sales_order_lines" set "quantity_unit" = 'pc' where lower(btrim(coalesce("quantity_unit", ''))) = 'qty';`,
23
+ );
24
+ this.addSql(
25
+ `update "sales_order_lines" set "normalized_unit" = 'pc' where lower(btrim(coalesce("normalized_unit", ''))) = 'qty';`,
26
+ );
27
+ this.addSql(
28
+ `update "sales_order_lines" set "uom_snapshot" = jsonb_set("uom_snapshot", '{enteredUnitCode}', '"pc"'::jsonb, true) where "uom_snapshot" is not null and lower(coalesce("uom_snapshot"->>'enteredUnitCode', '')) = 'qty';`,
29
+ );
30
+ this.addSql(
31
+ `update "sales_order_lines" set "uom_snapshot" = jsonb_set("uom_snapshot", '{baseUnitCode}', '"pc"'::jsonb, true) where "uom_snapshot" is not null and lower(coalesce("uom_snapshot"->>'baseUnitCode', '')) = 'qty';`,
32
+ );
33
+ this.addSql(
34
+ `update "sales_order_lines" set "uom_snapshot" = jsonb_set("uom_snapshot", '{unitPriceReference,referenceUnitCode}', '"pc"'::jsonb, true) where "uom_snapshot" is not null and lower(coalesce("uom_snapshot"#>>'{unitPriceReference,referenceUnitCode}', '')) = 'qty';`,
35
+ );
36
+
37
+ this.addSql(
38
+ `update "sales_invoice_lines" set "quantity_unit" = 'pc' where lower(btrim(coalesce("quantity_unit", ''))) = 'qty';`,
39
+ );
40
+ this.addSql(
41
+ `update "sales_invoice_lines" set "normalized_unit" = 'pc' where lower(btrim(coalesce("normalized_unit", ''))) = 'qty';`,
42
+ );
43
+ this.addSql(
44
+ `update "sales_invoice_lines" set "uom_snapshot" = jsonb_set("uom_snapshot", '{enteredUnitCode}', '"pc"'::jsonb, true) where "uom_snapshot" is not null and lower(coalesce("uom_snapshot"->>'enteredUnitCode', '')) = 'qty';`,
45
+ );
46
+ this.addSql(
47
+ `update "sales_invoice_lines" set "uom_snapshot" = jsonb_set("uom_snapshot", '{baseUnitCode}', '"pc"'::jsonb, true) where "uom_snapshot" is not null and lower(coalesce("uom_snapshot"->>'baseUnitCode', '')) = 'qty';`,
48
+ );
49
+ this.addSql(
50
+ `update "sales_invoice_lines" set "uom_snapshot" = jsonb_set("uom_snapshot", '{unitPriceReference,referenceUnitCode}', '"pc"'::jsonb, true) where "uom_snapshot" is not null and lower(coalesce("uom_snapshot"#>>'{unitPriceReference,referenceUnitCode}', '')) = 'qty';`,
51
+ );
52
+
53
+ this.addSql(
54
+ `update "sales_credit_memo_lines" set "quantity_unit" = 'pc' where lower(btrim(coalesce("quantity_unit", ''))) = 'qty';`,
55
+ );
56
+ this.addSql(
57
+ `update "sales_credit_memo_lines" set "normalized_unit" = 'pc' where lower(btrim(coalesce("normalized_unit", ''))) = 'qty';`,
58
+ );
59
+ this.addSql(
60
+ `update "sales_credit_memo_lines" set "uom_snapshot" = jsonb_set("uom_snapshot", '{enteredUnitCode}', '"pc"'::jsonb, true) where "uom_snapshot" is not null and lower(coalesce("uom_snapshot"->>'enteredUnitCode', '')) = 'qty';`,
61
+ );
62
+ this.addSql(
63
+ `update "sales_credit_memo_lines" set "uom_snapshot" = jsonb_set("uom_snapshot", '{baseUnitCode}', '"pc"'::jsonb, true) where "uom_snapshot" is not null and lower(coalesce("uom_snapshot"->>'baseUnitCode', '')) = 'qty';`,
64
+ );
65
+ this.addSql(
66
+ `update "sales_credit_memo_lines" set "uom_snapshot" = jsonb_set("uom_snapshot", '{unitPriceReference,referenceUnitCode}', '"pc"'::jsonb, true) where "uom_snapshot" is not null and lower(coalesce("uom_snapshot"#>>'{unitPriceReference,referenceUnitCode}', '')) = 'qty';`,
67
+ );
68
+ }
69
+
70
+ override async down(): Promise<void> {
71
+ // Intentionally empty: UoM column additions and data migrations are not reversible
72
+ }
73
+ }
@@ -473,6 +473,10 @@ export const searchConfig: SearchModuleConfig = {
473
473
  entityId: 'sales:sales_order_line',
474
474
  enabled: true,
475
475
  priority: 7,
476
+ fieldPolicy: {
477
+ searchable: ['name', 'description', 'quantity_unit', 'normalized_unit'],
478
+ excluded: ['uom_snapshot', 'metadata', 'catalog_snapshot', 'configuration', 'promotion_snapshot'],
479
+ },
476
480
  buildSource: async (ctx) => {
477
481
  const { t: translate } = await resolveTranslations()
478
482
  const record = ctx.record
@@ -481,6 +485,9 @@ export const searchConfig: SearchModuleConfig = {
481
485
  appendLine(lines, 'Description', record.description)
482
486
  appendLine(lines, 'Kind', record.kind)
483
487
  appendLine(lines, 'Quantity', record.quantity)
488
+ appendLine(lines, 'Quantity unit', record.quantity_unit ?? record.quantityUnit)
489
+ appendLine(lines, 'Normalized quantity', record.normalized_quantity ?? record.normalizedQuantity)
490
+ appendLine(lines, 'Normalized unit', record.normalized_unit ?? record.normalizedUnit)
484
491
  appendLine(lines, 'Status', record.status)
485
492
  return buildIndexSource(ctx, buildOrderLinePresenter(translate, record), lines)
486
493
  },
@@ -494,6 +501,10 @@ export const searchConfig: SearchModuleConfig = {
494
501
  entityId: 'sales:sales_quote_line',
495
502
  enabled: true,
496
503
  priority: 7,
504
+ fieldPolicy: {
505
+ searchable: ['name', 'description', 'quantity_unit', 'normalized_unit'],
506
+ excluded: ['uom_snapshot', 'metadata', 'catalog_snapshot', 'configuration', 'promotion_snapshot'],
507
+ },
497
508
  buildSource: async (ctx) => {
498
509
  const { t: translate } = await resolveTranslations()
499
510
  const record = ctx.record
@@ -502,6 +513,9 @@ export const searchConfig: SearchModuleConfig = {
502
513
  appendLine(lines, 'Description', record.description)
503
514
  appendLine(lines, 'Kind', record.kind)
504
515
  appendLine(lines, 'Quantity', record.quantity)
516
+ appendLine(lines, 'Quantity unit', record.quantity_unit ?? record.quantityUnit)
517
+ appendLine(lines, 'Normalized quantity', record.normalized_quantity ?? record.normalizedQuantity)
518
+ appendLine(lines, 'Normalized unit', record.normalized_unit ?? record.normalizedUnit)
505
519
  appendLine(lines, 'Status', record.status)
506
520
  return buildIndexSource(ctx, buildQuoteLinePresenter(translate, record), lines)
507
521
  },
@@ -613,6 +627,10 @@ export const searchConfig: SearchModuleConfig = {
613
627
  entityId: 'sales:sales_invoice_line',
614
628
  enabled: true,
615
629
  priority: 5,
630
+ fieldPolicy: {
631
+ searchable: ['description', 'quantity_unit', 'normalized_unit'],
632
+ excluded: ['uom_snapshot', 'metadata'],
633
+ },
616
634
  buildSource: async (ctx) => {
617
635
  const { t: translate } = await resolveTranslations()
618
636
  const record = ctx.record
@@ -620,6 +638,9 @@ export const searchConfig: SearchModuleConfig = {
620
638
  appendLine(lines, 'Description', record.description)
621
639
  appendLine(lines, 'Kind', record.kind)
622
640
  appendLine(lines, 'Quantity', record.quantity)
641
+ appendLine(lines, 'Quantity unit', record.quantity_unit ?? record.quantityUnit)
642
+ appendLine(lines, 'Normalized quantity', record.normalized_quantity ?? record.normalizedQuantity)
643
+ appendLine(lines, 'Normalized unit', record.normalized_unit ?? record.normalizedUnit)
623
644
  return buildIndexSource(ctx, buildInvoiceLinePresenter(translate, record), lines)
624
645
  },
625
646
  formatResult: async (ctx) => {
@@ -652,6 +673,10 @@ export const searchConfig: SearchModuleConfig = {
652
673
  entityId: 'sales:sales_credit_memo_line',
653
674
  enabled: true,
654
675
  priority: 5,
676
+ fieldPolicy: {
677
+ searchable: ['description', 'quantity_unit', 'normalized_unit'],
678
+ excluded: ['uom_snapshot', 'metadata'],
679
+ },
655
680
  buildSource: async (ctx) => {
656
681
  const { t: translate } = await resolveTranslations()
657
682
  const record = ctx.record
@@ -659,6 +684,9 @@ export const searchConfig: SearchModuleConfig = {
659
684
  appendLine(lines, 'Description', record.description)
660
685
  appendLine(lines, 'Kind', record.kind)
661
686
  appendLine(lines, 'Quantity', record.quantity)
687
+ appendLine(lines, 'Quantity unit', record.quantity_unit ?? record.quantityUnit)
688
+ appendLine(lines, 'Normalized quantity', record.normalized_quantity ?? record.normalizedQuantity)
689
+ appendLine(lines, 'Normalized unit', record.normalized_unit ?? record.normalizedUnit)
662
690
  return buildIndexSource(ctx, buildCreditMemoLinePresenter(translate, record), lines)
663
691
  },
664
692
  formatResult: async (ctx) => {
@@ -1310,6 +1310,9 @@ export async function seedSalesExamples(
1310
1310
  comment: line.comment ?? null,
1311
1311
  quantity: line.quantity,
1312
1312
  quantityUnit: line.quantityUnit ?? null,
1313
+ normalizedQuantity: line.quantity,
1314
+ normalizedUnit: line.quantityUnit ?? null,
1315
+ uomSnapshot: null,
1313
1316
  currencyCode,
1314
1317
  unitPriceNet,
1315
1318
  unitPriceGross,
@@ -1398,6 +1401,12 @@ export async function seedSalesExamples(
1398
1401
  comment: source.comment,
1399
1402
  quantity: toAmount(source.quantity),
1400
1403
  quantityUnit: source.quantityUnit ?? null,
1404
+ normalizedQuantity: toAmount(source.normalizedQuantity ?? source.quantity),
1405
+ normalizedUnit: source.normalizedUnit ?? source.quantityUnit ?? null,
1406
+ uomSnapshot:
1407
+ source.uomSnapshot && typeof source.uomSnapshot === 'object'
1408
+ ? (toSnapshot(source.uomSnapshot as Record<string, unknown>) as typeof source.uomSnapshot)
1409
+ : null,
1401
1410
  currencyCode: source.currencyCode,
1402
1411
  unitPriceNet: toAmount(source.unitPriceNet ?? 0),
1403
1412
  unitPriceGross: toAmount(source.unitPriceGross ?? source.unitPriceNet ?? 0),
@@ -1421,7 +1430,8 @@ export async function seedSalesExamples(
1421
1430
  })
1422
1431
 
1423
1432
  calculation.adjustments.forEach((adj, idx) => {
1424
- const lineIndex = adj.metadata && typeof adj.metadata === 'object' ? (adj.metadata as any).lineIndex : null
1433
+ const adjMeta = adj.metadata && typeof adj.metadata === 'object' ? (adj.metadata as Record<string, unknown>) : null
1434
+ const lineIndex = adjMeta ? (adjMeta.lineIndex as number | null) : null
1425
1435
  const lineRef =
1426
1436
  typeof lineIndex === 'number' && lineIndex >= 0 && lineIndex < seed.lines.length
1427
1437
  ? lineSnapshots[lineIndex]?.id ?? null
@@ -1520,6 +1530,9 @@ export async function seedSalesExamples(
1520
1530
  comment: line.comment ?? null,
1521
1531
  quantity: line.quantity,
1522
1532
  quantityUnit: line.quantityUnit ?? null,
1533
+ normalizedQuantity: line.quantity,
1534
+ normalizedUnit: line.quantityUnit ?? null,
1535
+ uomSnapshot: null,
1523
1536
  currencyCode,
1524
1537
  unitPriceNet,
1525
1538
  unitPriceGross,
@@ -1617,6 +1630,12 @@ export async function seedSalesExamples(
1617
1630
  comment: source.comment,
1618
1631
  quantity: toAmount(source.quantity),
1619
1632
  quantityUnit: source.quantityUnit ?? null,
1633
+ normalizedQuantity: toAmount(source.normalizedQuantity ?? source.quantity),
1634
+ normalizedUnit: source.normalizedUnit ?? source.quantityUnit ?? null,
1635
+ uomSnapshot:
1636
+ source.uomSnapshot && typeof source.uomSnapshot === 'object'
1637
+ ? (toSnapshot(source.uomSnapshot as Record<string, unknown>) as typeof source.uomSnapshot)
1638
+ : null,
1620
1639
  reservedQuantity: '0',
1621
1640
  fulfilledQuantity: '0',
1622
1641
  invoicedQuantity: '0',
@@ -125,7 +125,7 @@ function TimelineItem({
125
125
  const isStatusChange = entry.kind === 'status' && entry.metadata?.statusTo
126
126
 
127
127
  return (
128
- <div className="relative flex gap-3">
128
+ <div data-testid="timeline-entry" className="relative flex gap-3">
129
129
  {/* Vertical connector line */}
130
130
  {!isLast && (
131
131
  <div className="absolute left-[11px] top-6 bottom-0 w-px bg-border" aria-hidden />
@@ -1000,15 +1000,15 @@
1000
1000
  "workflows.visualEditor.clearDescription": "Dies löscht alle Metadaten und die Workflow-Arbeitsfläche. Diese Aktion kann nicht rückgängig gemacht werden.",
1001
1001
  "workflows.visualEditor.clearTitle": "Alles löschen?",
1002
1002
  "workflows.visualEditor.hideMetadata": "Metadaten ausblenden",
1003
- "workflows.visualEditor.loadExample": "Beispiel laden",
1004
- "workflows.visualEditor.runTest": "Test ausführen",
1005
- "workflows.visualEditor.showMetadata": "Metadaten anzeigen",
1006
- "workflows.visualEditor.validate": "Validieren",
1007
1003
  "workflows.visualEditor.hint.addSteps": "Klicke auf Schritttypen, um sie hinzuzufügen",
1008
1004
  "workflows.visualEditor.hint.connectSteps": "Verbinde Schritte durch Ziehen von den Griffen",
1009
1005
  "workflows.visualEditor.hint.dragSteps": "Ziehe Schritte, um sie zu positionieren",
1010
1006
  "workflows.visualEditor.hint.editSteps": "Klicke auf Schritte und Übergänge, um sie zu bearbeiten",
1011
1007
  "workflows.visualEditor.hint.validate": "Vor dem Speichern validieren",
1012
1008
  "workflows.visualEditor.howToUse": "Anleitung:",
1009
+ "workflows.visualEditor.loadExample": "Beispiel laden",
1010
+ "workflows.visualEditor.runTest": "Test ausführen",
1011
+ "workflows.visualEditor.showMetadata": "Metadaten anzeigen",
1012
+ "workflows.visualEditor.validate": "Validieren",
1013
1013
  "workflows.visualEditor.workflowMetadata": "Workflow-Metadaten"
1014
1014
  }
@@ -1000,15 +1000,15 @@
1000
1000
  "workflows.visualEditor.clearDescription": "This will clear all metadata and the workflow canvas. This action cannot be undone.",
1001
1001
  "workflows.visualEditor.clearTitle": "Clear Everything?",
1002
1002
  "workflows.visualEditor.hideMetadata": "Hide Metadata",
1003
- "workflows.visualEditor.loadExample": "Load Example",
1004
- "workflows.visualEditor.runTest": "Run Test",
1005
- "workflows.visualEditor.showMetadata": "Show Metadata",
1006
- "workflows.visualEditor.validate": "Validate",
1007
1003
  "workflows.visualEditor.hint.addSteps": "Click step types to add them",
1008
1004
  "workflows.visualEditor.hint.connectSteps": "Connect steps by dragging from handles",
1009
1005
  "workflows.visualEditor.hint.dragSteps": "Drag steps to position them",
1010
1006
  "workflows.visualEditor.hint.editSteps": "Click steps and transitions to edit them",
1011
1007
  "workflows.visualEditor.hint.validate": "Validate before saving",
1012
1008
  "workflows.visualEditor.howToUse": "How to use:",
1009
+ "workflows.visualEditor.loadExample": "Load Example",
1010
+ "workflows.visualEditor.runTest": "Run Test",
1011
+ "workflows.visualEditor.showMetadata": "Show Metadata",
1012
+ "workflows.visualEditor.validate": "Validate",
1013
1013
  "workflows.visualEditor.workflowMetadata": "Workflow Metadata"
1014
1014
  }
@@ -1000,15 +1000,15 @@
1000
1000
  "workflows.visualEditor.clearDescription": "Esto borrará todos los metadatos y el lienzo del flujo de trabajo. Esta acción no se puede deshacer.",
1001
1001
  "workflows.visualEditor.clearTitle": "¿Borrar todo?",
1002
1002
  "workflows.visualEditor.hideMetadata": "Ocultar metadatos",
1003
- "workflows.visualEditor.loadExample": "Cargar ejemplo",
1004
- "workflows.visualEditor.runTest": "Ejecutar prueba",
1005
- "workflows.visualEditor.showMetadata": "Mostrar metadatos",
1006
- "workflows.visualEditor.validate": "Validar",
1007
1003
  "workflows.visualEditor.hint.addSteps": "Haz clic en los tipos de pasos para agregarlos",
1008
1004
  "workflows.visualEditor.hint.connectSteps": "Conecta pasos arrastrando desde los conectores",
1009
1005
  "workflows.visualEditor.hint.dragSteps": "Arrastra los pasos para posicionarlos",
1010
1006
  "workflows.visualEditor.hint.editSteps": "Haz clic en pasos y transiciones para editarlos",
1011
1007
  "workflows.visualEditor.hint.validate": "Valida antes de guardar",
1012
1008
  "workflows.visualEditor.howToUse": "Cómo usar:",
1009
+ "workflows.visualEditor.loadExample": "Cargar ejemplo",
1010
+ "workflows.visualEditor.runTest": "Ejecutar prueba",
1011
+ "workflows.visualEditor.showMetadata": "Mostrar metadatos",
1012
+ "workflows.visualEditor.validate": "Validar",
1013
1013
  "workflows.visualEditor.workflowMetadata": "Metadatos del flujo de trabajo"
1014
1014
  }
@@ -1000,15 +1000,15 @@
1000
1000
  "workflows.visualEditor.clearDescription": "Spowoduje to wyczyszczenie wszystkich metadanych i obszaru roboczego przepływu pracy. Tej czynności nie można cofnąć.",
1001
1001
  "workflows.visualEditor.clearTitle": "Wyczyścić wszystko?",
1002
1002
  "workflows.visualEditor.hideMetadata": "Ukryj metadane",
1003
- "workflows.visualEditor.loadExample": "Załaduj przykład",
1004
- "workflows.visualEditor.runTest": "Uruchom test",
1005
- "workflows.visualEditor.showMetadata": "Pokaż metadane",
1006
- "workflows.visualEditor.validate": "Waliduj",
1007
1003
  "workflows.visualEditor.hint.addSteps": "Kliknij typy kroków, aby je dodać",
1008
1004
  "workflows.visualEditor.hint.connectSteps": "Łącz kroki, przeciągając z uchwytów",
1009
1005
  "workflows.visualEditor.hint.dragSteps": "Przeciągaj kroki, aby je rozmieścić",
1010
1006
  "workflows.visualEditor.hint.editSteps": "Kliknij kroki i przejścia, aby je edytować",
1011
1007
  "workflows.visualEditor.hint.validate": "Zwaliduj przed zapisaniem",
1012
1008
  "workflows.visualEditor.howToUse": "Jak używać:",
1009
+ "workflows.visualEditor.loadExample": "Załaduj przykład",
1010
+ "workflows.visualEditor.runTest": "Uruchom test",
1011
+ "workflows.visualEditor.showMetadata": "Pokaż metadane",
1012
+ "workflows.visualEditor.validate": "Waliduj",
1013
1013
  "workflows.visualEditor.workflowMetadata": "Metadane przepływu pracy"
1014
1014
  }