@open-mercato/core 0.6.2-develop.3467.1.2a1818709d → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/helpers/integration/queue-runner.js +15 -3
- package/dist/helpers/integration/queue-runner.js.map +2 -2
- package/dist/helpers/integration/queue.js +1 -1
- package/dist/helpers/integration/queue.js.map +2 -2
- package/dist/helpers/integration/salesUi.js +24 -1
- package/dist/helpers/integration/salesUi.js.map +2 -2
- package/dist/modules/auth/api/roles/acl/route.js +37 -1
- package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
- package/dist/modules/auth/api/roles/route.js +36 -2
- package/dist/modules/auth/api/roles/route.js.map +2 -2
- package/dist/modules/auth/api/users/acl/route.js +36 -1
- package/dist/modules/auth/api/users/acl/route.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +101 -13
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/roles/[id]/edit/page.js +32 -14
- package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +1 -1
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/create/page.js +1 -1
- package/dist/modules/auth/backend/users/create/page.js.map +2 -2
- package/dist/modules/auth/backend/users/roleOptions.js +1 -1
- package/dist/modules/auth/backend/users/roleOptions.js.map +2 -2
- package/dist/modules/auth/commands/roles.js +8 -0
- package/dist/modules/auth/commands/roles.js.map +2 -2
- package/dist/modules/auth/commands/users.js +3 -0
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/components/AclEditor.js +53 -26
- package/dist/modules/auth/components/AclEditor.js.map +2 -2
- package/dist/modules/auth/lib/grantChecks.js +97 -2
- package/dist/modules/auth/lib/grantChecks.js.map +2 -2
- package/dist/modules/business_rules/lib/expression-evaluator.js +9 -7
- package/dist/modules/business_rules/lib/expression-evaluator.js.map +2 -2
- package/dist/modules/customers/ai-agents.js +10 -1
- package/dist/modules/customers/ai-agents.js.map +2 -2
- package/dist/modules/customers/ai-tools/settings-pack.js +55 -1
- package/dist/modules/customers/ai-tools/settings-pack.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +13 -5
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
- package/dist/modules/customers/lib/personCompanyLinkTable.js +4 -30
- package/dist/modules/customers/lib/personCompanyLinkTable.js.map +2 -2
- package/dist/modules/customers/migrations/Migration20260521175027.js +12 -0
- package/dist/modules/customers/migrations/Migration20260521175027.js.map +7 -0
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +41 -12
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
- package/dist/modules/data_sync/api/options.js +2 -0
- package/dist/modules/data_sync/api/options.js.map +2 -2
- package/dist/modules/data_sync/api/run.js +77 -58
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +3 -2
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js +8 -3
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +15 -13
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/integrations/data/validators.js +9 -6
- package/dist/modules/integrations/data/validators.js.map +2 -2
- package/dist/modules/messages/api/route.js +2 -2
- package/dist/modules/messages/api/route.js.map +2 -2
- package/dist/modules/messages/commands/messages.js +42 -2
- package/dist/modules/messages/commands/messages.js.map +2 -2
- package/dist/modules/messages/components/MessagesInboxPageClient.js +66 -87
- package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
- package/dist/modules/messages/components/inboxFilters.js +133 -0
- package/dist/modules/messages/components/inboxFilters.js.map +7 -0
- package/dist/modules/messages/data/validators.js +8 -6
- package/dist/modules/messages/data/validators.js.map +2 -2
- package/dist/modules/progress/api/jobs/route.js +26 -16
- package/dist/modules/progress/api/jobs/route.js.map +2 -2
- package/dist/modules/query_index/di.js +8 -6
- package/dist/modules/query_index/di.js.map +2 -2
- package/dist/modules/query_index/lib/indexer.js +12 -10
- package/dist/modules/query_index/lib/indexer.js.map +2 -2
- package/dist/modules/query_index/subscribers/upsert_one.js +7 -1
- package/dist/modules/query_index/subscribers/upsert_one.js.map +2 -2
- package/dist/modules/sales/api/order-adjustments/route.js.map +2 -2
- package/dist/modules/sales/api/quote-adjustments/route.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +104 -0
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/data/validators.js +20 -1
- package/dist/modules/sales/data/validators.js.map +2 -2
- package/dist/modules/sales/lib/makeSalesLineRoute.js.map +2 -2
- package/dist/modules/sync_excel/api/import/route.js +126 -116
- package/dist/modules/sync_excel/api/import/route.js.map +2 -2
- package/dist/modules/sync_excel/lib/adapters/customers.js +1 -0
- package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
- package/dist/modules/translations/components/TranslationManager.js +9 -4
- package/dist/modules/translations/components/TranslationManager.js.map +2 -2
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/event-trigger-service.js +23 -11
- package/dist/modules/workflows/lib/event-trigger-service.js.map +2 -2
- package/jest.config.cjs +4 -1
- package/package.json +15 -16
- package/src/helpers/integration/queue-runner.ts +21 -2
- package/src/helpers/integration/queue.ts +1 -1
- package/src/helpers/integration/salesUi.ts +26 -1
- package/src/modules/auth/api/roles/acl/route.ts +39 -1
- package/src/modules/auth/api/roles/route.ts +38 -2
- package/src/modules/auth/api/users/acl/route.ts +41 -1
- package/src/modules/auth/api/users/route.ts +105 -12
- package/src/modules/auth/backend/roles/[id]/edit/page.tsx +35 -13
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +1 -1
- package/src/modules/auth/backend/users/create/page.tsx +1 -1
- package/src/modules/auth/backend/users/roleOptions.ts +2 -1
- package/src/modules/auth/commands/roles.ts +8 -0
- package/src/modules/auth/commands/users.ts +3 -0
- package/src/modules/auth/components/AclEditor.tsx +50 -24
- package/src/modules/auth/lib/grantChecks.ts +116 -2
- package/src/modules/business_rules/lib/expression-evaluator.ts +9 -13
- package/src/modules/customers/ai-agents.ts +10 -1
- package/src/modules/customers/ai-tools/settings-pack.ts +62 -1
- package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +18 -7
- package/src/modules/customers/lib/personCompanyLinkTable.ts +33 -41
- package/src/modules/customers/migrations/Migration20260521175027.ts +39 -0
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +38 -12
- package/src/modules/data_sync/api/options.ts +2 -0
- package/src/modules/data_sync/api/run.ts +80 -61
- package/src/modules/data_sync/backend/data-sync/page.tsx +10 -0
- package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +17 -3
- package/src/modules/data_sync/i18n/de.json +3 -0
- package/src/modules/data_sync/i18n/en.json +3 -0
- package/src/modules/data_sync/i18n/es.json +3 -0
- package/src/modules/data_sync/i18n/pl.json +3 -0
- package/src/modules/data_sync/lib/adapter.ts +9 -0
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +15 -13
- package/src/modules/integrations/data/validators.ts +5 -4
- package/src/modules/messages/api/route.ts +2 -2
- package/src/modules/messages/commands/messages.ts +54 -2
- package/src/modules/messages/components/MessagesInboxPageClient.tsx +75 -90
- package/src/modules/messages/components/inboxFilters.ts +162 -0
- package/src/modules/messages/data/validators.ts +7 -5
- package/src/modules/messages/i18n/de.json +5 -4
- package/src/modules/messages/i18n/en.json +5 -4
- package/src/modules/messages/i18n/es.json +5 -4
- package/src/modules/messages/i18n/pl.json +5 -4
- package/src/modules/progress/api/jobs/route.ts +30 -20
- package/src/modules/query_index/di.ts +9 -7
- package/src/modules/query_index/lib/indexer.ts +13 -11
- package/src/modules/query_index/subscribers/upsert_one.ts +9 -1
- package/src/modules/sales/api/order-adjustments/route.ts +2 -1
- package/src/modules/sales/api/quote-adjustments/route.ts +2 -1
- package/src/modules/sales/commands/documents.ts +116 -0
- package/src/modules/sales/data/validators.ts +38 -0
- package/src/modules/sales/i18n/de.json +2 -0
- package/src/modules/sales/i18n/en.json +2 -0
- package/src/modules/sales/i18n/es.json +2 -0
- package/src/modules/sales/i18n/pl.json +2 -0
- package/src/modules/sales/lib/makeSalesLineRoute.ts +2 -1
- package/src/modules/staff/i18n/de.json +1 -0
- package/src/modules/staff/i18n/en.json +1 -0
- package/src/modules/staff/i18n/es.json +1 -0
- package/src/modules/staff/i18n/pl.json +1 -0
- package/src/modules/sync_excel/api/import/route.ts +141 -131
- package/src/modules/sync_excel/lib/adapters/customers.ts +1 -0
- package/src/modules/translations/components/TranslationManager.tsx +9 -4
- package/src/modules/workflows/lib/activity-executor.ts +4 -3
- package/src/modules/workflows/lib/event-trigger-service.ts +24 -11
- package/tsconfig.json +1 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'
|
|
2
|
+
|
|
3
|
+
type Translator = (key: string, fallback?: string) => string
|
|
4
|
+
|
|
5
|
+
export type SenderOption = {
|
|
6
|
+
value: string
|
|
7
|
+
label: string
|
|
8
|
+
description?: string | null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type MessagesInboxFilterContext = {
|
|
12
|
+
t: Translator
|
|
13
|
+
typeOptions: { value: string; label: string }[]
|
|
14
|
+
senderOptions: SenderOption[]
|
|
15
|
+
loadSenderOptions: (query?: string) => Promise<SenderOption[]>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function buildMessagesInboxFilters({
|
|
19
|
+
t,
|
|
20
|
+
typeOptions,
|
|
21
|
+
senderOptions,
|
|
22
|
+
loadSenderOptions,
|
|
23
|
+
}: MessagesInboxFilterContext): FilterDef[] {
|
|
24
|
+
const senderLabel = (val: string): string => {
|
|
25
|
+
const match = senderOptions.find((opt) => opt.value === val)
|
|
26
|
+
return match?.label ?? val
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
id: 'status',
|
|
32
|
+
label: t('messages.filters.status', 'Status'),
|
|
33
|
+
type: 'select',
|
|
34
|
+
options: [
|
|
35
|
+
{ value: '', label: t('messages.filters.all', 'All') },
|
|
36
|
+
{ value: 'unread', label: t('messages.status.unread', 'Unread') },
|
|
37
|
+
{ value: 'read', label: t('messages.status.read', 'Read') },
|
|
38
|
+
{ value: 'archived', label: t('messages.status.archived', 'Archived') },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 'type',
|
|
43
|
+
label: t('messages.filters.type', 'Type'),
|
|
44
|
+
type: 'select',
|
|
45
|
+
options: [{ value: '', label: t('messages.filters.all', 'All') }, ...typeOptions],
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'hasObjects',
|
|
49
|
+
label: t('messages.filters.hasObjects', 'Has objects'),
|
|
50
|
+
type: 'select',
|
|
51
|
+
options: [
|
|
52
|
+
{ value: '', label: t('messages.filters.all', 'All') },
|
|
53
|
+
{ value: 'true', label: t('common.yes', 'Yes') },
|
|
54
|
+
{ value: 'false', label: t('common.no', 'No') },
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: 'hasAttachments',
|
|
59
|
+
label: t('messages.filters.hasAttachments', 'Has attachments'),
|
|
60
|
+
type: 'select',
|
|
61
|
+
options: [
|
|
62
|
+
{ value: '', label: t('messages.filters.all', 'All') },
|
|
63
|
+
{ value: 'true', label: t('common.yes', 'Yes') },
|
|
64
|
+
{ value: 'false', label: t('common.no', 'No') },
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'hasActions',
|
|
69
|
+
label: t('messages.filters.hasActions', 'Has actions'),
|
|
70
|
+
type: 'select',
|
|
71
|
+
options: [
|
|
72
|
+
{ value: '', label: t('messages.filters.all', 'All') },
|
|
73
|
+
{ value: 'true', label: t('common.yes', 'Yes') },
|
|
74
|
+
{ value: 'false', label: t('common.no', 'No') },
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'senderId',
|
|
79
|
+
label: t('messages.filters.sender', 'Sender'),
|
|
80
|
+
type: 'combobox',
|
|
81
|
+
placeholder: t('messages.filters.senderEmpty', 'Type to search users'),
|
|
82
|
+
options: senderOptions,
|
|
83
|
+
loadOptions: loadSenderOptions,
|
|
84
|
+
formatValue: senderLabel,
|
|
85
|
+
formatDescription: (val: string) =>
|
|
86
|
+
senderOptions.find((opt) => opt.value === val)?.description ?? null,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 'since',
|
|
90
|
+
label: t('messages.filters.since', 'Sent after'),
|
|
91
|
+
type: 'text',
|
|
92
|
+
placeholder: t('messages.filters.sincePlaceholder', 'YYYY-MM-DD'),
|
|
93
|
+
},
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/
|
|
98
|
+
|
|
99
|
+
export function normalizeMessagesSinceValue(input: string): string | null {
|
|
100
|
+
const trimmed = input.trim()
|
|
101
|
+
if (!trimmed) return null
|
|
102
|
+
if (DATE_ONLY_PATTERN.test(trimmed)) {
|
|
103
|
+
const parsed = new Date(`${trimmed}T00:00:00.000Z`)
|
|
104
|
+
if (Number.isNaN(parsed.getTime())) return null
|
|
105
|
+
if (parsed.toISOString().slice(0, 10) !== trimmed) return null
|
|
106
|
+
return parsed.toISOString()
|
|
107
|
+
}
|
|
108
|
+
const parsed = new Date(trimmed)
|
|
109
|
+
if (Number.isNaN(parsed.getTime())) return null
|
|
110
|
+
return parsed.toISOString()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export type BuildMessagesListParamsOptions = {
|
|
114
|
+
folder: string
|
|
115
|
+
page: number
|
|
116
|
+
pageSize: number
|
|
117
|
+
search: string
|
|
118
|
+
filterValues: FilterValues
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function buildMessagesListParams({
|
|
122
|
+
folder,
|
|
123
|
+
page,
|
|
124
|
+
pageSize,
|
|
125
|
+
search,
|
|
126
|
+
filterValues,
|
|
127
|
+
}: BuildMessagesListParamsOptions): URLSearchParams {
|
|
128
|
+
const params = new URLSearchParams()
|
|
129
|
+
params.set('folder', folder)
|
|
130
|
+
params.set('page', String(page))
|
|
131
|
+
params.set('pageSize', String(pageSize))
|
|
132
|
+
|
|
133
|
+
if (search.trim()) {
|
|
134
|
+
params.set('search', search.trim())
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const stringValue = (id: string): string => {
|
|
138
|
+
const raw = filterValues[id]
|
|
139
|
+
return typeof raw === 'string' ? raw.trim() : ''
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const status = stringValue('status')
|
|
143
|
+
const type = stringValue('type')
|
|
144
|
+
const hasObjects = stringValue('hasObjects')
|
|
145
|
+
const hasAttachments = stringValue('hasAttachments')
|
|
146
|
+
const hasActions = stringValue('hasActions')
|
|
147
|
+
const senderId = stringValue('senderId')
|
|
148
|
+
const sinceRaw = stringValue('since')
|
|
149
|
+
|
|
150
|
+
if (status) params.set('status', status)
|
|
151
|
+
if (type) params.set('type', type)
|
|
152
|
+
if (hasObjects) params.set('hasObjects', hasObjects)
|
|
153
|
+
if (hasAttachments) params.set('hasAttachments', hasAttachments)
|
|
154
|
+
if (hasActions) params.set('hasActions', hasActions)
|
|
155
|
+
if (senderId) params.set('senderId', senderId)
|
|
156
|
+
if (sinceRaw) {
|
|
157
|
+
const normalized = normalizeMessagesSinceValue(sinceRaw)
|
|
158
|
+
if (normalized) params.set('since', normalized)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return params
|
|
162
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
|
+
import { parseBooleanFlag } from '@open-mercato/shared/lib/boolean'
|
|
2
3
|
|
|
3
4
|
function collectDuplicateRecipientIds(
|
|
4
5
|
recipients: Array<{ userId: string }>,
|
|
@@ -178,14 +179,15 @@ export const updateDraftSchema = z.object({
|
|
|
178
179
|
externalEmail: z.string().email().optional(),
|
|
179
180
|
externalName: z.string().min(1).max(255).optional(),
|
|
180
181
|
recipients: z.array(messageRecipientSchema).optional(),
|
|
181
|
-
subject: z.string().
|
|
182
|
-
body: z.string().
|
|
182
|
+
subject: z.string().max(500).optional(),
|
|
183
|
+
body: z.string().max(50000).optional(),
|
|
183
184
|
bodyFormat: z.enum(['text', 'markdown']).optional(),
|
|
184
185
|
priority: z.enum(['low', 'normal', 'high', 'urgent']).optional(),
|
|
185
186
|
objects: z.array(messageObjectSchema).optional(),
|
|
186
187
|
attachmentIds: z.array(z.string().uuid()).optional(),
|
|
187
188
|
actionData: messageActionDataSchema.optional(),
|
|
188
189
|
sendViaEmail: z.boolean().optional(),
|
|
190
|
+
isDraft: z.literal(false).optional(),
|
|
189
191
|
}).superRefine((value, ctx) => {
|
|
190
192
|
if (value.recipients) {
|
|
191
193
|
const duplicateRecipientIds = collectDuplicateRecipientIds(value.recipients)
|
|
@@ -209,9 +211,9 @@ export const listMessagesSchema = z.object({
|
|
|
209
211
|
sourceEntityType: z.string().optional(),
|
|
210
212
|
sourceEntityId: z.string().uuid().optional(),
|
|
211
213
|
externalEmail: z.string().email().optional(),
|
|
212
|
-
hasObjects: z.
|
|
213
|
-
hasAttachments: z.
|
|
214
|
-
hasActions: z.
|
|
214
|
+
hasObjects: z.string().transform(parseBooleanFlag).optional(),
|
|
215
|
+
hasAttachments: z.string().transform(parseBooleanFlag).optional(),
|
|
216
|
+
hasActions: z.string().transform(parseBooleanFlag).optional(),
|
|
215
217
|
senderId: z.string().uuid().optional(),
|
|
216
218
|
search: z.string().max(200).optional(),
|
|
217
219
|
since: z.string().datetime().optional(),
|
|
@@ -131,12 +131,13 @@
|
|
|
131
131
|
"messages.externalName": "Externer Name",
|
|
132
132
|
"messages.fields.type": "Nachrichtentyp",
|
|
133
133
|
"messages.filters.all": "Alle",
|
|
134
|
-
"messages.filters.hasActions": "Aktionen",
|
|
135
|
-
"messages.filters.hasAttachments": "Anhänge",
|
|
136
|
-
"messages.filters.hasObjects": "Objekte",
|
|
134
|
+
"messages.filters.hasActions": "Hat Aktionen",
|
|
135
|
+
"messages.filters.hasAttachments": "Hat Anhänge",
|
|
136
|
+
"messages.filters.hasObjects": "Hat Objekte",
|
|
137
137
|
"messages.filters.sender": "Absender",
|
|
138
|
+
"messages.filters.senderEmpty": "Tippe, um Benutzer zu suchen",
|
|
138
139
|
"messages.filters.since": "Gesendet nach",
|
|
139
|
-
"messages.filters.sincePlaceholder": "YYYY-MM-
|
|
140
|
+
"messages.filters.sincePlaceholder": "YYYY-MM-DD",
|
|
140
141
|
"messages.filters.status": "Status",
|
|
141
142
|
"messages.filters.type": "Typ",
|
|
142
143
|
"messages.flash.actionSuccess": "Aktion abgeschlossen.",
|
|
@@ -131,12 +131,13 @@
|
|
|
131
131
|
"messages.externalName": "External name",
|
|
132
132
|
"messages.fields.type": "Message type",
|
|
133
133
|
"messages.filters.all": "All",
|
|
134
|
-
"messages.filters.hasActions": "
|
|
135
|
-
"messages.filters.hasAttachments": "
|
|
136
|
-
"messages.filters.hasObjects": "
|
|
134
|
+
"messages.filters.hasActions": "Has actions",
|
|
135
|
+
"messages.filters.hasAttachments": "Has attachments",
|
|
136
|
+
"messages.filters.hasObjects": "Has objects",
|
|
137
137
|
"messages.filters.sender": "Sender",
|
|
138
|
+
"messages.filters.senderEmpty": "Type to search users",
|
|
138
139
|
"messages.filters.since": "Sent after",
|
|
139
|
-
"messages.filters.sincePlaceholder": "YYYY-MM-
|
|
140
|
+
"messages.filters.sincePlaceholder": "YYYY-MM-DD",
|
|
140
141
|
"messages.filters.status": "Status",
|
|
141
142
|
"messages.filters.type": "Type",
|
|
142
143
|
"messages.flash.actionSuccess": "Action completed.",
|
|
@@ -131,12 +131,13 @@
|
|
|
131
131
|
"messages.externalName": "Nombre externo",
|
|
132
132
|
"messages.fields.type": "Tipo de mensaje",
|
|
133
133
|
"messages.filters.all": "Todos",
|
|
134
|
-
"messages.filters.hasActions": "
|
|
135
|
-
"messages.filters.hasAttachments": "
|
|
136
|
-
"messages.filters.hasObjects": "
|
|
134
|
+
"messages.filters.hasActions": "Tiene acciones",
|
|
135
|
+
"messages.filters.hasAttachments": "Tiene adjuntos",
|
|
136
|
+
"messages.filters.hasObjects": "Tiene objetos",
|
|
137
137
|
"messages.filters.sender": "Remitente",
|
|
138
|
+
"messages.filters.senderEmpty": "Escribe para buscar usuarios",
|
|
138
139
|
"messages.filters.since": "Enviado después de",
|
|
139
|
-
"messages.filters.sincePlaceholder": "YYYY-MM-
|
|
140
|
+
"messages.filters.sincePlaceholder": "YYYY-MM-DD",
|
|
140
141
|
"messages.filters.status": "Estado",
|
|
141
142
|
"messages.filters.type": "Tipo",
|
|
142
143
|
"messages.flash.actionSuccess": "Acción completada.",
|
|
@@ -131,12 +131,13 @@
|
|
|
131
131
|
"messages.externalName": "Nazwa zewnętrzna",
|
|
132
132
|
"messages.fields.type": "Typ wiadomości",
|
|
133
133
|
"messages.filters.all": "Wszystkie",
|
|
134
|
-
"messages.filters.hasActions": "
|
|
135
|
-
"messages.filters.hasAttachments": "
|
|
136
|
-
"messages.filters.hasObjects": "
|
|
134
|
+
"messages.filters.hasActions": "Ma akcje",
|
|
135
|
+
"messages.filters.hasAttachments": "Ma załączniki",
|
|
136
|
+
"messages.filters.hasObjects": "Ma obiekty",
|
|
137
137
|
"messages.filters.sender": "Nadawca",
|
|
138
|
+
"messages.filters.senderEmpty": "Wpisz, aby wyszukać użytkowników",
|
|
138
139
|
"messages.filters.since": "Wysłane po",
|
|
139
|
-
"messages.filters.sincePlaceholder": "
|
|
140
|
+
"messages.filters.sincePlaceholder": "RRRR-MM-DD",
|
|
140
141
|
"messages.filters.status": "Status",
|
|
141
142
|
"messages.filters.type": "Typ",
|
|
142
143
|
"messages.flash.actionSuccess": "Akcja wykonana.",
|
|
@@ -138,27 +138,37 @@ export async function GET(req: Request) {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
export async function POST(req: Request) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
141
|
+
try {
|
|
142
|
+
const auth = await getAuthFromRequest(req)
|
|
143
|
+
if (!auth || !auth.tenantId) {
|
|
144
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const body = await req.json()
|
|
148
|
+
const parsed = createProgressJobSchema.safeParse(body)
|
|
149
|
+
if (!parsed.success) {
|
|
150
|
+
return NextResponse.json({ error: 'Invalid input', details: parsed.error.flatten() }, { status: 400 })
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const container = await createRequestContainer()
|
|
154
|
+
const progressService = container.resolve('progressService') as import('../../lib/progressService').ProgressService
|
|
155
|
+
|
|
156
|
+
const job = await progressService.createJob(parsed.data, {
|
|
157
|
+
tenantId: auth.tenantId,
|
|
158
|
+
organizationId: auth.orgId,
|
|
159
|
+
userId: auth.sub,
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
return NextResponse.json({ id: job.id }, { status: 201 })
|
|
163
|
+
} catch (error) {
|
|
164
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
165
|
+
const stack = error instanceof Error ? error.stack : undefined
|
|
166
|
+
console.error('[progress.jobs.create] unhandled error', { message, stack })
|
|
167
|
+
return NextResponse.json(
|
|
168
|
+
{ error: 'Failed to create progress job.', message, stack },
|
|
169
|
+
{ status: 500 },
|
|
170
|
+
)
|
|
150
171
|
}
|
|
151
|
-
|
|
152
|
-
const container = await createRequestContainer()
|
|
153
|
-
const progressService = container.resolve('progressService') as import('../../lib/progressService').ProgressService
|
|
154
|
-
|
|
155
|
-
const job = await progressService.createJob(parsed.data, {
|
|
156
|
-
tenantId: auth.tenantId,
|
|
157
|
-
organizationId: auth.orgId,
|
|
158
|
-
userId: auth.sub,
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
return NextResponse.json({ id: job.id }, { status: 201 })
|
|
162
172
|
}
|
|
163
173
|
|
|
164
174
|
const jobListItemSchema = z.object({
|
|
@@ -66,8 +66,8 @@ export function register(container: AppContainer) {
|
|
|
66
66
|
const makeUpsertHandler = (entityType: string) => async (payload: any, ctx: any) => {
|
|
67
67
|
try {
|
|
68
68
|
const em = ctx.resolve('em')
|
|
69
|
-
let orgId = payload?.organizationId || payload?.orgId || null
|
|
70
|
-
let tenantId = payload?.tenantId || null
|
|
69
|
+
let orgId = payload?.organizationId || payload?.orgId || ctx?.organizationId || null
|
|
70
|
+
let tenantId = payload?.tenantId || ctx?.tenantId || null
|
|
71
71
|
const id = String(payload?.id || payload?.recordId || '')
|
|
72
72
|
if (!id) return
|
|
73
73
|
if (!orgId || !tenantId) {
|
|
@@ -119,24 +119,26 @@ export function register(container: AppContainer) {
|
|
|
119
119
|
const makeDeleteHandler = (entityType: string) => async (payload: any, ctx: any) => {
|
|
120
120
|
try {
|
|
121
121
|
const em = ctx.resolve('em')
|
|
122
|
-
let orgId = payload?.organizationId || payload?.orgId || null
|
|
122
|
+
let orgId = payload?.organizationId || payload?.orgId || ctx?.organizationId || null
|
|
123
|
+
let tenantId = payload?.tenantId || ctx?.tenantId || null
|
|
123
124
|
const id = String(payload?.id || payload?.recordId || '')
|
|
124
125
|
if (!id) return
|
|
125
|
-
if (!orgId) {
|
|
126
|
+
if (!orgId || !tenantId) {
|
|
126
127
|
try {
|
|
127
128
|
const db = (em as any).getKysely()
|
|
128
129
|
const table = resolveEntityTableName(em, entityType)
|
|
129
130
|
const row = await db
|
|
130
131
|
.selectFrom(table as any)
|
|
131
|
-
.select(['organization_id' as any])
|
|
132
|
+
.select(['organization_id' as any, 'tenant_id' as any])
|
|
132
133
|
.where('id' as any, '=', id)
|
|
133
|
-
.executeTakeFirst() as { organization_id: string | null } | undefined
|
|
134
|
+
.executeTakeFirst() as { organization_id: string | null; tenant_id: string | null } | undefined
|
|
134
135
|
orgId = row?.organization_id ?? orgId
|
|
136
|
+
tenantId = row?.tenant_id ?? tenantId
|
|
135
137
|
} catch {}
|
|
136
138
|
}
|
|
137
139
|
try {
|
|
138
140
|
const bus = ctx.resolve('eventBus') as any
|
|
139
|
-
await bus.emitEvent('query_index.delete_one', { entityType, recordId: id, organizationId: orgId })
|
|
141
|
+
await bus.emitEvent('query_index.delete_one', { entityType, recordId: id, organizationId: orgId, tenantId })
|
|
140
142
|
} catch {}
|
|
141
143
|
} catch {}
|
|
142
144
|
}
|
|
@@ -158,7 +158,7 @@ function scopeEntityIndexes<QB extends { where: (...args: any[]) => QB }>(
|
|
|
158
158
|
|
|
159
159
|
export async function upsertIndexRow(
|
|
160
160
|
em: EntityManager,
|
|
161
|
-
args: { entityType: string; recordId: string; organizationId?: string | null; tenantId?: string | null }
|
|
161
|
+
args: { entityType: string; recordId: string; organizationId?: string | null; tenantId?: string | null; searchTokenDoc?: Record<string, unknown> | null }
|
|
162
162
|
): Promise<UpsertIndexResult> {
|
|
163
163
|
const db = (em as any).getKysely()
|
|
164
164
|
|
|
@@ -234,21 +234,23 @@ export async function upsertIndexRow(
|
|
|
234
234
|
const created = !existed
|
|
235
235
|
const revived = existed && wasDeleted
|
|
236
236
|
try {
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
237
|
+
const tokenDoc = args.searchTokenDoc ?? (() => {
|
|
238
|
+
const encryption = resolveTenantEncryptionService(em as any)
|
|
239
|
+
const dekKeyCache = new Map<string | null, string | null>()
|
|
240
|
+
return decryptIndexDocForSearch(
|
|
241
|
+
args.entityType,
|
|
242
|
+
doc,
|
|
243
|
+
{ tenantId: args.tenantId ?? null, organizationId: args.organizationId ?? null },
|
|
244
|
+
encryption,
|
|
245
|
+
dekKeyCache,
|
|
246
|
+
)
|
|
247
|
+
})()
|
|
246
248
|
await replaceSearchTokensForRecord(db, {
|
|
247
249
|
entityType: args.entityType,
|
|
248
250
|
recordId: args.recordId,
|
|
249
251
|
organizationId: args.organizationId ?? null,
|
|
250
252
|
tenantId: args.tenantId ?? null,
|
|
251
|
-
doc: tokenDoc,
|
|
253
|
+
doc: await tokenDoc,
|
|
252
254
|
})
|
|
253
255
|
} catch {}
|
|
254
256
|
return { doc, existed, wasDeleted, created, revived }
|
|
@@ -28,7 +28,15 @@ export default async function handle(payload: any, ctx: { resolve: <T=any>(name:
|
|
|
28
28
|
organizationId = resolvedScope.organizationId
|
|
29
29
|
tenantId = resolvedScope.tenantId
|
|
30
30
|
|
|
31
|
-
const result = await upsertIndexRow(em, {
|
|
31
|
+
const result = await upsertIndexRow(em, {
|
|
32
|
+
entityType,
|
|
33
|
+
recordId,
|
|
34
|
+
organizationId,
|
|
35
|
+
tenantId,
|
|
36
|
+
searchTokenDoc: typeof payload?.searchTokenDoc === 'object' && payload.searchTokenDoc && !Array.isArray(payload.searchTokenDoc)
|
|
37
|
+
? payload.searchTokenDoc
|
|
38
|
+
: null,
|
|
39
|
+
})
|
|
32
40
|
if (!suppressCoverage) {
|
|
33
41
|
const doc = result.doc
|
|
34
42
|
const isActive = !!doc && (doc.deleted_at == null || doc.deleted_at === null)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/core'
|
|
2
3
|
import { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'
|
|
3
4
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
4
5
|
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
@@ -85,7 +86,7 @@ const crud = makeCrudRoute({
|
|
|
85
86
|
const filters: Record<string, unknown> = {}
|
|
86
87
|
if (query.orderId) filters.order_id = { $eq: query.orderId }
|
|
87
88
|
try {
|
|
88
|
-
const em = ctx.container.resolve('em')
|
|
89
|
+
const em = ctx.container.resolve<EntityManager>('em')
|
|
89
90
|
const cfFilters = await buildCustomFieldFiltersFromQuery({
|
|
90
91
|
entityId: E.sales.sales_order_adjustment,
|
|
91
92
|
query,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
|
+
import type { EntityManager } from '@mikro-orm/core'
|
|
2
3
|
import { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'
|
|
3
4
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
4
5
|
import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
|
|
@@ -82,7 +83,7 @@ const crud = makeCrudRoute({
|
|
|
82
83
|
const filters: Record<string, unknown> = {}
|
|
83
84
|
if (query.quoteId) filters.quote_id = { $eq: query.quoteId }
|
|
84
85
|
try {
|
|
85
|
-
const em = ctx.container.resolve('em')
|
|
86
|
+
const em = ctx.container.resolve<EntityManager>('em')
|
|
86
87
|
const cfFilters = await buildCustomFieldFiltersFromQuery({
|
|
87
88
|
entityId: E.sales.sales_quote_adjustment,
|
|
88
89
|
query,
|
|
@@ -67,6 +67,9 @@ import {
|
|
|
67
67
|
} from "../../customers/data/entities";
|
|
68
68
|
import {
|
|
69
69
|
enforceAdjustmentSign,
|
|
70
|
+
validateReturnAdjustmentWithinRemaining,
|
|
71
|
+
RETURN_ADJUSTMENT_EXCEEDS_REMAINING_GROSS_MESSAGE,
|
|
72
|
+
RETURN_ADJUSTMENT_EXCEEDS_REMAINING_NET_MESSAGE,
|
|
70
73
|
quoteCreateSchema,
|
|
71
74
|
quoteLineCreateSchema,
|
|
72
75
|
quoteAdjustmentCreateSchema,
|
|
@@ -7384,6 +7387,63 @@ const orderAdjustmentUpsertCommand: CommandHandler<
|
|
|
7384
7387
|
shippingMethodCode: order.shippingMethodCode ?? null,
|
|
7385
7388
|
paymentMethodCode: order.paymentMethodCode ?? null,
|
|
7386
7389
|
});
|
|
7390
|
+
const effectiveAdjustment = nextAdjustments.find(
|
|
7391
|
+
(adj) => adj.id === adjustmentId,
|
|
7392
|
+
);
|
|
7393
|
+
if (effectiveAdjustment?.kind === "return") {
|
|
7394
|
+
const baselineAdjustments = nextAdjustments.filter(
|
|
7395
|
+
(adj) => adj.id !== adjustmentId,
|
|
7396
|
+
);
|
|
7397
|
+
const baselineCalculation =
|
|
7398
|
+
await salesCalculationService.calculateDocumentTotals({
|
|
7399
|
+
documentKind: "order",
|
|
7400
|
+
lines: calcLines,
|
|
7401
|
+
adjustments: baselineAdjustments,
|
|
7402
|
+
context: calculationContext,
|
|
7403
|
+
existingTotals: resolveExistingPaymentTotals(order),
|
|
7404
|
+
});
|
|
7405
|
+
const issues = validateReturnAdjustmentWithinRemaining({
|
|
7406
|
+
kind: "return",
|
|
7407
|
+
amountNet:
|
|
7408
|
+
effectiveAdjustment.amountNet ?? effectiveAdjustment.amountGross ?? 0,
|
|
7409
|
+
amountGross:
|
|
7410
|
+
effectiveAdjustment.amountGross ?? effectiveAdjustment.amountNet ?? 0,
|
|
7411
|
+
remainingNet: Number(
|
|
7412
|
+
baselineCalculation.totals?.grandTotalNetAmount ?? 0,
|
|
7413
|
+
),
|
|
7414
|
+
remainingGross: Number(
|
|
7415
|
+
baselineCalculation.totals?.grandTotalGrossAmount ?? 0,
|
|
7416
|
+
),
|
|
7417
|
+
});
|
|
7418
|
+
if (issues.length > 0) {
|
|
7419
|
+
const { translate } = await resolveTranslations();
|
|
7420
|
+
const grossIssue = issues.find((issue) => issue.path === "amountGross");
|
|
7421
|
+
const netIssue = issues.find((issue) => issue.path === "amountNet");
|
|
7422
|
+
const fieldErrors: Record<string, string> = {};
|
|
7423
|
+
if (grossIssue) {
|
|
7424
|
+
fieldErrors.amountGross = translate(
|
|
7425
|
+
"sales.adjustments.error.returnExceedsRemainingGrandTotalGross",
|
|
7426
|
+
RETURN_ADJUSTMENT_EXCEEDS_REMAINING_GROSS_MESSAGE,
|
|
7427
|
+
);
|
|
7428
|
+
}
|
|
7429
|
+
if (netIssue) {
|
|
7430
|
+
fieldErrors.amountNet = translate(
|
|
7431
|
+
"sales.adjustments.error.returnExceedsRemainingGrandTotalNet",
|
|
7432
|
+
RETURN_ADJUSTMENT_EXCEEDS_REMAINING_NET_MESSAGE,
|
|
7433
|
+
);
|
|
7434
|
+
}
|
|
7435
|
+
throw new CrudHttpError(400, {
|
|
7436
|
+
error:
|
|
7437
|
+
fieldErrors.amountGross ??
|
|
7438
|
+
fieldErrors.amountNet ??
|
|
7439
|
+
translate(
|
|
7440
|
+
"sales.adjustments.error.returnExceedsRemainingGrandTotalGross",
|
|
7441
|
+
RETURN_ADJUSTMENT_EXCEEDS_REMAINING_GROSS_MESSAGE,
|
|
7442
|
+
),
|
|
7443
|
+
fieldErrors,
|
|
7444
|
+
});
|
|
7445
|
+
}
|
|
7446
|
+
}
|
|
7387
7447
|
const calculation = await salesCalculationService.calculateDocumentTotals({
|
|
7388
7448
|
documentKind: "order",
|
|
7389
7449
|
lines: calcLines,
|
|
@@ -7766,6 +7826,62 @@ const quoteAdjustmentUpsertCommand: CommandHandler<
|
|
|
7766
7826
|
shippingMethodCode: quote.shippingMethodCode ?? null,
|
|
7767
7827
|
paymentMethodCode: quote.paymentMethodCode ?? null,
|
|
7768
7828
|
});
|
|
7829
|
+
const effectiveAdjustment = nextAdjustments.find(
|
|
7830
|
+
(adj) => adj.id === adjustmentId,
|
|
7831
|
+
);
|
|
7832
|
+
if (effectiveAdjustment?.kind === "return") {
|
|
7833
|
+
const baselineAdjustments = nextAdjustments.filter(
|
|
7834
|
+
(adj) => adj.id !== adjustmentId,
|
|
7835
|
+
);
|
|
7836
|
+
const baselineCalculation =
|
|
7837
|
+
await salesCalculationService.calculateDocumentTotals({
|
|
7838
|
+
documentKind: "quote",
|
|
7839
|
+
lines: calcLines,
|
|
7840
|
+
adjustments: baselineAdjustments,
|
|
7841
|
+
context: calculationContext,
|
|
7842
|
+
});
|
|
7843
|
+
const issues = validateReturnAdjustmentWithinRemaining({
|
|
7844
|
+
kind: "return",
|
|
7845
|
+
amountNet:
|
|
7846
|
+
effectiveAdjustment.amountNet ?? effectiveAdjustment.amountGross ?? 0,
|
|
7847
|
+
amountGross:
|
|
7848
|
+
effectiveAdjustment.amountGross ?? effectiveAdjustment.amountNet ?? 0,
|
|
7849
|
+
remainingNet: Number(
|
|
7850
|
+
baselineCalculation.totals?.grandTotalNetAmount ?? 0,
|
|
7851
|
+
),
|
|
7852
|
+
remainingGross: Number(
|
|
7853
|
+
baselineCalculation.totals?.grandTotalGrossAmount ?? 0,
|
|
7854
|
+
),
|
|
7855
|
+
});
|
|
7856
|
+
if (issues.length > 0) {
|
|
7857
|
+
const { translate } = await resolveTranslations();
|
|
7858
|
+
const grossIssue = issues.find((issue) => issue.path === "amountGross");
|
|
7859
|
+
const netIssue = issues.find((issue) => issue.path === "amountNet");
|
|
7860
|
+
const fieldErrors: Record<string, string> = {};
|
|
7861
|
+
if (grossIssue) {
|
|
7862
|
+
fieldErrors.amountGross = translate(
|
|
7863
|
+
"sales.adjustments.error.returnExceedsRemainingGrandTotalGross",
|
|
7864
|
+
RETURN_ADJUSTMENT_EXCEEDS_REMAINING_GROSS_MESSAGE,
|
|
7865
|
+
);
|
|
7866
|
+
}
|
|
7867
|
+
if (netIssue) {
|
|
7868
|
+
fieldErrors.amountNet = translate(
|
|
7869
|
+
"sales.adjustments.error.returnExceedsRemainingGrandTotalNet",
|
|
7870
|
+
RETURN_ADJUSTMENT_EXCEEDS_REMAINING_NET_MESSAGE,
|
|
7871
|
+
);
|
|
7872
|
+
}
|
|
7873
|
+
throw new CrudHttpError(400, {
|
|
7874
|
+
error:
|
|
7875
|
+
fieldErrors.amountGross ??
|
|
7876
|
+
fieldErrors.amountNet ??
|
|
7877
|
+
translate(
|
|
7878
|
+
"sales.adjustments.error.returnExceedsRemainingGrandTotalGross",
|
|
7879
|
+
RETURN_ADJUSTMENT_EXCEEDS_REMAINING_GROSS_MESSAGE,
|
|
7880
|
+
),
|
|
7881
|
+
fieldErrors,
|
|
7882
|
+
});
|
|
7883
|
+
}
|
|
7884
|
+
}
|
|
7769
7885
|
const calculation = await salesCalculationService.calculateDocumentTotals({
|
|
7770
7886
|
documentKind: "quote",
|
|
7771
7887
|
lines: calcLines,
|
|
@@ -523,6 +523,44 @@ export const enforceAdjustmentSign = (
|
|
|
523
523
|
*/
|
|
524
524
|
export const enforceReturnAdjustmentSign = enforceAdjustmentSign
|
|
525
525
|
|
|
526
|
+
export const RETURN_ADJUSTMENT_EXCEEDS_REMAINING_NET_MESSAGE =
|
|
527
|
+
'Return adjustment amountNet exceeds the remaining grand total. Reduce the amount or remove existing returns first.'
|
|
528
|
+
export const RETURN_ADJUSTMENT_EXCEEDS_REMAINING_GROSS_MESSAGE =
|
|
529
|
+
'Return adjustment amountGross exceeds the remaining grand total. Reduce the amount or remove existing returns first.'
|
|
530
|
+
|
|
531
|
+
export type ReturnAdjustmentRemainingCheck = {
|
|
532
|
+
kind?: string | null
|
|
533
|
+
amountNet?: number | null
|
|
534
|
+
amountGross?: number | null
|
|
535
|
+
remainingNet: number
|
|
536
|
+
remainingGross: number
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
export type ReturnAdjustmentRemainingIssue = {
|
|
540
|
+
path: 'amountNet' | 'amountGross'
|
|
541
|
+
message: string
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Inclusive: abs(amount) === remaining is allowed. Tiny epsilon absorbs
|
|
545
|
+
// floating-point rounding from upstream tax/rate adjustments.
|
|
546
|
+
const RETURN_REMAINING_EPSILON = 0.005
|
|
547
|
+
|
|
548
|
+
export const validateReturnAdjustmentWithinRemaining = (
|
|
549
|
+
value: ReturnAdjustmentRemainingCheck
|
|
550
|
+
): ReturnAdjustmentRemainingIssue[] => {
|
|
551
|
+
if (value.kind !== 'return') return []
|
|
552
|
+
const absNet = typeof value.amountNet === 'number' ? Math.abs(value.amountNet) : 0
|
|
553
|
+
const absGross = typeof value.amountGross === 'number' ? Math.abs(value.amountGross) : 0
|
|
554
|
+
const issues: ReturnAdjustmentRemainingIssue[] = []
|
|
555
|
+
if (absGross > value.remainingGross + RETURN_REMAINING_EPSILON) {
|
|
556
|
+
issues.push({ path: 'amountGross', message: RETURN_ADJUSTMENT_EXCEEDS_REMAINING_GROSS_MESSAGE })
|
|
557
|
+
}
|
|
558
|
+
if (absNet > value.remainingNet + RETURN_REMAINING_EPSILON) {
|
|
559
|
+
issues.push({ path: 'amountNet', message: RETURN_ADJUSTMENT_EXCEEDS_REMAINING_NET_MESSAGE })
|
|
560
|
+
}
|
|
561
|
+
return issues
|
|
562
|
+
}
|
|
563
|
+
|
|
526
564
|
export const orderAdjustmentCreateSchema = scoped.extend({
|
|
527
565
|
orderId: uuid(),
|
|
528
566
|
orderLineId: uuid().optional(),
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
"audit_logs.resource_kind.sales.payment": "Zahlung",
|
|
5
5
|
"audit_logs.resource_kind.sales.shipment": "Versand",
|
|
6
6
|
"customers~sales.nav.group": "Vertrieb",
|
|
7
|
+
"sales.adjustments.error.returnExceedsRemainingGrandTotalGross": "Der Bruttobetrag der Rücksendeanpassung überschreitet den verbleibenden Gesamtbetrag. Bitte verringern Sie den Betrag oder entfernen Sie bestehende Rücksendungen.",
|
|
8
|
+
"sales.adjustments.error.returnExceedsRemainingGrandTotalNet": "Der Nettobetrag der Rücksendeanpassung überschreitet den verbleibenden Gesamtbetrag. Bitte verringern Sie den Betrag oder entfernen Sie bestehende Rücksendungen.",
|
|
7
9
|
"sales.audit.channels.create": "Vertriebskanal erstellen",
|
|
8
10
|
"sales.audit.channels.delete": "Vertriebskanal löschen",
|
|
9
11
|
"sales.audit.channels.update": "Vertriebskanal aktualisieren",
|