@open-mercato/core 0.6.3-develop.3525.3.cc18b8a43f → 0.6.3-develop.3562.1.71049275ff

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 (36) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/auth/api/users/route.js +49 -9
  3. package/dist/modules/auth/api/users/route.js.map +2 -2
  4. package/dist/modules/auth/backend/roles/[id]/edit/page.js +32 -14
  5. package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
  6. package/dist/modules/auth/backend/users/[id]/edit/page.js +1 -1
  7. package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
  8. package/dist/modules/auth/backend/users/create/page.js +1 -1
  9. package/dist/modules/auth/backend/users/create/page.js.map +2 -2
  10. package/dist/modules/auth/backend/users/roleOptions.js +1 -1
  11. package/dist/modules/auth/backend/users/roleOptions.js.map +2 -2
  12. package/dist/modules/auth/commands/roles.js +8 -0
  13. package/dist/modules/auth/commands/roles.js.map +2 -2
  14. package/dist/modules/auth/components/AclEditor.js +53 -26
  15. package/dist/modules/auth/components/AclEditor.js.map +2 -2
  16. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +41 -12
  17. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
  18. package/dist/modules/sales/commands/documents.js +104 -0
  19. package/dist/modules/sales/commands/documents.js.map +2 -2
  20. package/dist/modules/sales/data/validators.js +20 -1
  21. package/dist/modules/sales/data/validators.js.map +2 -2
  22. package/package.json +7 -7
  23. package/src/modules/auth/api/users/route.ts +51 -9
  24. package/src/modules/auth/backend/roles/[id]/edit/page.tsx +35 -13
  25. package/src/modules/auth/backend/users/[id]/edit/page.tsx +1 -1
  26. package/src/modules/auth/backend/users/create/page.tsx +1 -1
  27. package/src/modules/auth/backend/users/roleOptions.ts +2 -1
  28. package/src/modules/auth/commands/roles.ts +8 -0
  29. package/src/modules/auth/components/AclEditor.tsx +50 -24
  30. package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +38 -12
  31. package/src/modules/sales/commands/documents.ts +116 -0
  32. package/src/modules/sales/data/validators.ts +38 -0
  33. package/src/modules/sales/i18n/de.json +2 -0
  34. package/src/modules/sales/i18n/en.json +2 -0
  35. package/src/modules/sales/i18n/es.json +2 -0
  36. package/src/modules/sales/i18n/pl.json +2 -0
@@ -7,6 +7,7 @@ type RoleListResponse = {
7
7
 
8
8
  type FetchRoleOptionsParams = {
9
9
  tenantId?: string | null
10
+ includeSuperAdmin?: boolean
10
11
  }
11
12
 
12
13
  export async function fetchRoleOptions(query?: string, params?: FetchRoleOptionsParams): Promise<CrudFieldOption[]> {
@@ -28,7 +29,7 @@ export async function fetchRoleOptions(query?: string, params?: FetchRoleOptions
28
29
  const id = typeof item?.id === 'string' ? item.id.trim() : ''
29
30
  const name = typeof item?.name === 'string' ? item?.name.trim() : ''
30
31
  if (!id || !name) return null
31
- if (name === 'superadmin') return null
32
+ if (name === 'superadmin' && !params?.includeSuperAdmin) return null
32
33
  return { value: id, label: name }
33
34
  })
34
35
  .filter((opt): opt is CrudFieldOption => !!opt)
@@ -235,6 +235,14 @@ const updateRoleCommand: CommandHandler<Record<string, unknown>, Role> = {
235
235
  }
236
236
  }
237
237
  }
238
+ const wantsTenantChange = parsed.tenantId !== undefined && parsed.tenantId !== current.tenantId
239
+ if (wantsTenantChange) {
240
+ const assignments = await em.count(UserRole, { role: current, deletedAt: null })
241
+ if (assignments > 0) {
242
+ throw new CrudHttpError(400, { error: 'Role cannot be moved to another tenant while users are assigned' })
243
+ }
244
+ await em.nativeDelete(RoleAcl, { role: parsed.id as unknown as Role })
245
+ }
238
246
  const de = (ctx.container.resolve('dataEngine') as DataEngine)
239
247
  const role = await de.updateOrmEntity({
240
248
  entity: Role,
@@ -100,6 +100,7 @@ export function AclEditor({
100
100
  userRoles,
101
101
  currentUserIsSuperAdmin,
102
102
  tenantId,
103
+ preserveOnTenantChange = false,
103
104
  }: {
104
105
  kind: 'user' | 'role'
105
106
  targetId: string
@@ -109,6 +110,7 @@ export function AclEditor({
109
110
  userRoles?: string[]
110
111
  currentUserIsSuperAdmin?: boolean
111
112
  tenantId?: string | null
113
+ preserveOnTenantChange?: boolean
112
114
  }) {
113
115
  const actorIsSuperAdmin = !!currentUserIsSuperAdmin
114
116
  const [loading, setLoading] = React.useState(true)
@@ -142,8 +144,33 @@ export function AclEditor({
142
144
  [actorSanitizeFeatures],
143
145
  )
144
146
 
147
+ const tenantIdRef = React.useRef(tenantId)
148
+ React.useEffect(() => { tenantIdRef.current = tenantId }, [tenantId])
149
+ const hasMountedRef = React.useRef(false)
150
+
151
+ const fetchAclState = React.useCallback(async (forTenantId: string | null | undefined, cancelledRef: { current: boolean }) => {
152
+ try {
153
+ const aclQuery = new URLSearchParams()
154
+ aclQuery.set(kind === 'user' ? 'userId' : 'roleId', targetId)
155
+ if (forTenantId) aclQuery.set('tenantId', forTenantId)
156
+ const aclQueryString = aclQuery.toString()
157
+ const aclJson = await readJsonOr<AclPayload>(
158
+ `/api/auth/${kind === 'user' ? 'users' : 'roles'}/acl${aclQueryString ? `?${aclQueryString}` : ''}`,
159
+ undefined,
160
+ { hasCustomAcl: true, isSuperAdmin: false, features: [], organizations: null },
161
+ )
162
+ if (cancelledRef.current) return
163
+ const customAclExists = aclJson.hasCustomAcl !== false
164
+ setHasCustomAcl(customAclExists)
165
+ setOverrideEnabled(customAclExists)
166
+ setIsSuperAdmin(!!aclJson.isSuperAdmin)
167
+ setGranted(actorSanitizeFeatures(aclJson.features))
168
+ setOrganizations(aclJson.organizations == null ? null : Array.isArray(aclJson.organizations) ? aclJson.organizations : [])
169
+ } catch {}
170
+ }, [kind, targetId, actorSanitizeFeatures])
171
+
145
172
  React.useEffect(() => {
146
- let cancelled = false
173
+ const cancelled = { current: false }
147
174
  async function load() {
148
175
  setLoading(true)
149
176
  try {
@@ -152,30 +179,30 @@ export function AclEditor({
152
179
  undefined,
153
180
  { items: [], modules: [] },
154
181
  )
155
- if (!cancelled) {
182
+ if (!cancelled.current) {
156
183
  setFeatures(fJson.items || [])
157
184
  setModules(fJson.modules || [])
158
185
  }
159
186
  } catch {}
160
- try {
161
- const aclQuery = new URLSearchParams()
162
- aclQuery.set(kind === 'user' ? 'userId' : 'roleId', targetId)
163
- if (tenantId) aclQuery.set('tenantId', tenantId)
164
- const aclQueryString = aclQuery.toString()
165
- const aclJson = await readJsonOr<AclPayload>(
166
- `/api/auth/${kind === 'user' ? 'users' : 'roles'}/acl${aclQueryString ? `?${aclQueryString}` : ''}`,
167
- undefined,
168
- { hasCustomAcl: true, isSuperAdmin: false, features: [], organizations: null },
169
- )
170
- if (!cancelled) {
171
- const customAclExists = aclJson.hasCustomAcl !== false
172
- setHasCustomAcl(customAclExists)
173
- setOverrideEnabled(customAclExists)
174
- setIsSuperAdmin(!!aclJson.isSuperAdmin)
175
- setGranted(actorSanitizeFeatures(aclJson.features))
176
- setOrganizations(aclJson.organizations == null ? null : Array.isArray(aclJson.organizations) ? aclJson.organizations : [])
177
- }
178
- } catch {}
187
+ await fetchAclState(tenantIdRef.current, cancelled)
188
+ hasMountedRef.current = true
189
+ if (!cancelled.current) setLoading(false)
190
+ }
191
+ load()
192
+ return () => { cancelled.current = true }
193
+ }, [kind, targetId, fetchAclState])
194
+
195
+ React.useEffect(() => {
196
+ if (!hasMountedRef.current) return
197
+ if (preserveOnTenantChange) return
198
+ const cancelled = { current: false }
199
+ fetchAclState(tenantId, cancelled)
200
+ return () => { cancelled.current = true }
201
+ }, [tenantId, preserveOnTenantChange, fetchAclState])
202
+
203
+ React.useEffect(() => {
204
+ let cancelled = false
205
+ async function loadTenantScoped() {
179
206
  if (canEditOrganizations) {
180
207
  try {
181
208
  const orgQuery = new URLSearchParams()
@@ -206,11 +233,10 @@ export function AclEditor({
206
233
  }
207
234
  } catch {}
208
235
  }
209
- if (!cancelled) setLoading(false)
210
236
  }
211
- load()
237
+ loadTenantScoped()
212
238
  return () => { cancelled = true }
213
- }, [kind, targetId, canEditOrganizations, userRoles, actorSanitizeFeatures, tenantId])
239
+ }, [kind, canEditOrganizations, userRoles, tenantId])
214
240
 
215
241
  // Notify parent of changes
216
242
  React.useEffect(() => {
@@ -32,6 +32,7 @@ type UserResponse = {
32
32
  type BaseProps = {
33
33
  tenantId?: string | null
34
34
  organizationId?: string | null
35
+ preserveOnTenantChange?: boolean
35
36
  }
36
37
 
37
38
  type RoleProps = BaseProps & {
@@ -92,7 +93,7 @@ export const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHan
92
93
  (widget: WidgetCatalogItem) => resolveWidgetText(t, widget.id, 'description', widget.description || ''),
93
94
  [t],
94
95
  )
95
- const { kind, targetId, tenantId, organizationId } = props
96
+ const { kind, targetId, tenantId, organizationId, preserveOnTenantChange = false } = props
96
97
  const [catalog, setCatalog] = React.useState<WidgetCatalogItem[]>([])
97
98
  const [loading, setLoading] = React.useState(true)
98
99
  const [saving, setSaving] = React.useState(false)
@@ -136,10 +137,16 @@ export const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHan
136
137
  setCatalog(mapped)
137
138
  }, [])
138
139
 
139
- const loadRoleData = React.useCallback(async () => {
140
+ const tenantIdRef = React.useRef(tenantId)
141
+ React.useEffect(() => { tenantIdRef.current = tenantId }, [tenantId])
142
+ const organizationIdRef = React.useRef(organizationId)
143
+ React.useEffect(() => { organizationIdRef.current = organizationId }, [organizationId])
144
+ const hasMountedRef = React.useRef(false)
145
+
146
+ const loadRoleData = React.useCallback(async (forTenantId: string | null | undefined, forOrganizationId: string | null | undefined) => {
140
147
  const params = new URLSearchParams({ roleId: targetId })
141
- if (tenantId) params.set('tenantId', tenantId)
142
- if (organizationId) params.set('organizationId', organizationId)
148
+ if (forTenantId) params.set('tenantId', forTenantId)
149
+ if (forOrganizationId) params.set('organizationId', forOrganizationId)
143
150
  const data = await readApiResultOrThrow<RoleResponse>(
144
151
  `/api/dashboards/roles/widgets?${params.toString()}`,
145
152
  undefined,
@@ -151,12 +158,12 @@ export const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHan
151
158
  setMode('override')
152
159
  setOriginalMode('override')
153
160
  setEffective(ids)
154
- }, [organizationId, targetId, tenantId])
161
+ }, [targetId])
155
162
 
156
- const loadUserData = React.useCallback(async () => {
163
+ const loadUserData = React.useCallback(async (forTenantId: string | null | undefined, forOrganizationId: string | null | undefined) => {
157
164
  const params = new URLSearchParams({ userId: targetId })
158
- if (tenantId) params.set('tenantId', tenantId)
159
- if (organizationId) params.set('organizationId', organizationId)
165
+ if (forTenantId) params.set('tenantId', forTenantId)
166
+ if (forOrganizationId) params.set('organizationId', forOrganizationId)
160
167
  const data = await readApiResultOrThrow<UserResponse>(
161
168
  `/api/dashboards/users/widgets?${params.toString()}`,
162
169
  undefined,
@@ -168,7 +175,7 @@ export const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHan
168
175
  setMode(data.mode || 'inherit')
169
176
  setOriginalMode(data.mode || 'inherit')
170
177
  setEffective(Array.isArray(data.effectiveWidgetIds) ? data.effectiveWidgetIds : [])
171
- }, [organizationId, targetId, tenantId])
178
+ }, [targetId])
172
179
 
173
180
  React.useEffect(() => {
174
181
  let cancelled = false
@@ -177,21 +184,40 @@ export const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHan
177
184
  setError(null)
178
185
  try {
179
186
  await loadCatalog()
180
- if (kind === 'role') await loadRoleData()
181
- else await loadUserData()
187
+ if (kind === 'role') await loadRoleData(tenantIdRef.current, organizationIdRef.current)
188
+ else await loadUserData(tenantIdRef.current, organizationIdRef.current)
182
189
  } catch (err) {
183
190
  console.error('Failed to load widget visibility data', err)
184
191
  if (!cancelled) {
185
192
  setError(tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.'))
186
193
  }
187
194
  } finally {
188
- if (!cancelled) setLoading(false)
195
+ if (!cancelled) {
196
+ setLoading(false)
197
+ hasMountedRef.current = true
198
+ }
189
199
  }
190
200
  }
191
201
  load()
192
202
  return () => { cancelled = true }
193
203
  }, [kind, loadCatalog, loadRoleData, loadUserData])
194
204
 
205
+ React.useEffect(() => {
206
+ if (!hasMountedRef.current) return
207
+ if (preserveOnTenantChange) return
208
+ let cancelled = false
209
+ async function refetch() {
210
+ try {
211
+ if (kind === 'role') await loadRoleData(tenantId, organizationId)
212
+ else await loadUserData(tenantId, organizationId)
213
+ } catch (err) {
214
+ console.error('Failed to reload widget visibility data', err)
215
+ }
216
+ }
217
+ refetch()
218
+ return () => { cancelled = true }
219
+ }, [tenantId, organizationId, kind, loadRoleData, loadUserData, preserveOnTenantChange])
220
+
195
221
  const toggle = React.useCallback((id: string) => {
196
222
  setSelected((prev) => (prev.includes(id) ? prev.filter((value) => value !== id) : [...prev, id]))
197
223
  }, [])
@@ -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",
@@ -4,6 +4,8 @@
4
4
  "audit_logs.resource_kind.sales.payment": "Payment",
5
5
  "audit_logs.resource_kind.sales.shipment": "Shipment",
6
6
  "customers~sales.nav.group": "Sales",
7
+ "sales.adjustments.error.returnExceedsRemainingGrandTotalGross": "Return adjustment amountGross exceeds the remaining grand total. Reduce the amount or remove existing returns first.",
8
+ "sales.adjustments.error.returnExceedsRemainingGrandTotalNet": "Return adjustment amountNet exceeds the remaining grand total. Reduce the amount or remove existing returns first.",
7
9
  "sales.audit.channels.create": "Create sales channel",
8
10
  "sales.audit.channels.delete": "Delete sales channel",
9
11
  "sales.audit.channels.update": "Update sales channel",
@@ -4,6 +4,8 @@
4
4
  "audit_logs.resource_kind.sales.payment": "Pago",
5
5
  "audit_logs.resource_kind.sales.shipment": "Envío",
6
6
  "customers~sales.nav.group": "Ventas",
7
+ "sales.adjustments.error.returnExceedsRemainingGrandTotalGross": "El importe bruto del ajuste de devolución supera el total restante. Reduzca el importe o elimine las devoluciones existentes.",
8
+ "sales.adjustments.error.returnExceedsRemainingGrandTotalNet": "El importe neto del ajuste de devolución supera el total restante. Reduzca el importe o elimine las devoluciones existentes.",
7
9
  "sales.audit.channels.create": "Crear canal de ventas",
8
10
  "sales.audit.channels.delete": "Eliminar canal de ventas",
9
11
  "sales.audit.channels.update": "Actualizar canal de ventas",
@@ -4,6 +4,8 @@
4
4
  "audit_logs.resource_kind.sales.payment": "Płatność",
5
5
  "audit_logs.resource_kind.sales.shipment": "Wysyłka",
6
6
  "customers~sales.nav.group": "Sprzedaż",
7
+ "sales.adjustments.error.returnExceedsRemainingGrandTotalGross": "Kwota brutto korekty zwrotu przekracza pozostałą wartość zamówienia. Zmniejsz kwotę lub usuń istniejące zwroty.",
8
+ "sales.adjustments.error.returnExceedsRemainingGrandTotalNet": "Kwota netto korekty zwrotu przekracza pozostałą wartość zamówienia. Zmniejsz kwotę lub usuń istniejące zwroty.",
7
9
  "sales.audit.channels.create": "Utwórz kanał sprzedaży",
8
10
  "sales.audit.channels.delete": "Usuń kanał sprzedaży",
9
11
  "sales.audit.channels.update": "Zaktualizuj kanał sprzedaży",