@open-mercato/core 0.6.6-develop.6543.1.49fadb4c31 → 0.6.6

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 (166) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/api_keys/api/keys/route.js +9 -23
  3. package/dist/modules/api_keys/api/keys/route.js.map +2 -2
  4. package/dist/modules/api_keys/cli.js +1 -2
  5. package/dist/modules/api_keys/cli.js.map +2 -2
  6. package/dist/modules/attachments/api/file/[id]/route.js +7 -9
  7. package/dist/modules/attachments/api/file/[id]/route.js.map +2 -2
  8. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +7 -9
  9. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
  10. package/dist/modules/attachments/api/route.js +17 -21
  11. package/dist/modules/attachments/api/route.js.map +2 -2
  12. package/dist/modules/auth/api/users/route.js +2 -6
  13. package/dist/modules/auth/api/users/route.js.map +2 -2
  14. package/dist/modules/auth/cli.js +2 -3
  15. package/dist/modules/auth/cli.js.map +2 -2
  16. package/dist/modules/configs/lib/upgrade-actions.js +0 -22
  17. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  18. package/dist/modules/customer_accounts/widgets/injection-table.js +2 -0
  19. package/dist/modules/customer_accounts/widgets/injection-table.js.map +2 -2
  20. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +1 -7
  21. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  22. package/dist/modules/customers/components/detail/CompanyDetailTabs.js +7 -11
  23. package/dist/modules/customers/components/detail/CompanyDetailTabs.js.map +2 -2
  24. package/dist/modules/dashboards/api/roles/widgets/route.js +3 -22
  25. package/dist/modules/dashboards/api/roles/widgets/route.js.map +2 -2
  26. package/dist/modules/dashboards/api/users/widgets/route.js +3 -22
  27. package/dist/modules/dashboards/api/users/widgets/route.js.map +2 -2
  28. package/dist/modules/dashboards/cli.js +3 -4
  29. package/dist/modules/dashboards/cli.js.map +2 -2
  30. package/dist/modules/dashboards/lib/widgetAssignmentScope.js +1 -9
  31. package/dist/modules/dashboards/lib/widgetAssignmentScope.js.map +2 -2
  32. package/dist/modules/dictionaries/commands/entries.js +14 -7
  33. package/dist/modules/dictionaries/commands/entries.js.map +2 -2
  34. package/dist/modules/dictionaries/commands/entry-operations.js +16 -7
  35. package/dist/modules/dictionaries/commands/entry-operations.js.map +2 -2
  36. package/dist/modules/dictionaries/commands/factory.js +3 -9
  37. package/dist/modules/dictionaries/commands/factory.js.map +2 -2
  38. package/dist/modules/dictionaries/fields/dictionary.js +41 -56
  39. package/dist/modules/dictionaries/fields/dictionary.js.map +2 -2
  40. package/dist/modules/directory/utils/organizationScope.js +3 -10
  41. package/dist/modules/directory/utils/organizationScope.js.map +2 -2
  42. package/dist/modules/entities/api/records.js +4 -5
  43. package/dist/modules/entities/api/records.js.map +2 -2
  44. package/dist/modules/entities/backend/entities/system/[entityId]/page.meta.js +0 -1
  45. package/dist/modules/entities/backend/entities/system/[entityId]/page.meta.js.map +2 -2
  46. package/dist/modules/entities/backend/entities/user/[entityId]/page.js +49 -49
  47. package/dist/modules/entities/backend/entities/user/[entityId]/page.js.map +2 -2
  48. package/dist/modules/entities/backend/entities/user/[entityId]/page.meta.js +0 -1
  49. package/dist/modules/entities/backend/entities/user/[entityId]/page.meta.js.map +2 -2
  50. package/dist/modules/entities/cli.js +1 -2
  51. package/dist/modules/entities/cli.js.map +2 -2
  52. package/dist/modules/query_index/api/status.js +8 -2
  53. package/dist/modules/query_index/api/status.js.map +2 -2
  54. package/dist/modules/sales/commands/documents.js +0 -44
  55. package/dist/modules/sales/commands/documents.js.map +2 -2
  56. package/dist/modules/sales/commands/returns.js +27 -2
  57. package/dist/modules/sales/commands/returns.js.map +2 -2
  58. package/dist/modules/sales/components/documents/AddressesSection.js +2 -6
  59. package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
  60. package/dist/modules/sales/components/documents/ItemsSection.js +0 -1
  61. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  62. package/dist/modules/sales/components/documents/LineItemDialog.js +0 -9
  63. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  64. package/dist/modules/sales/components/documents/SalesDocumentForm.js +0 -44
  65. package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
  66. package/dist/modules/sales/components/documents/ShipmentDialog.js +38 -59
  67. package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
  68. package/dist/modules/sales/lib/shipments/snapshots.js +1 -27
  69. package/dist/modules/sales/lib/shipments/snapshots.js.map +2 -2
  70. package/dist/modules/workflows/frontend/checkout-demo/page.js +8 -1
  71. package/dist/modules/workflows/frontend/checkout-demo/page.js.map +2 -2
  72. package/dist/modules/workflows/lib/activity-executor.js +1 -1
  73. package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
  74. package/dist/modules/workflows/lib/find-definition.js +18 -2
  75. package/dist/modules/workflows/lib/find-definition.js.map +2 -2
  76. package/dist/modules/workflows/lib/signal-handler.js +3 -2
  77. package/dist/modules/workflows/lib/signal-handler.js.map +2 -2
  78. package/dist/modules/workflows/lib/step-handler.js +4 -8
  79. package/dist/modules/workflows/lib/step-handler.js.map +2 -2
  80. package/dist/modules/workflows/lib/task-handler.js +3 -5
  81. package/dist/modules/workflows/lib/task-handler.js.map +2 -2
  82. package/dist/modules/workflows/lib/timer-handler.js +2 -1
  83. package/dist/modules/workflows/lib/timer-handler.js.map +2 -2
  84. package/dist/modules/workflows/lib/transition-handler.js +5 -13
  85. package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
  86. package/dist/modules/workflows/lib/workflow-executor.js +11 -9
  87. package/dist/modules/workflows/lib/workflow-executor.js.map +2 -2
  88. package/dist/modules/workflows/migrations/Migration20260715120000.js +68 -0
  89. package/dist/modules/workflows/migrations/Migration20260715120000.js.map +7 -0
  90. package/dist/modules/workflows/migrations/Migration20260716120000.js +24 -0
  91. package/dist/modules/workflows/migrations/Migration20260716120000.js.map +7 -0
  92. package/dist/modules/workflows/workflows.js +2 -0
  93. package/dist/modules/workflows/workflows.js.map +2 -2
  94. package/package.json +8 -9
  95. package/src/modules/api_keys/api/keys/route.ts +9 -27
  96. package/src/modules/api_keys/cli.ts +3 -2
  97. package/src/modules/attachments/api/file/[id]/route.ts +7 -11
  98. package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +7 -11
  99. package/src/modules/attachments/api/route.ts +21 -26
  100. package/src/modules/auth/api/users/route.ts +2 -11
  101. package/src/modules/auth/cli.ts +2 -3
  102. package/src/modules/configs/i18n/de.json +1 -5
  103. package/src/modules/configs/i18n/en.json +1 -5
  104. package/src/modules/configs/i18n/es.json +1 -5
  105. package/src/modules/configs/i18n/pl.json +1 -5
  106. package/src/modules/configs/lib/upgrade-actions.ts +0 -24
  107. package/src/modules/customer_accounts/AGENTS.md +6 -8
  108. package/src/modules/customer_accounts/widgets/injection-table.ts +2 -0
  109. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +1 -11
  110. package/src/modules/customers/components/detail/CompanyDetailTabs.tsx +7 -13
  111. package/src/modules/dashboards/api/roles/widgets/route.ts +3 -22
  112. package/src/modules/dashboards/api/users/widgets/route.ts +3 -22
  113. package/src/modules/dashboards/cli.ts +9 -4
  114. package/src/modules/dashboards/lib/widgetAssignmentScope.ts +0 -18
  115. package/src/modules/dictionaries/commands/entries.ts +16 -7
  116. package/src/modules/dictionaries/commands/entry-operations.ts +18 -8
  117. package/src/modules/dictionaries/commands/factory.ts +2 -8
  118. package/src/modules/dictionaries/fields/dictionary.tsx +32 -55
  119. package/src/modules/dictionaries/i18n/de.json +0 -2
  120. package/src/modules/dictionaries/i18n/en.json +0 -2
  121. package/src/modules/dictionaries/i18n/es.json +0 -2
  122. package/src/modules/dictionaries/i18n/pl.json +0 -2
  123. package/src/modules/directory/utils/organizationScope.ts +2 -36
  124. package/src/modules/entities/api/records.ts +7 -5
  125. package/src/modules/entities/backend/entities/system/[entityId]/page.meta.ts +0 -1
  126. package/src/modules/entities/backend/entities/user/[entityId]/page.meta.ts +0 -1
  127. package/src/modules/entities/backend/entities/user/[entityId]/page.tsx +49 -51
  128. package/src/modules/entities/cli.ts +1 -2
  129. package/src/modules/entities/i18n/de.json +0 -34
  130. package/src/modules/entities/i18n/en.json +0 -34
  131. package/src/modules/entities/i18n/es.json +0 -34
  132. package/src/modules/entities/i18n/pl.json +0 -34
  133. package/src/modules/query_index/api/status.ts +8 -2
  134. package/src/modules/sales/commands/documents.ts +0 -65
  135. package/src/modules/sales/commands/returns.ts +32 -2
  136. package/src/modules/sales/components/documents/AddressesSection.tsx +2 -6
  137. package/src/modules/sales/components/documents/ItemsSection.tsx +0 -5
  138. package/src/modules/sales/components/documents/LineItemDialog.tsx +0 -10
  139. package/src/modules/sales/components/documents/SalesDocumentForm.tsx +0 -46
  140. package/src/modules/sales/components/documents/ShipmentDialog.tsx +40 -65
  141. package/src/modules/sales/i18n/de.json +0 -4
  142. package/src/modules/sales/i18n/en.json +0 -4
  143. package/src/modules/sales/i18n/es.json +0 -4
  144. package/src/modules/sales/i18n/pl.json +0 -4
  145. package/src/modules/sales/lib/shipments/snapshots.ts +0 -30
  146. package/src/modules/workflows/frontend/checkout-demo/page.tsx +12 -1
  147. package/src/modules/workflows/lib/activity-executor.ts +22 -2
  148. package/src/modules/workflows/lib/find-definition.ts +53 -1
  149. package/src/modules/workflows/lib/signal-handler.ts +5 -4
  150. package/src/modules/workflows/lib/step-handler.ts +4 -8
  151. package/src/modules/workflows/lib/task-handler.ts +2 -4
  152. package/src/modules/workflows/lib/timer-handler.ts +3 -2
  153. package/src/modules/workflows/lib/transition-handler.ts +5 -13
  154. package/src/modules/workflows/lib/workflow-executor.ts +11 -8
  155. package/src/modules/workflows/migrations/Migration20260715120000.ts +114 -0
  156. package/src/modules/workflows/migrations/Migration20260716120000.ts +57 -0
  157. package/src/modules/workflows/workflows.ts +2 -0
  158. package/dist/modules/attachments/lib/reconcileOrganization.js +0 -110
  159. package/dist/modules/attachments/lib/reconcileOrganization.js.map +0 -7
  160. package/dist/modules/attachments/lib/requestScope.js +0 -10
  161. package/dist/modules/attachments/lib/requestScope.js.map +0 -7
  162. package/dist/modules/customers/components/detail/useDealsAccess.js +0 -16
  163. package/dist/modules/customers/components/detail/useDealsAccess.js.map +0 -7
  164. package/src/modules/attachments/lib/reconcileOrganization.ts +0 -188
  165. package/src/modules/attachments/lib/requestScope.ts +0 -27
  166. package/src/modules/customers/components/detail/useDealsAccess.ts +0 -19
@@ -93,7 +93,6 @@
93
93
  "entities.customFields.editor.noFieldsets": "Nie zdefiniowano jeszcze zestawów pól. Zestawy pól pozwalają grupować pola niestandardowe dla różnych wariantów tej samej encji (np. produkty Moda vs Sport).",
94
94
  "entities.customFields.editor.noGroup": "Bez grupy",
95
95
  "entities.customFields.editor.noOptionsDefined": "Nie zdefiniowano opcji.",
96
- "entities.customFields.editor.noParameterPlaceholder": "-",
97
96
  "entities.customFields.editor.noValidationRules": "Nie zdefiniowano reguł walidacji.",
98
97
  "entities.customFields.editor.optionLabelPlaceholder": "Etykieta widoczna dla użytkowników (opcjonalnie)",
99
98
  "entities.customFields.editor.options": "Opcje",
@@ -119,17 +118,6 @@
119
118
  "entities.customFields.editor.unassignedFields": "Pola nieprzypisane",
120
119
  "entities.customFields.editor.unitsOptional": "Jednostki (opcjonalnie)",
121
120
  "entities.customFields.editor.unitsPlaceholder": "kg, cm itp.",
122
- "entities.customFields.editor.validation.date": "data",
123
- "entities.customFields.editor.validation.eq": "równe",
124
- "entities.customFields.editor.validation.float": "liczba zmiennoprzecinkowa",
125
- "entities.customFields.editor.validation.gt": "większe niż",
126
- "entities.customFields.editor.validation.gte": "większe lub równe",
127
- "entities.customFields.editor.validation.integer": "liczba całkowita",
128
- "entities.customFields.editor.validation.lt": "mniejsze niż",
129
- "entities.customFields.editor.validation.lte": "mniejsze lub równe",
130
- "entities.customFields.editor.validation.ne": "różne",
131
- "entities.customFields.editor.validation.regex": "regex",
132
- "entities.customFields.editor.validation.required": "wymagane",
133
121
  "entities.customFields.editor.validationRules": "Reguły walidacji",
134
122
  "entities.customFields.editor.value": "Wartość",
135
123
  "entities.customFields.editor.valueRequired": "Wartość jest wymagana",
@@ -195,28 +183,6 @@
195
183
  "entities.nav.systemEntities": "Encje systemowe",
196
184
  "entities.nav.userEntities": "Encje użytkownika",
197
185
  "entities.nav.userEntities.create": "Utwórz encję",
198
- "entities.userEntities.edit.errors.deleteEntityFailed": "Nie udało się usunąć encji",
199
- "entities.userEntities.edit.errors.invalidEntityDescription": "Brakuje identyfikatora encji albo jest on nieprawidłowy.",
200
- "entities.userEntities.edit.errors.invalidEntityId": "Nieprawidłowy identyfikator encji",
201
- "entities.userEntities.edit.errors.invalidEntityTitle": "Nieprawidłowa encja",
202
- "entities.userEntities.edit.errors.loadDefinitionsFailed": "Nie udało się wczytać definicji encji",
203
- "entities.userEntities.edit.errors.loadFailed": "Nie udało się wczytać",
204
- "entities.userEntities.edit.errors.loadMetadataFailed": "Nie udało się wczytać metadanych encji",
205
- "entities.userEntities.edit.errors.reloadDefinitionsFailed": "Nie udało się ponownie wczytać definicji pól",
206
- "entities.userEntities.edit.errors.restoreFieldFailed": "Nie udało się przywrócić pola",
207
- "entities.userEntities.edit.errors.saveEntityFailed": "Nie udało się zapisać encji",
208
- "entities.userEntities.edit.errors.saveOrderFailed": "Nie udało się zapisać kolejności",
209
- "entities.userEntities.edit.errors.validationFailed": "Walidacja nie powiodła się",
210
- "entities.userEntities.edit.fieldsetTitle": "Edytuj zestaw pól: {{name}}",
211
- "entities.userEntities.edit.flash.entityDeleted": "Encja została usunięta",
212
- "entities.userEntities.edit.flash.orderSaved": "Kolejność została zapisana",
213
- "entities.userEntities.edit.flash.restoredField": "Przywrócono {{key}}",
214
- "entities.userEntities.edit.groups.definitions": "Definicje pól",
215
- "entities.userEntities.edit.groups.settings": "Ustawienia encji",
216
- "entities.userEntities.edit.orderNotice": "Zmieniono kolejność - zapisze się po opuszczeniu pola",
217
- "entities.userEntities.edit.pageTitle": "Edytuj definicje",
218
- "entities.userEntities.edit.showRecords": "Pokaż rekordy",
219
- "entities.userEntities.edit.title": "Edytuj encję: {{entityId}}",
220
186
  "entities.userEntities.errors.entityIdExists": "Identyfikator encji już istnieje",
221
187
  "entities.userEntities.errors.entityIdRequired": "Identyfikator encji jest wymagany",
222
188
  "entities.userEntities.errors.loadFailed": "Nie udało się wczytać encji",
@@ -415,7 +415,10 @@ export async function GET(req: Request) {
415
415
  errorQuery = errorQuery.where('tenant_id' as any, 'is', null as any)
416
416
  }
417
417
  if (Array.isArray(organizationScopeIds) && organizationScopeIds.length) {
418
- errorQuery = errorQuery.where('organization_id' as any, 'in', organizationScopeIds)
418
+ errorQuery = errorQuery.where((eb: any) => eb.or([
419
+ eb('organization_id' as any, 'is', null),
420
+ eb('organization_id' as any, 'in', organizationScopeIds),
421
+ ]))
419
422
  } else {
420
423
  errorQuery = errorQuery.where('organization_id' as any, 'is', null as any)
421
424
  }
@@ -453,7 +456,10 @@ export async function GET(req: Request) {
453
456
  logsQuery = logsQuery.where('tenant_id' as any, 'is', null as any)
454
457
  }
455
458
  if (Array.isArray(organizationScopeIds) && organizationScopeIds.length) {
456
- logsQuery = logsQuery.where('organization_id' as any, 'in', organizationScopeIds)
459
+ logsQuery = logsQuery.where((eb: any) => eb.or([
460
+ eb('organization_id' as any, 'is', null),
461
+ eb('organization_id' as any, 'in', organizationScopeIds),
462
+ ]))
457
463
  } else {
458
464
  logsQuery = logsQuery.where('organization_id' as any, 'is', null as any)
459
465
  }
@@ -125,7 +125,6 @@ import {
125
125
  type SalesLineCalculationResult,
126
126
  type SalesDocumentCalculationResult,
127
127
  } from "../lib/types";
128
- import { loadShippedQuantityByLine } from "../lib/shipments/snapshots";
129
128
  import { resolveDictionaryEntryValue } from "../lib/dictionaries";
130
129
  import { resolveStatusEntryIdByValue } from "../lib/statusHelpers";
131
130
  import { SalesDocumentNumberGenerator } from "../services/salesDocumentNumberGenerator";
@@ -6522,69 +6521,6 @@ const quoteAdjustmentDeleteSchema = z.object({
6522
6521
  quoteId: z.string().uuid(),
6523
6522
  });
6524
6523
 
6525
- const SHIPPED_QUANTITY_TOLERANCE = 1e-6;
6526
-
6527
- const hasNumericChange = (
6528
- next: number | null | undefined,
6529
- previous: number | null | undefined,
6530
- ): boolean => {
6531
- if (next === undefined || next === null) return false;
6532
- if (previous === undefined || previous === null) return true;
6533
- return Math.abs(next - previous) > SHIPPED_QUANTITY_TOLERANCE;
6534
- };
6535
-
6536
- const hasUnitChange = (
6537
- next: string | null | undefined,
6538
- previous: string | null | undefined,
6539
- ): boolean => {
6540
- if (next === undefined || next === null) return false;
6541
- const normalizedPrevious = (previous ?? "").trim().toLowerCase();
6542
- if (!normalizedPrevious) return false;
6543
- return next.trim().toLowerCase() !== normalizedPrevious;
6544
- };
6545
-
6546
- async function assertShippedOrderLineEditable(
6547
- em: EntityManager,
6548
- order: SalesOrder,
6549
- existingSnapshot: SalesLineSnapshot | null,
6550
- parsed: z.infer<typeof orderLineUpsertSchema>,
6551
- ): Promise<void> {
6552
- const lineId = existingSnapshot?.id;
6553
- if (!existingSnapshot || !lineId) return;
6554
- const shippedByLine = await loadShippedQuantityByLine(em, order.id, {
6555
- tenantId: order.tenantId,
6556
- organizationId: order.organizationId,
6557
- });
6558
- const shippedQuantity = shippedByLine.get(lineId) ?? 0;
6559
- if (shippedQuantity <= 0) return;
6560
-
6561
- const { translate } = await resolveTranslations();
6562
- const nextQuantity = parsed.quantity ?? existingSnapshot.quantity;
6563
- if (nextQuantity < shippedQuantity - SHIPPED_QUANTITY_TOLERANCE) {
6564
- throw new CrudHttpError(409, {
6565
- error: translate(
6566
- "sales.documents.items.errorQuantityBelowShipped",
6567
- "You cannot lower the quantity below the {{shipped}} already shipped.",
6568
- { shipped: shippedQuantity },
6569
- ),
6570
- });
6571
- }
6572
-
6573
- const pricingChanged =
6574
- hasNumericChange(parsed.unitPriceNet, existingSnapshot.unitPriceNet) ||
6575
- hasNumericChange(parsed.unitPriceGross, existingSnapshot.unitPriceGross) ||
6576
- hasNumericChange(parsed.taxRate, existingSnapshot.taxRate) ||
6577
- hasUnitChange(parsed.quantityUnit, existingSnapshot.quantityUnit);
6578
- if (pricingChanged) {
6579
- throw new CrudHttpError(409, {
6580
- error: translate(
6581
- "sales.documents.items.errorPriceShipped",
6582
- "You cannot change the price or unit of a line that has shipped items.",
6583
- ),
6584
- });
6585
- }
6586
- }
6587
-
6588
6524
  const orderLineUpsertCommand: CommandHandler<
6589
6525
  { body?: Record<string, unknown>; query?: Record<string, unknown> },
6590
6526
  { orderId: string; lineId: string }
@@ -6629,7 +6565,6 @@ const orderLineUpsertCommand: CommandHandler<
6629
6565
  const existingSnapshot = parsed.id
6630
6566
  ? (lineSnapshots.find((line) => line.id === parsed.id) ?? null)
6631
6567
  : null;
6632
- await assertShippedOrderLineEditable(em, order, existingSnapshot, parsed);
6633
6568
  const priceMode =
6634
6569
  parsed.priceMode === "gross"
6635
6570
  ? "gross"
@@ -15,8 +15,8 @@ import type { SalesCalculationService } from '../services/salesCalculationServic
15
15
  import type { SalesAdjustmentDraft, SalesLineSnapshot, SalesDocumentCalculationResult } from '../lib/types'
16
16
  import { cloneJson, deriveLineNetFromGross, ensureOrganizationScope, ensureSameScope, ensureTenantScope, extractUndoPayload, toNumericString, enforceSalesDocumentOptimisticLock, SALES_RESOURCE_KIND_ORDER, SALES_RESOURCE_KIND_RETURN } from './shared'
17
17
  import { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'
18
- import { SalesOrder, SalesOrderAdjustment, SalesOrderLine, SalesReturn, SalesReturnLine } from '../data/entities'
19
- import { loadShippedQuantityByLine } from '../lib/shipments/snapshots'
18
+ import { SalesOrder, SalesOrderAdjustment, SalesOrderLine, SalesReturn, SalesReturnLine, SalesShipment, SalesShipmentItem } from '../data/entities'
19
+ import { coerceShipmentQuantity } from '../lib/shipments/snapshots'
20
20
  import { computeAvailableReturnQuantity } from '../lib/returnQuantity'
21
21
  import {
22
22
  returnCreateSchema,
@@ -581,6 +581,36 @@ async function restoreReturnEffects(
581
581
  return createdLines
582
582
  }
583
583
 
584
+ async function loadShippedQuantityByLine(
585
+ em: EntityManager,
586
+ orderId: string,
587
+ scope: { tenantId: string; organizationId: string },
588
+ ): Promise<Map<string, number>> {
589
+ const shipments = await findWithDecryption(
590
+ em,
591
+ SalesShipment,
592
+ { order: orderId, deletedAt: null },
593
+ {},
594
+ scope,
595
+ )
596
+ const shippedByLine = new Map<string, number>()
597
+ if (!shipments.length) return shippedByLine
598
+ const items = await findWithDecryption(
599
+ em,
600
+ SalesShipmentItem,
601
+ { shipment: { $in: shipments.map((shipment) => shipment.id) } },
602
+ {},
603
+ scope,
604
+ )
605
+ items.forEach((item) => {
606
+ const orderLineId = typeof item.orderLine === 'string' ? item.orderLine : item.orderLine?.id ?? null
607
+ if (!orderLineId) return
608
+ const next = (shippedByLine.get(orderLineId) ?? 0) + coerceShipmentQuantity(item.quantity)
609
+ shippedByLine.set(orderLineId, next)
610
+ })
611
+ return shippedByLine
612
+ }
613
+
584
614
  function normalizeLinesInput(lines: ReturnCreateInput['lines']): ReturnLineInput[] {
585
615
  const seen = new Set<string>()
586
616
  const result: ReturnLineInput[] = []
@@ -890,12 +890,8 @@ export function SalesDocumentAddressesSection({
890
890
  if (res?.result?.id) billingId = res.result.id
891
891
  }
892
892
 
893
- if (shippingSnapshot || !shippingId) {
894
- payload.shippingAddressSnapshot = shippingSnapshot ?? null
895
- }
896
- if (billingSnapshot || !billingId) {
897
- payload.billingAddressSnapshot = billingSnapshot ?? null
898
- }
893
+ payload.shippingAddressSnapshot = shippingSnapshot ?? null
894
+ payload.billingAddressSnapshot = billingSnapshot ?? null
899
895
  payload.shippingAddressId = shippingSnapshot ? null : shippingId
900
896
  payload.billingAddressId = billingSnapshot ? null : billingId
901
897
 
@@ -817,11 +817,6 @@ export function SalesDocumentItemsSection({
817
817
  organizationId={resolvedOrganizationId}
818
818
  tenantId={resolvedTenantId}
819
819
  initialLine={lineForEdit}
820
- shippedQuantity={
821
- lineForEdit
822
- ? Math.max(0, shippedTotals.get(lineForEdit.id) ?? 0)
823
- : 0
824
- }
825
820
  onSaved={async () => {
826
821
  await loadItems();
827
822
  emitSalesDocumentTotalsRefresh({ documentId, kind });
@@ -278,7 +278,6 @@ type SalesLineDialogProps = {
278
278
  organizationId: string | null;
279
279
  tenantId: string | null;
280
280
  initialLine?: SalesLineRecord | null;
281
- shippedQuantity?: number;
282
281
  onOpenChange: (open: boolean) => void;
283
282
  onSaved?: () => Promise<void> | void;
284
283
  };
@@ -473,7 +472,6 @@ export function LineItemDialog({
473
472
  organizationId,
474
473
  tenantId,
475
474
  initialLine,
476
- shippedQuantity = 0,
477
475
  onOpenChange,
478
476
  onSaved,
479
477
  }: SalesLineDialogProps) {
@@ -1328,14 +1326,6 @@ export function LineItemDialog({
1328
1326
  },
1329
1327
  );
1330
1328
  }
1331
- if (shippedQuantity > 0 && qtyNumber < shippedQuantity) {
1332
- const message = t(
1333
- "sales.documents.items.errorQuantityBelowShipped",
1334
- "You cannot lower the quantity below the {{shipped}} already shipped.",
1335
- { shipped: shippedQuantity },
1336
- );
1337
- throw createCrudFormError(message, { quantity: message });
1338
- }
1339
1329
  const resolvedQuantityUnit = (() => {
1340
1330
  const entered = normalizeUnitCode(values.quantityUnit);
1341
1331
  if (isCustomLine) return entered;
@@ -168,7 +168,6 @@ function CustomerQuickCreate({ t, onCreated }: CustomerQuickCreateProps) {
168
168
  async (values: PersonFormValues) => {
169
169
  setSaving(true)
170
170
  try {
171
- const addresses = Array.isArray(values.addresses) ? values.addresses : []
172
171
  const payload = buildPersonPayload(values, organizationId)
173
172
  const { result } = await createCrud<{ id?: string; entityId?: string }>('customers/people', payload, {
174
173
  errorMessage: t('sales.documents.form.customer.quick.error', 'Failed to create customer.'),
@@ -178,51 +177,6 @@ function CustomerQuickCreate({ t, onCreated }: CustomerQuickCreateProps) {
178
177
  (result && typeof result.id === 'string' && result.id) ||
179
178
  null
180
179
  if (!id) throw new Error('Missing customer id')
181
- if (addresses.length) {
182
- const normalize = (value?: string | null) => {
183
- if (typeof value !== 'string') return undefined
184
- const trimmed = value.trim()
185
- return trimmed.length ? trimmed : undefined
186
- }
187
- for (const entry of addresses) {
188
- const normalizedLine1 = normalize(entry.addressLine1)
189
- if (!normalizedLine1) continue
190
- const body: Record<string, unknown> = {
191
- entityId: id,
192
- ...(organizationId ? { organizationId } : {}),
193
- addressLine1: normalizedLine1,
194
- isPrimary: entry.isPrimary ?? false,
195
- }
196
- const name = normalize(entry.name)
197
- if (name !== undefined) body.name = name
198
- const purpose = normalize(entry.purpose)
199
- if (purpose !== undefined) body.purpose = purpose
200
- const line2 = normalize(entry.addressLine2)
201
- if (line2 !== undefined) body.addressLine2 = line2
202
- const buildingNumber = normalize(entry.buildingNumber)
203
- if (buildingNumber !== undefined) body.buildingNumber = buildingNumber
204
- const flatNumber = normalize(entry.flatNumber)
205
- if (flatNumber !== undefined) body.flatNumber = flatNumber
206
- const city = normalize(entry.city)
207
- if (city !== undefined) body.city = city
208
- const region = normalize(entry.region)
209
- if (region !== undefined) body.region = region
210
- const postalCode = normalize(entry.postalCode)
211
- if (postalCode !== undefined) body.postalCode = postalCode
212
- const country = normalize(entry.country)
213
- if (country !== undefined) body.country = country.toUpperCase()
214
- if (typeof entry.latitude === 'number') body.latitude = entry.latitude
215
- if (typeof entry.longitude === 'number') body.longitude = entry.longitude
216
- try {
217
- await createCrud('customers/addresses', body)
218
- } catch (addressErr) {
219
- const message =
220
- (addressErr instanceof Error && addressErr.message ? addressErr.message : null) ||
221
- t('customers.people.detail.addresses.error')
222
- flash(message, 'error')
223
- }
224
- }
225
- }
226
180
  const displayName =
227
181
  typeof values.displayName === 'string' && values.displayName.trim().length
228
182
  ? values.displayName.trim()
@@ -296,13 +296,6 @@ export function ShipmentDialog({
296
296
  const [addressOptions, setAddressOptions] = React.useState<ShipmentAddressOption[]>([])
297
297
  const [addressLoading, setAddressLoading] = React.useState(false)
298
298
  const [addressError, setAddressError] = React.useState<string | null>(null)
299
- const addressLoadPromiseRef = React.useRef<{
300
- orderId: string
301
- promise: Promise<ShipmentAddressOption[]>
302
- } | null>(null)
303
- const currentAddressOrderIdRef = React.useRef(orderId)
304
- currentAddressOrderIdRef.current = orderId
305
- const initializedDialogKeyRef = React.useRef<string | null>(null)
306
299
  const [documentStatuses, setDocumentStatuses] = React.useState<StatusOption[]>([])
307
300
  const [lineStatuses, setLineStatuses] = React.useState<StatusOption[]>([])
308
301
  const [shipmentStatuses, setShipmentStatuses] = React.useState<StatusOption[]>([])
@@ -486,52 +479,37 @@ export function ShipmentDialog({
486
479
  [],
487
480
  )
488
481
 
489
- const loadAddressOptions = React.useCallback((): Promise<ShipmentAddressOption[]> => {
490
- const activeRequest = addressLoadPromiseRef.current
491
- if (activeRequest?.orderId === orderId) return activeRequest.promise
492
-
482
+ const loadAddressOptions = React.useCallback(async () => {
493
483
  setAddressLoading(true)
494
- const request = (async () => {
495
- try {
496
- const params = new URLSearchParams({
497
- page: '1',
498
- pageSize: '100',
499
- documentId: orderId,
500
- documentKind: 'order',
501
- })
502
- const response = await apiCall<{ items?: Array<Record<string, unknown>> }>(
503
- `/api/sales/document-addresses?${params.toString()}`,
504
- undefined,
505
- { fallback: { items: [] } },
506
- )
507
- const items = Array.isArray(response.result?.items) ? response.result.items : []
508
- const mapped = items
509
- .map((item) => mapDocumentAddressOption(item))
510
- .filter((entry): entry is ShipmentAddressOption => Boolean(entry))
511
- const loadedOptions = dedupeAddressOptions([...baseAddressOptions, ...mapped])
512
- if (currentAddressOrderIdRef.current !== orderId) return baseAddressOptions
513
- mergeAddressOptions(loadedOptions)
514
- setAddressError(null)
515
- return loadedOptions
516
- } catch (err) {
517
- logger.error('sales.shipments.addresses.load', { err })
518
- if (currentAddressOrderIdRef.current === orderId) {
519
- setAddressError(
520
- t('sales.documents.shipments.addressLoadError', 'Failed to load addresses.'),
521
- )
522
- }
523
- return baseAddressOptions
524
- }
525
- })()
526
- addressLoadPromiseRef.current = { orderId, promise: request }
527
- void request.then(() => {
528
- if (addressLoadPromiseRef.current?.promise === request) {
529
- addressLoadPromiseRef.current = null
530
- setAddressLoading(false)
531
- }
532
- })
533
- return request
534
- }, [baseAddressOptions, mapDocumentAddressOption, mergeAddressOptions, orderId, t])
484
+ try {
485
+ const params = new URLSearchParams({
486
+ page: '1',
487
+ pageSize: '100',
488
+ documentId: orderId,
489
+ documentKind: 'order',
490
+ })
491
+ const response = await apiCall<{ items?: Array<Record<string, unknown>> }>(
492
+ `/api/sales/document-addresses?${params.toString()}`,
493
+ undefined,
494
+ { fallback: { items: [] } },
495
+ )
496
+ const items = Array.isArray(response.result?.items) ? response.result.items : []
497
+ const mapped = items
498
+ .map((item) => mapDocumentAddressOption(item))
499
+ .filter((entry): entry is ShipmentAddressOption => Boolean(entry))
500
+ mergeAddressOptions(mapped)
501
+ setAddressError(null)
502
+ return mapped
503
+ } catch (err) {
504
+ logger.error('sales.shipments.addresses.load', { err })
505
+ setAddressError(
506
+ t('sales.documents.shipments.addressLoadError', 'Failed to load addresses.'),
507
+ )
508
+ return []
509
+ } finally {
510
+ setAddressLoading(false)
511
+ }
512
+ }, [mapDocumentAddressOption, mergeAddressOptions, orderId, t])
535
513
 
536
514
  const buildPriceSubtitle = React.useCallback(
537
515
  (option: ShippingMethodOption): string | undefined => {
@@ -873,20 +851,16 @@ export function ShipmentDialog({
873
851
  )
874
852
 
875
853
  React.useEffect(() => {
876
- if (!open) {
877
- initializedDialogKeyRef.current = null
878
- return
879
- }
880
- const dialogKey = `${orderId}:${mode}:${shipment?.id ?? 'new'}`
881
- if (initializedDialogKeyRef.current === dialogKey) return
882
- initializedDialogKeyRef.current = dialogKey
854
+ if (!open) return
883
855
  setFormResetKey((prev) => prev + 1)
884
856
  if (!shippingMethods.length) {
885
857
  void loadShippingMethods()
886
858
  }
887
859
  setAddressOptions(baseAddressOptions)
888
860
  setAddressError(null)
889
- void loadAddressOptions()
861
+ if (!addressOptions.length) {
862
+ void loadAddressOptions()
863
+ }
890
864
  if (!shipmentStatuses.length) {
891
865
  void loadShipmentStatuses()
892
866
  }
@@ -897,6 +871,7 @@ export function ShipmentDialog({
897
871
  void loadLineStatuses()
898
872
  }
899
873
  }, [
874
+ addressOptions.length,
900
875
  baseAddressOptions,
901
876
  documentStatuses.length,
902
877
  loadAddressOptions,
@@ -906,8 +881,6 @@ export function ShipmentDialog({
906
881
  loadShippingMethods,
907
882
  mode,
908
883
  open,
909
- orderId,
910
- shipment?.id,
911
884
  lineStatuses.length,
912
885
  shipmentStatuses.length,
913
886
  shippingMethods.length,
@@ -966,9 +939,11 @@ export function ShipmentDialog({
966
939
 
967
940
  const fetchAddressItems = React.useCallback(
968
941
  async (query?: string): Promise<LookupSelectItem[]> => {
969
- const options = addressOptions.length ? addressOptions : await loadAddressOptions()
942
+ if (!addressOptions.length && !addressLoading) {
943
+ await loadAddressOptions()
944
+ }
970
945
  const needle = query?.trim().toLowerCase() ?? ''
971
- return options
946
+ return addressOptions
972
947
  .filter((option) => {
973
948
  if (!needle) return true
974
949
  return (
@@ -987,7 +962,7 @@ export function ShipmentDialog({
987
962
  ),
988
963
  }))
989
964
  },
990
- [addressOptions, loadAddressOptions],
965
+ [addressLoading, addressOptions, loadAddressOptions],
991
966
  )
992
967
 
993
968
  const validateItems = React.useCallback(
@@ -638,10 +638,8 @@
638
638
  "sales.documents.detail.items.errorDelete": "Position konnte nicht gelöscht werden.",
639
639
  "sales.documents.detail.items.errorDeleteShipped": "Positionen mit versandten Artikeln können nicht gelöscht werden.",
640
640
  "sales.documents.detail.items.errorLoad": "Positionen konnten nicht geladen werden.",
641
- "sales.documents.detail.items.errorPriceShipped": "Preis oder Einheit einer Position mit versandten Artikeln können nicht geändert werden.",
642
641
  "sales.documents.detail.items.errorProductRequired": "Wähle ein Produkt aus, um fortzufahren.",
643
642
  "sales.documents.detail.items.errorQuantity": "Die Menge muss größer als 0 sein.",
644
- "sales.documents.detail.items.errorQuantityBelowShipped": "Die Menge kann nicht unter die bereits versandten {{shipped}} gesenkt werden.",
645
643
  "sales.documents.detail.items.errorSave": "Position konnte nicht gespeichert werden.",
646
644
  "sales.documents.detail.items.errorUnitPrice": "Der Stückpreis muss größer als 0 sein.",
647
645
  "sales.documents.detail.items.errorVariantRequired": "Wähle eine Variante aus, um fortzufahren.",
@@ -952,10 +950,8 @@
952
950
  "sales.documents.items.errorDeleteShipped": "Positionen mit versandten Artikeln können nicht gelöscht werden.",
953
951
  "sales.documents.items.errorLoad": "Positionen konnten nicht geladen werden.",
954
952
  "sales.documents.items.errorNameRequired": "Name ist für benutzerdefinierte Positionen erforderlich.",
955
- "sales.documents.items.errorPriceShipped": "Preis oder Einheit einer Position mit versandten Artikeln können nicht geändert werden.",
956
953
  "sales.documents.items.errorProductRequired": "Wähle ein Produkt aus, um fortzufahren.",
957
954
  "sales.documents.items.errorQuantity": "Die Menge muss größer als 0 sein.",
958
- "sales.documents.items.errorQuantityBelowShipped": "Die Menge kann nicht unter die bereits versandten {{shipped}} gesenkt werden.",
959
955
  "sales.documents.items.errorSave": "Position konnte nicht gespeichert werden.",
960
956
  "sales.documents.items.errorScope": "Organisation und Mandant sind erforderlich.",
961
957
  "sales.documents.items.errorUnitPrice": "Der Stückpreis muss größer als 0 sein.",
@@ -638,10 +638,8 @@
638
638
  "sales.documents.detail.items.errorDelete": "Failed to delete line.",
639
639
  "sales.documents.detail.items.errorDeleteShipped": "You cannot delete a line that has shipped items.",
640
640
  "sales.documents.detail.items.errorLoad": "Failed to load items.",
641
- "sales.documents.detail.items.errorPriceShipped": "You cannot change the price or unit of a line that has shipped items.",
642
641
  "sales.documents.detail.items.errorProductRequired": "Select a product to continue.",
643
642
  "sales.documents.detail.items.errorQuantity": "Quantity must be greater than 0.",
644
- "sales.documents.detail.items.errorQuantityBelowShipped": "You cannot lower the quantity below the {{shipped}} already shipped.",
645
643
  "sales.documents.detail.items.errorSave": "Failed to save line.",
646
644
  "sales.documents.detail.items.errorUnitPrice": "Unit price must be greater than 0.",
647
645
  "sales.documents.detail.items.errorVariantRequired": "Select a variant to continue.",
@@ -952,10 +950,8 @@
952
950
  "sales.documents.items.errorDeleteShipped": "You cannot delete a line that has shipped items.",
953
951
  "sales.documents.items.errorLoad": "Failed to load items.",
954
952
  "sales.documents.items.errorNameRequired": "Name is required for custom lines.",
955
- "sales.documents.items.errorPriceShipped": "You cannot change the price or unit of a line that has shipped items.",
956
953
  "sales.documents.items.errorProductRequired": "Select a product to continue.",
957
954
  "sales.documents.items.errorQuantity": "Quantity must be greater than 0.",
958
- "sales.documents.items.errorQuantityBelowShipped": "You cannot lower the quantity below the {{shipped}} already shipped.",
959
955
  "sales.documents.items.errorSave": "Failed to save line.",
960
956
  "sales.documents.items.errorScope": "Organization and tenant are required.",
961
957
  "sales.documents.items.errorUnitPrice": "Unit price must be greater than 0.",
@@ -638,10 +638,8 @@
638
638
  "sales.documents.detail.items.errorDelete": "No se pudo eliminar la línea.",
639
639
  "sales.documents.detail.items.errorDeleteShipped": "No puedes eliminar una línea que ya tiene envíos.",
640
640
  "sales.documents.detail.items.errorLoad": "No se pudieron cargar los artículos.",
641
- "sales.documents.detail.items.errorPriceShipped": "No puedes cambiar el precio ni la unidad de una línea que ya tiene envíos.",
642
641
  "sales.documents.detail.items.errorProductRequired": "Selecciona un producto para continuar.",
643
642
  "sales.documents.detail.items.errorQuantity": "La cantidad debe ser mayor que 0.",
644
- "sales.documents.detail.items.errorQuantityBelowShipped": "No puedes reducir la cantidad por debajo de las {{shipped}} ya enviadas.",
645
643
  "sales.documents.detail.items.errorSave": "No se pudo guardar la línea.",
646
644
  "sales.documents.detail.items.errorUnitPrice": "El precio unitario debe ser mayor que 0.",
647
645
  "sales.documents.detail.items.errorVariantRequired": "Selecciona una variante para continuar.",
@@ -952,10 +950,8 @@
952
950
  "sales.documents.items.errorDeleteShipped": "No puedes eliminar una línea que ya tiene envíos.",
953
951
  "sales.documents.items.errorLoad": "No se pudieron cargar los artículos.",
954
952
  "sales.documents.items.errorNameRequired": "El nombre es obligatorio para líneas personalizadas.",
955
- "sales.documents.items.errorPriceShipped": "No puedes cambiar el precio ni la unidad de una línea que ya tiene envíos.",
956
953
  "sales.documents.items.errorProductRequired": "Selecciona un producto para continuar.",
957
954
  "sales.documents.items.errorQuantity": "La cantidad debe ser mayor que 0.",
958
- "sales.documents.items.errorQuantityBelowShipped": "No puedes reducir la cantidad por debajo de las {{shipped}} ya enviadas.",
959
955
  "sales.documents.items.errorSave": "No se pudo guardar la línea.",
960
956
  "sales.documents.items.errorScope": "Se requieren organización e inquilino.",
961
957
  "sales.documents.items.errorUnitPrice": "El precio unitario debe ser mayor que 0.",
@@ -638,10 +638,8 @@
638
638
  "sales.documents.detail.items.errorDelete": "Nie udało się usunąć pozycji.",
639
639
  "sales.documents.detail.items.errorDeleteShipped": "Nie można usunąć pozycji, która ma wysłane sztuki.",
640
640
  "sales.documents.detail.items.errorLoad": "Nie udało się wczytać pozycji.",
641
- "sales.documents.detail.items.errorPriceShipped": "Nie można zmienić ceny ani jednostki pozycji, która ma wysłane sztuki.",
642
641
  "sales.documents.detail.items.errorProductRequired": "Wybierz produkt, aby kontynuować.",
643
642
  "sales.documents.detail.items.errorQuantity": "Ilość musi być większa niż 0.",
644
- "sales.documents.detail.items.errorQuantityBelowShipped": "Nie można obniżyć ilości poniżej {{shipped}} już wysłanych.",
645
643
  "sales.documents.detail.items.errorSave": "Nie udało się zapisać pozycji.",
646
644
  "sales.documents.detail.items.errorUnitPrice": "Cena jednostkowa musi być większa niż 0.",
647
645
  "sales.documents.detail.items.errorVariantRequired": "Wybierz wariant, aby kontynuować.",
@@ -952,10 +950,8 @@
952
950
  "sales.documents.items.errorDeleteShipped": "Nie można usunąć pozycji, która ma wysłane sztuki.",
953
951
  "sales.documents.items.errorLoad": "Nie udało się załadować pozycji.",
954
952
  "sales.documents.items.errorNameRequired": "Nazwa jest wymagana dla niestandardowych pozycji.",
955
- "sales.documents.items.errorPriceShipped": "Nie można zmienić ceny ani jednostki pozycji, która ma wysłane sztuki.",
956
953
  "sales.documents.items.errorProductRequired": "Wybierz produkt, aby kontynuować.",
957
954
  "sales.documents.items.errorQuantity": "Ilość musi być większa niż 0.",
958
- "sales.documents.items.errorQuantityBelowShipped": "Nie można obniżyć ilości poniżej {{shipped}} już wysłanych.",
959
955
  "sales.documents.items.errorSave": "Nie udało się zapisać pozycji.",
960
956
  "sales.documents.items.errorScope": "Wymagane są organizacja i najemca.",
961
957
  "sales.documents.items.errorUnitPrice": "Cena jednostkowa musi być większa niż 0.",
@@ -136,34 +136,4 @@ export const refreshShipmentItemsSnapshot = async (
136
136
  return snapshot
137
137
  }
138
138
 
139
- export const loadShippedQuantityByLine = async (
140
- em: EntityManager,
141
- orderId: string,
142
- scope: { tenantId: string; organizationId: string },
143
- ): Promise<Map<string, number>> => {
144
- const shipments = await findWithDecryption(
145
- em,
146
- SalesShipment,
147
- { order: orderId, deletedAt: null },
148
- {},
149
- scope,
150
- )
151
- const shippedByLine = new Map<string, number>()
152
- if (!shipments.length) return shippedByLine
153
- const items = await findWithDecryption(
154
- em,
155
- SalesShipmentItem,
156
- { shipment: { $in: shipments.map((shipment) => shipment.id) } },
157
- {},
158
- scope,
159
- )
160
- items.forEach((item) => {
161
- const orderLineId = typeof item.orderLine === 'string' ? item.orderLine : (item.orderLine?.id ?? null)
162
- if (!orderLineId) return
163
- const next = (shippedByLine.get(orderLineId) ?? 0) + coerceShipmentQuantity(item.quantity)
164
- shippedByLine.set(orderLineId, next)
165
- })
166
- return shippedByLine
167
- }
168
-
169
139
  export type { ShipmentItemSnapshot } from './types'
@@ -389,9 +389,20 @@ export default function CheckoutDemoPage() {
389
389
  currencyCode: selectedCurrency || 'USD', // Ensure currency is always set
390
390
  kind: 'product' as const,
391
391
  productId: item.id,
392
- lineDescription: item.name,
392
+ name: item.name,
393
393
  unitPriceGross: item.price,
394
394
  })),
395
+ // Fold the demo's shipping and tax into order-level adjustments so the
396
+ // created order's grand total matches the cart total shown to the user
397
+ // (line items alone only cover the product subtotal).
398
+ orderAdjustments: [
399
+ ...(shipping > 0
400
+ ? [{ kind: 'shipping' as const, label: 'Shipping', currencyCode: selectedCurrency || 'USD', amountGross: shipping }]
401
+ : []),
402
+ ...(tax > 0
403
+ ? [{ kind: 'tax' as const, label: 'Tax', currencyCode: selectedCurrency || 'USD', amountGross: tax }]
404
+ : []),
405
+ ],
395
406
  itemCount: demoCart.reduce((sum, item) => sum + item.quantity, 0),
396
407
  subtotal: subtotal,
397
408
  tax: tax,