@open-mercato/core 0.6.7-develop.6726.1.983ae8a07e → 0.6.7-develop.6749.1.6b54c56dfe

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 (191) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities/resources_resource/index.js +2 -0
  3. package/dist/generated/entities/resources_resource/index.js.map +2 -2
  4. package/dist/generated/entity-fields-registry.js +1 -0
  5. package/dist/generated/entity-fields-registry.js.map +2 -2
  6. package/dist/helpers/integration/salesFixtures.js +31 -2
  7. package/dist/helpers/integration/salesFixtures.js.map +2 -2
  8. package/dist/helpers/integration/salesUi.js +5 -0
  9. package/dist/helpers/integration/salesUi.js.map +2 -2
  10. package/dist/modules/auth/api/profile/route.js +8 -2
  11. package/dist/modules/auth/api/profile/route.js.map +2 -2
  12. package/dist/modules/auth/backend/auth/profile/page.js.map +2 -2
  13. package/dist/modules/auth/backend/profile/change-password/page.js.map +2 -2
  14. package/dist/modules/communication_channels/data/enrichers.js +4 -4
  15. package/dist/modules/communication_channels/data/enrichers.js.map +2 -2
  16. package/dist/modules/customers/analytics.js +1 -0
  17. package/dist/modules/customers/analytics.js.map +2 -2
  18. package/dist/modules/customers/components/detail/DealForm.js +2 -1
  19. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  20. package/dist/modules/customers/components/detail/DealsSection.js +1 -1
  21. package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
  22. package/dist/modules/customers/data/validators.js +3 -1
  23. package/dist/modules/customers/data/validators.js.map +2 -2
  24. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/config.js +52 -2
  25. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/config.js.map +2 -2
  26. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js +42 -26
  27. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js.map +2 -2
  28. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js +2 -2
  29. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js.map +2 -2
  30. package/dist/modules/data_sync/lib/queue-policy.js +15 -0
  31. package/dist/modules/data_sync/lib/queue-policy.js.map +7 -0
  32. package/dist/modules/data_sync/lib/queue.js +16 -1
  33. package/dist/modules/data_sync/lib/queue.js.map +2 -2
  34. package/dist/modules/data_sync/lib/start-run.js +2 -1
  35. package/dist/modules/data_sync/lib/start-run.js.map +2 -2
  36. package/dist/modules/data_sync/lib/sync-engine.js +35 -2
  37. package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
  38. package/dist/modules/data_sync/lib/sync-run-service.js +70 -2
  39. package/dist/modules/data_sync/lib/sync-run-service.js.map +2 -2
  40. package/dist/modules/data_sync/workers/sync-export.js +9 -2
  41. package/dist/modules/data_sync/workers/sync-export.js.map +2 -2
  42. package/dist/modules/data_sync/workers/sync-import.js +9 -2
  43. package/dist/modules/data_sync/workers/sync-import.js.map +2 -2
  44. package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +34 -12
  45. package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
  46. package/dist/modules/dictionaries/api/openapi.js +15 -2
  47. package/dist/modules/dictionaries/api/openapi.js.map +2 -2
  48. package/dist/modules/dictionaries/components/hooks/useDictionaryEntries.js +30 -29
  49. package/dist/modules/dictionaries/components/hooks/useDictionaryEntries.js.map +2 -2
  50. package/dist/modules/dictionaries/data/validators.js +7 -0
  51. package/dist/modules/dictionaries/data/validators.js.map +2 -2
  52. package/dist/modules/dictionaries/lib/clientEntries.js +4 -3
  53. package/dist/modules/dictionaries/lib/clientEntries.js.map +2 -2
  54. package/dist/modules/dictionaries/lib/fetchAllEntries.js +43 -0
  55. package/dist/modules/dictionaries/lib/fetchAllEntries.js.map +7 -0
  56. package/dist/modules/entities/api/definitions.js +51 -3
  57. package/dist/modules/entities/api/definitions.js.map +2 -2
  58. package/dist/modules/entities/api/entities.js +21 -2
  59. package/dist/modules/entities/api/entities.js.map +2 -2
  60. package/dist/modules/entities/api/records.js +3 -24
  61. package/dist/modules/entities/api/records.js.map +2 -2
  62. package/dist/modules/entities/api/sidebar-entities.js +34 -12
  63. package/dist/modules/entities/api/sidebar-entities.js.map +2 -2
  64. package/dist/modules/entities/lib/entityAcl.js +46 -0
  65. package/dist/modules/entities/lib/entityAcl.js.map +2 -2
  66. package/dist/modules/messages/api/route.js +2 -5
  67. package/dist/modules/messages/api/route.js.map +2 -2
  68. package/dist/modules/messages/lib/participantScope.js +15 -0
  69. package/dist/modules/messages/lib/participantScope.js.map +7 -0
  70. package/dist/modules/notifications/api/route.js +3 -1
  71. package/dist/modules/notifications/api/route.js.map +2 -2
  72. package/dist/modules/notifications/api/unread-count/route.js +25 -6
  73. package/dist/modules/notifications/api/unread-count/route.js.map +2 -2
  74. package/dist/modules/notifications/lib/notificationScope.js +26 -0
  75. package/dist/modules/notifications/lib/notificationScope.js.map +7 -0
  76. package/dist/modules/notifications/lib/notificationService.js +7 -3
  77. package/dist/modules/notifications/lib/notificationService.js.map +2 -2
  78. package/dist/modules/notifications/lib/routeHelpers.js +16 -2
  79. package/dist/modules/notifications/lib/routeHelpers.js.map +2 -2
  80. package/dist/modules/planner/components/unavailabilityReasons.js +3 -3
  81. package/dist/modules/planner/components/unavailabilityReasons.js.map +2 -2
  82. package/dist/modules/progress/lib/progressService.js +2 -0
  83. package/dist/modules/progress/lib/progressService.js.map +2 -2
  84. package/dist/modules/progress/lib/progressServiceImpl.js +322 -104
  85. package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
  86. package/dist/modules/resources/api/resources.js +2 -0
  87. package/dist/modules/resources/api/resources.js.map +2 -2
  88. package/dist/modules/resources/backend/resources/resources/[id]/page.js +3 -2
  89. package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
  90. package/dist/modules/resources/commands/resources.js +9 -0
  91. package/dist/modules/resources/commands/resources.js.map +2 -2
  92. package/dist/modules/resources/components/detail/dictionaries.js +3 -3
  93. package/dist/modules/resources/components/detail/dictionaries.js.map +2 -2
  94. package/dist/modules/resources/data/entities.js +3 -0
  95. package/dist/modules/resources/data/entities.js.map +2 -2
  96. package/dist/modules/resources/data/validators.js +6 -2
  97. package/dist/modules/resources/data/validators.js.map +2 -2
  98. package/dist/modules/resources/lib/seeds.js +16 -9
  99. package/dist/modules/resources/lib/seeds.js.map +2 -2
  100. package/dist/modules/resources/migrations/Migration20260608231000.js +13 -0
  101. package/dist/modules/resources/migrations/Migration20260608231000.js.map +7 -0
  102. package/dist/modules/sales/backend/sales/documents/create/page.js +11 -14
  103. package/dist/modules/sales/backend/sales/documents/create/page.js.map +2 -2
  104. package/dist/modules/sales/commands/documents.js +8 -0
  105. package/dist/modules/sales/commands/documents.js.map +2 -2
  106. package/dist/modules/sales/components/documents/ItemsSection.js +18 -9
  107. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  108. package/dist/modules/sales/components/documents/LineItemDialog.js +12 -5
  109. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  110. package/dist/modules/sales/components/documents/SalesDocumentForm.js +48 -2
  111. package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
  112. package/dist/modules/sales/components/documents/SalesOrderDraftLines.js +167 -0
  113. package/dist/modules/sales/components/documents/SalesOrderDraftLines.js.map +7 -0
  114. package/dist/modules/sales/data/validators.js +5 -1
  115. package/dist/modules/sales/data/validators.js.map +2 -2
  116. package/dist/modules/staff/components/detail/dictionaries.js +3 -3
  117. package/dist/modules/staff/components/detail/dictionaries.js.map +2 -2
  118. package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js +11 -4
  119. package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js.map +2 -2
  120. package/generated/entities/resources_resource/index.ts +1 -0
  121. package/generated/entity-fields-registry.ts +1 -0
  122. package/package.json +7 -7
  123. package/src/helpers/integration/salesFixtures.ts +34 -2
  124. package/src/helpers/integration/salesUi.ts +8 -0
  125. package/src/modules/auth/api/profile/route.ts +8 -2
  126. package/src/modules/auth/backend/auth/profile/page.tsx +1 -0
  127. package/src/modules/auth/backend/profile/change-password/page.tsx +1 -0
  128. package/src/modules/communication_channels/data/enrichers.ts +6 -26
  129. package/src/modules/customers/analytics.ts +1 -0
  130. package/src/modules/customers/components/detail/DealForm.tsx +5 -1
  131. package/src/modules/customers/components/detail/DealsSection.tsx +1 -1
  132. package/src/modules/customers/data/validators.ts +5 -1
  133. package/src/modules/dashboards/i18n/de.json +3 -0
  134. package/src/modules/dashboards/i18n/en.json +3 -0
  135. package/src/modules/dashboards/i18n/es.json +3 -0
  136. package/src/modules/dashboards/i18n/pl.json +3 -0
  137. package/src/modules/dashboards/widgets/dashboard/pipeline-summary/config.ts +86 -0
  138. package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.tsx +33 -18
  139. package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.ts +2 -2
  140. package/src/modules/data_sync/lib/adapter.ts +18 -0
  141. package/src/modules/data_sync/lib/queue-policy.ts +30 -0
  142. package/src/modules/data_sync/lib/queue.ts +19 -1
  143. package/src/modules/data_sync/lib/start-run.ts +2 -1
  144. package/src/modules/data_sync/lib/sync-engine.ts +46 -0
  145. package/src/modules/data_sync/lib/sync-run-service.ts +82 -1
  146. package/src/modules/data_sync/workers/sync-export.ts +8 -1
  147. package/src/modules/data_sync/workers/sync-import.ts +8 -1
  148. package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +39 -11
  149. package/src/modules/dictionaries/api/openapi.ts +19 -0
  150. package/src/modules/dictionaries/components/hooks/useDictionaryEntries.ts +35 -36
  151. package/src/modules/dictionaries/data/validators.ts +14 -0
  152. package/src/modules/dictionaries/lib/clientEntries.ts +4 -3
  153. package/src/modules/dictionaries/lib/fetchAllEntries.ts +72 -0
  154. package/src/modules/entities/api/definitions.ts +51 -3
  155. package/src/modules/entities/api/entities.ts +30 -2
  156. package/src/modules/entities/api/records.ts +3 -27
  157. package/src/modules/entities/api/sidebar-entities.ts +43 -14
  158. package/src/modules/entities/lib/entityAcl.ts +58 -0
  159. package/src/modules/messages/api/route.ts +4 -5
  160. package/src/modules/messages/lib/participantScope.ts +64 -0
  161. package/src/modules/notifications/api/route.ts +2 -0
  162. package/src/modules/notifications/api/unread-count/route.ts +42 -5
  163. package/src/modules/notifications/lib/notificationScope.ts +40 -0
  164. package/src/modules/notifications/lib/notificationService.ts +5 -0
  165. package/src/modules/notifications/lib/routeHelpers.ts +19 -2
  166. package/src/modules/planner/components/unavailabilityReasons.ts +3 -3
  167. package/src/modules/progress/AGENTS.md +11 -1
  168. package/src/modules/progress/lib/progressService.ts +1 -0
  169. package/src/modules/progress/lib/progressServiceImpl.ts +385 -117
  170. package/src/modules/resources/api/resources.ts +2 -0
  171. package/src/modules/resources/backend/resources/resources/[id]/page.tsx +9 -3
  172. package/src/modules/resources/commands/resources.ts +10 -0
  173. package/src/modules/resources/components/detail/dictionaries.ts +3 -3
  174. package/src/modules/resources/data/entities.ts +3 -0
  175. package/src/modules/resources/data/validators.ts +5 -0
  176. package/src/modules/resources/lib/seeds.ts +16 -9
  177. package/src/modules/resources/migrations/.snapshot-open-mercato.json +17 -1
  178. package/src/modules/resources/migrations/Migration20260608231000.ts +13 -0
  179. package/src/modules/sales/backend/sales/documents/create/page.tsx +2 -4
  180. package/src/modules/sales/commands/documents.ts +8 -0
  181. package/src/modules/sales/components/documents/ItemsSection.tsx +19 -10
  182. package/src/modules/sales/components/documents/LineItemDialog.tsx +13 -5
  183. package/src/modules/sales/components/documents/SalesDocumentForm.tsx +48 -6
  184. package/src/modules/sales/components/documents/SalesOrderDraftLines.tsx +198 -0
  185. package/src/modules/sales/data/validators.ts +7 -1
  186. package/src/modules/sales/i18n/de.json +2 -0
  187. package/src/modules/sales/i18n/en.json +2 -0
  188. package/src/modules/sales/i18n/es.json +2 -0
  189. package/src/modules/sales/i18n/pl.json +2 -0
  190. package/src/modules/staff/components/detail/dictionaries.ts +3 -3
  191. package/src/modules/workflows/widgets/injection/order-approval/widget.client.tsx +12 -5
@@ -0,0 +1,198 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import type { ColumnDef } from '@tanstack/react-table'
5
+ import { Plus } from 'lucide-react'
6
+ import { DataTable } from '@open-mercato/ui/backend/DataTable'
7
+ import { RowActions } from '@open-mercato/ui/backend/RowActions'
8
+ import { EmptyState } from '@open-mercato/ui/primitives/empty-state'
9
+ import { Button } from '@open-mercato/ui/primitives/button'
10
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
11
+ import { LineItemDialog } from './LineItemDialog'
12
+ import type { SalesLineRecord } from './lineItemTypes'
13
+ import { formatMoney, normalizeNumber } from './lineItemUtils'
14
+
15
+ export type SalesOrderLineDraft = {
16
+ id: string
17
+ payload: Record<string, unknown>
18
+ record: SalesLineRecord
19
+ }
20
+
21
+ type SalesOrderDraftLinesProps = {
22
+ currencyCode: string | null | undefined
23
+ organizationId: string | null
24
+ tenantId: string | null
25
+ lines: SalesOrderLineDraft[]
26
+ error?: string | null
27
+ onChange: (lines: SalesOrderLineDraft[]) => void
28
+ }
29
+
30
+ function draftId(): string {
31
+ return typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
32
+ ? crypto.randomUUID()
33
+ : `draft-${Date.now()}-${Math.round(performance.now())}`
34
+ }
35
+
36
+ export function createSalesOrderLineDraft(
37
+ payload: Record<string, unknown>,
38
+ id = draftId(),
39
+ ): SalesOrderLineDraft {
40
+ const quantity = normalizeNumber(payload.quantity, 0)
41
+ const unitPriceNet = normalizeNumber(payload.unitPriceNet, 0)
42
+ const unitPriceGross = normalizeNumber(payload.unitPriceGross, unitPriceNet)
43
+ const taxRate = normalizeNumber(payload.taxRate, 0)
44
+ const totalNet = normalizeNumber(payload.totalNetAmount, unitPriceNet * quantity)
45
+ const totalGross = normalizeNumber(payload.totalGrossAmount, unitPriceGross * quantity)
46
+ const metadata = payload.metadata && typeof payload.metadata === 'object'
47
+ ? payload.metadata as Record<string, unknown>
48
+ : null
49
+ const catalogSnapshot = payload.catalogSnapshot && typeof payload.catalogSnapshot === 'object'
50
+ ? payload.catalogSnapshot as Record<string, unknown>
51
+ : null
52
+
53
+ return {
54
+ id,
55
+ payload: { ...payload },
56
+ record: {
57
+ id,
58
+ name: typeof payload.name === 'string' ? payload.name : null,
59
+ productId: typeof payload.productId === 'string' ? payload.productId : null,
60
+ productVariantId: typeof payload.productVariantId === 'string' ? payload.productVariantId : null,
61
+ quantity,
62
+ quantityUnit: typeof payload.quantityUnit === 'string' ? payload.quantityUnit : null,
63
+ normalizedQuantity: normalizeNumber(payload.normalizedQuantity, quantity),
64
+ normalizedUnit: typeof payload.normalizedUnit === 'string' ? payload.normalizedUnit : null,
65
+ currencyCode: typeof payload.currencyCode === 'string' ? payload.currencyCode : null,
66
+ unitPriceNet,
67
+ unitPriceGross,
68
+ taxRate,
69
+ totalNet,
70
+ totalGross,
71
+ priceMode: payload.priceMode === 'net' ? 'net' : 'gross',
72
+ uomSnapshot: null,
73
+ metadata,
74
+ catalogSnapshot,
75
+ customFieldSetId: typeof payload.customFieldSetId === 'string' ? payload.customFieldSetId : null,
76
+ customFields: payload.customFields && typeof payload.customFields === 'object'
77
+ ? payload.customFields as Record<string, unknown>
78
+ : null,
79
+ status: null,
80
+ statusEntryId: typeof payload.statusEntryId === 'string' ? payload.statusEntryId : null,
81
+ },
82
+ }
83
+ }
84
+
85
+ export function SalesOrderDraftLines({
86
+ currencyCode,
87
+ organizationId,
88
+ tenantId,
89
+ lines,
90
+ error,
91
+ onChange,
92
+ }: SalesOrderDraftLinesProps) {
93
+ const t = useT()
94
+ const [dialogOpen, setDialogOpen] = React.useState(false)
95
+ const [editing, setEditing] = React.useState<SalesOrderLineDraft | null>(null)
96
+
97
+ const columns = React.useMemo<ColumnDef<SalesOrderLineDraft>[]>(() => [
98
+ {
99
+ id: 'name',
100
+ header: t('sales.documents.items.table.product', 'Product'),
101
+ cell: ({ row }) => row.original.record.name ?? t('sales.documents.items.untitled', 'Untitled'),
102
+ },
103
+ {
104
+ id: 'quantity',
105
+ header: t('sales.documents.items.table.quantity', 'Qty'),
106
+ cell: ({ row }) => row.original.record.quantityUnit
107
+ ? `${row.original.record.quantity} ${row.original.record.quantityUnit}`
108
+ : row.original.record.quantity,
109
+ },
110
+ {
111
+ id: 'unitPrice',
112
+ header: t('sales.documents.items.table.unit', 'Unit price'),
113
+ cell: ({ row }) => formatMoney(row.original.record.unitPriceGross, row.original.record.currencyCode ?? currencyCode ?? undefined),
114
+ },
115
+ {
116
+ id: 'total',
117
+ header: t('sales.documents.items.table.total', 'Total'),
118
+ cell: ({ row }) => formatMoney(row.original.record.totalGross, row.original.record.currencyCode ?? currencyCode ?? undefined),
119
+ },
120
+ ], [currencyCode, t])
121
+
122
+ const openCreate = React.useCallback(() => {
123
+ setEditing(null)
124
+ setDialogOpen(true)
125
+ }, [])
126
+
127
+ const rowActions = React.useCallback((line: SalesOrderLineDraft) => (
128
+ <RowActions
129
+ items={[
130
+ {
131
+ id: 'edit',
132
+ label: t('ui.actions.edit', 'Edit'),
133
+ onSelect: () => {
134
+ setEditing(line)
135
+ setDialogOpen(true)
136
+ },
137
+ },
138
+ {
139
+ id: 'delete',
140
+ label: t('ui.actions.delete', 'Delete'),
141
+ destructive: true,
142
+ onSelect: () => onChange(lines.filter((candidate) => candidate.id !== line.id)),
143
+ },
144
+ ]}
145
+ />
146
+ ), [lines, onChange, t])
147
+
148
+ return (
149
+ <div className="space-y-3">
150
+ <div className="flex items-center justify-between gap-4">
151
+ <div>
152
+ <h3 className="text-sm font-medium">{t('sales.orders.form.lines', 'Line Items')}</h3>
153
+ <p className="text-sm text-muted-foreground">
154
+ {t('sales.orders.linesRequired', 'Add at least one line item before creating the order.')}
155
+ </p>
156
+ </div>
157
+ <Button type="button" variant="outline" onClick={openCreate}>
158
+ <Plus className="h-4 w-4" aria-hidden="true" />
159
+ {t('sales.documents.items.add', 'Add item')}
160
+ </Button>
161
+ </div>
162
+ {error ? <p className="text-sm text-destructive" role="alert">{error}</p> : null}
163
+ <DataTable
164
+ columns={columns}
165
+ data={lines}
166
+ embedded
167
+ disableRowClick
168
+ rowActions={rowActions}
169
+ emptyState={(
170
+ <EmptyState
171
+ size="sm"
172
+ title={t('sales.documents.items.empty', 'No items yet.')}
173
+ description={t('sales.orders.linesRequired', 'Add at least one line item before creating the order.')}
174
+ actions={<Button type="button" variant="outline" size="sm" onClick={openCreate}>{t('sales.documents.items.add', 'Add item')}</Button>}
175
+ />
176
+ )}
177
+ />
178
+ <LineItemDialog
179
+ open={dialogOpen}
180
+ onOpenChange={(open) => {
181
+ setDialogOpen(open)
182
+ if (!open) setEditing(null)
183
+ }}
184
+ kind="order"
185
+ currencyCode={currencyCode}
186
+ organizationId={organizationId}
187
+ tenantId={tenantId}
188
+ initialLine={editing?.record ?? null}
189
+ onDraftSaved={(payload, lineId) => {
190
+ const draft = createSalesOrderLineDraft(payload, lineId ?? undefined)
191
+ onChange(lineId
192
+ ? lines.map((line) => line.id === lineId ? draft : line)
193
+ : [...lines, draft])
194
+ }}
195
+ />
196
+ </div>
197
+ )
198
+ }
@@ -679,6 +679,8 @@ const quoteTotalsSchema = z.object({
679
679
  lineItemCount: z.coerce.number().int().min(0).optional(),
680
680
  })
681
681
 
682
+ export const SALES_ORDER_LINES_REQUIRED_MESSAGE_KEY = 'sales.orders.linesRequired'
683
+
682
684
  export const orderCreateSchema = scoped.extend({
683
685
  orderNumber: z.string().trim().min(1).max(191).optional(),
684
686
  externalReference: z.string().trim().max(191).optional(),
@@ -715,7 +717,11 @@ export const orderCreateSchema = scoped.extend({
715
717
  paymentMethodSnapshot: jsonRecord.optional(),
716
718
  metadata,
717
719
  customFieldSetId: uuid().optional(),
718
- lines: z.array(orderLineCreateSchema.omit({ organizationId: true, tenantId: true, orderId: true })).optional(),
720
+ lines: z
721
+ .array(orderLineCreateSchema.omit({ organizationId: true, tenantId: true, orderId: true }), {
722
+ error: SALES_ORDER_LINES_REQUIRED_MESSAGE_KEY,
723
+ })
724
+ .min(1, SALES_ORDER_LINES_REQUIRED_MESSAGE_KEY),
719
725
  adjustments: z.array(orderAdjustmentCreateSchema.omit({ organizationId: true, tenantId: true, orderId: true })).optional(),
720
726
  tags: z.array(uuid()).optional(),
721
727
  ...orderTotalsSchema.shape,
@@ -950,6 +950,7 @@
950
950
  "sales.documents.items.empty": "Noch keine Positionen.",
951
951
  "sales.documents.items.errorCurrency": "Währung ist erforderlich.",
952
952
  "sales.documents.items.errorDelete": "Position konnte nicht gelöscht werden.",
953
+ "sales.documents.items.errorDeleteLast": "Eine Bestellung muss mindestens eine Position enthalten.",
953
954
  "sales.documents.items.errorDeleteShipped": "Positionen mit versandten Artikeln können nicht gelöscht werden.",
954
955
  "sales.documents.items.errorLoad": "Positionen konnten nicht geladen werden.",
955
956
  "sales.documents.items.errorNameRequired": "Name ist für benutzerdefinierte Positionen erforderlich.",
@@ -1226,6 +1227,7 @@
1226
1227
  "sales.orders.form.shipments": "Sendungen",
1227
1228
  "sales.orders.lines.delete": "Bestellposition löschen",
1228
1229
  "sales.orders.lines.upsert": "Bestellposition aktualisieren",
1230
+ "sales.orders.linesRequired": "Fügen Sie vor dem Erstellen der Bestellung mindestens eine Position hinzu.",
1229
1231
  "sales.orders.list.title": "Aufträge",
1230
1232
  "sales.orders.status_change.actor_api_key": "API-Schlüssel {name}",
1231
1233
  "sales.orders.status_change.actor_unknown": "Unbekannter Benutzer",
@@ -950,6 +950,7 @@
950
950
  "sales.documents.items.empty": "No items yet.",
951
951
  "sales.documents.items.errorCurrency": "Currency is required.",
952
952
  "sales.documents.items.errorDelete": "Failed to delete line.",
953
+ "sales.documents.items.errorDeleteLast": "An order must contain at least one line item.",
953
954
  "sales.documents.items.errorDeleteShipped": "You cannot delete a line that has shipped items.",
954
955
  "sales.documents.items.errorLoad": "Failed to load items.",
955
956
  "sales.documents.items.errorNameRequired": "Name is required for custom lines.",
@@ -1226,6 +1227,7 @@
1226
1227
  "sales.orders.form.shipments": "Shipments",
1227
1228
  "sales.orders.lines.delete": "Delete order line",
1228
1229
  "sales.orders.lines.upsert": "Update order line",
1230
+ "sales.orders.linesRequired": "Add at least one line item before creating the order.",
1229
1231
  "sales.orders.list.title": "Orders",
1230
1232
  "sales.orders.status_change.actor_api_key": "API key {name}",
1231
1233
  "sales.orders.status_change.actor_unknown": "Unknown user",
@@ -950,6 +950,7 @@
950
950
  "sales.documents.items.empty": "Aún no hay artículos.",
951
951
  "sales.documents.items.errorCurrency": "La moneda es obligatoria.",
952
952
  "sales.documents.items.errorDelete": "No se pudo eliminar la línea.",
953
+ "sales.documents.items.errorDeleteLast": "Un pedido debe contener al menos una línea.",
953
954
  "sales.documents.items.errorDeleteShipped": "No puedes eliminar una línea que ya tiene envíos.",
954
955
  "sales.documents.items.errorLoad": "No se pudieron cargar los artículos.",
955
956
  "sales.documents.items.errorNameRequired": "El nombre es obligatorio para líneas personalizadas.",
@@ -1226,6 +1227,7 @@
1226
1227
  "sales.orders.form.shipments": "Envíos",
1227
1228
  "sales.orders.lines.delete": "Eliminar línea de pedido",
1228
1229
  "sales.orders.lines.upsert": "Actualizar línea de pedido",
1230
+ "sales.orders.linesRequired": "Añade al menos una línea antes de crear el pedido.",
1229
1231
  "sales.orders.list.title": "Pedidos",
1230
1232
  "sales.orders.status_change.actor_api_key": "Clave API {name}",
1231
1233
  "sales.orders.status_change.actor_unknown": "Usuario desconocido",
@@ -950,6 +950,7 @@
950
950
  "sales.documents.items.empty": "Brak pozycji.",
951
951
  "sales.documents.items.errorCurrency": "Waluta jest wymagana.",
952
952
  "sales.documents.items.errorDelete": "Nie udało się usunąć pozycji.",
953
+ "sales.documents.items.errorDeleteLast": "Zamówienie musi zawierać co najmniej jedną pozycję.",
953
954
  "sales.documents.items.errorDeleteShipped": "Nie można usunąć pozycji, która ma wysłane sztuki.",
954
955
  "sales.documents.items.errorLoad": "Nie udało się załadować pozycji.",
955
956
  "sales.documents.items.errorNameRequired": "Nazwa jest wymagana dla niestandardowych pozycji.",
@@ -1226,6 +1227,7 @@
1226
1227
  "sales.orders.form.shipments": "Wysyłki",
1227
1228
  "sales.orders.lines.delete": "Usuń pozycję zamówienia",
1228
1229
  "sales.orders.lines.upsert": "Zaktualizuj pozycję zamówienia",
1230
+ "sales.orders.linesRequired": "Dodaj co najmniej jedną pozycję przed utworzeniem zamówienia.",
1229
1231
  "sales.orders.list.title": "Zamówienia",
1230
1232
  "sales.orders.status_change.actor_api_key": "Klucz API {name}",
1231
1233
  "sales.orders.status_change.actor_unknown": "Nieznany użytkownik",
@@ -1,6 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
4
+ import { fetchAllDictionaryEntries } from '@open-mercato/core/modules/dictionaries/lib/fetchAllEntries'
4
5
 
5
6
  export type DictionaryEntryOption = {
6
7
  value: string
@@ -51,10 +52,9 @@ export async function loadStaffDictionary(
51
52
  const name = kind === 'activityTypes' ? 'Staff activity types' : 'Staff address types'
52
53
  const dictionary = await ensureDictionary(key, name)
53
54
  if (!dictionary) return { dictionary: null, entries: [] }
54
- const entriesCall = await apiCall<{ items?: Record<string, unknown>[] }>(`/api/dictionaries/${dictionary.id}/entries`)
55
+ const entriesCall = await fetchAllDictionaryEntries(dictionary.id)
55
56
  if (!entriesCall.ok) return { dictionary, entries: [] }
56
- const items = Array.isArray(entriesCall.result?.items) ? entriesCall.result?.items ?? [] : []
57
- const entries = items
57
+ const entries = entriesCall.items
58
58
  .map((entry) => {
59
59
  if (!entry || typeof entry !== 'object') return null
60
60
  const record = entry as Record<string, unknown>
@@ -10,6 +10,7 @@ import { Spinner } from '@open-mercato/ui/primitives/spinner'
10
10
  import { Textarea } from '@open-mercato/ui/primitives/textarea'
11
11
  import { Badge } from '@open-mercato/ui/primitives/badge'
12
12
  import { emitSalesDocumentDataRefresh } from '@open-mercato/core/modules/sales/lib/frontend/documentDataEvents'
13
+ import { fetchAllDictionaryEntries } from '@open-mercato/core/modules/dictionaries/lib/fetchAllEntries'
13
14
 
14
15
  type OrderRecord = {
15
16
  id: string
@@ -165,12 +166,18 @@ export default function OrderApprovalWidget({ data }: InjectionWidgetComponentPr
165
166
  // Fetch order status dictionary entries using the dictionary ID
166
167
  const { data: statusEntriesData } = useQuery({
167
168
  queryKey: ['dictionary-entries', orderStatusDictionaryId],
168
- queryFn: async () => {
169
+ queryFn: async (): Promise<{ items: DictionaryEntry[] }> => {
169
170
  if (!orderStatusDictionaryId) return { items: [] }
170
- const result = await apiCall<{ items: DictionaryEntry[] }>(
171
- `/api/dictionaries/${orderStatusDictionaryId}/entries`
172
- )
173
- return result.ok ? result.result : { items: [] }
171
+ const result = await fetchAllDictionaryEntries(orderStatusDictionaryId)
172
+ if (!result.ok) return { items: [] }
173
+ const items = result.items.flatMap((entry) => {
174
+ const id = typeof entry.id === 'string' ? entry.id : null
175
+ const value = typeof entry.value === 'string' ? entry.value : null
176
+ if (!id || !value) return []
177
+ const label = typeof entry.label === 'string' && entry.label.trim().length ? entry.label : value
178
+ return [{ id, value, label }]
179
+ })
180
+ return { items }
174
181
  },
175
182
  enabled: Boolean(orderStatusDictionaryId),
176
183
  staleTime: 60_000,