@open-mercato/core 0.6.6-develop.5617.1.62538c48ca → 0.6.6-develop.5619.1.29f01e2c42

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 (30) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/sales/acl.js +6 -0
  3. package/dist/modules/sales/acl.js.map +2 -2
  4. package/dist/modules/sales/api/returns/route.js +43 -3
  5. package/dist/modules/sales/api/returns/route.js.map +2 -2
  6. package/dist/modules/sales/commands/returns.js +473 -213
  7. package/dist/modules/sales/commands/returns.js.map +2 -2
  8. package/dist/modules/sales/commands/shared.js +2 -0
  9. package/dist/modules/sales/commands/shared.js.map +2 -2
  10. package/dist/modules/sales/components/documents/ReturnEditDialog.js +125 -0
  11. package/dist/modules/sales/components/documents/ReturnEditDialog.js.map +7 -0
  12. package/dist/modules/sales/components/documents/ReturnsSection.js +102 -6
  13. package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
  14. package/dist/modules/sales/data/validators.js +13 -0
  15. package/dist/modules/sales/data/validators.js.map +2 -2
  16. package/dist/modules/sales/setup.js +1 -0
  17. package/dist/modules/sales/setup.js.map +2 -2
  18. package/package.json +7 -7
  19. package/src/modules/sales/acl.ts +6 -0
  20. package/src/modules/sales/api/returns/route.ts +41 -3
  21. package/src/modules/sales/commands/returns.ts +561 -229
  22. package/src/modules/sales/commands/shared.ts +1 -0
  23. package/src/modules/sales/components/documents/ReturnEditDialog.tsx +157 -0
  24. package/src/modules/sales/components/documents/ReturnsSection.tsx +105 -3
  25. package/src/modules/sales/data/validators.ts +15 -0
  26. package/src/modules/sales/i18n/de.json +11 -0
  27. package/src/modules/sales/i18n/en.json +11 -0
  28. package/src/modules/sales/i18n/es.json +11 -0
  29. package/src/modules/sales/i18n/pl.json +11 -0
  30. package/src/modules/sales/setup.ts +1 -0
@@ -10,6 +10,7 @@ export { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'
10
10
  /** Resource kinds used by the document-aggregate optimistic-lock check. */
11
11
  export const SALES_RESOURCE_KIND_ORDER = 'sales.order'
12
12
  export const SALES_RESOURCE_KIND_QUOTE = 'sales.quote'
13
+ export const SALES_RESOURCE_KIND_RETURN = 'sales.return'
13
14
 
14
15
  /**
15
16
  * Enforce the document-aggregate OSS optimistic lock for a sales sub-resource
@@ -0,0 +1,157 @@
1
+ "use client"
2
+
3
+ import * as React from 'react'
4
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'
5
+ import { useDialogKeyHandler } from '@open-mercato/ui/hooks/useDialogKeyHandler'
6
+ import { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'
7
+ import { updateCrud } from '@open-mercato/ui/backend/utils/crud'
8
+ import { flash } from '@open-mercato/ui/backend/FlashMessages'
9
+ import { useT } from '@open-mercato/shared/lib/i18n/context'
10
+ import { E } from '#generated/entities.ids.generated'
11
+ import { handleSectionMutationError } from './optimisticLock'
12
+
13
+ export type ReturnEditRecord = {
14
+ id: string
15
+ reason: string | null
16
+ notes: string | null
17
+ returnedAt: string | null
18
+ updatedAt: string | null
19
+ }
20
+
21
+ type ReturnEditFormValues = {
22
+ id: string
23
+ updatedAt: string | null
24
+ reason: string
25
+ notes: string
26
+ returnedAt: string
27
+ }
28
+
29
+ type ReturnEditDialogProps = {
30
+ open: boolean
31
+ returnRecord: ReturnEditRecord | null
32
+ orderId: string
33
+ organizationId: string | null
34
+ tenantId: string | null
35
+ onClose: () => void
36
+ onSaved: () => Promise<void>
37
+ }
38
+
39
+ function toDateInputValue(value: string | null | undefined): string {
40
+ if (!value) return ''
41
+ const date = new Date(value)
42
+ if (Number.isNaN(date.getTime())) return ''
43
+ return date.toISOString().slice(0, 10)
44
+ }
45
+
46
+ export function ReturnEditDialog({
47
+ open,
48
+ returnRecord,
49
+ orderId,
50
+ organizationId,
51
+ tenantId,
52
+ onClose,
53
+ onSaved,
54
+ }: ReturnEditDialogProps) {
55
+ const t = useT()
56
+ const dialogContentRef = React.useRef<HTMLDivElement | null>(null)
57
+
58
+ const initialValues = React.useMemo<ReturnEditFormValues>(
59
+ () => ({
60
+ id: returnRecord?.id ?? '',
61
+ // Drives CrudForm's automatic optimistic-lock header derivation.
62
+ updatedAt: returnRecord?.updatedAt ?? null,
63
+ reason: returnRecord?.reason ?? '',
64
+ notes: returnRecord?.notes ?? '',
65
+ returnedAt: toDateInputValue(returnRecord?.returnedAt),
66
+ }),
67
+ [returnRecord],
68
+ )
69
+
70
+ const fields = React.useMemo<CrudField[]>(
71
+ () => [
72
+ {
73
+ id: 'reason',
74
+ label: t('sales.returns.reason', 'Reason'),
75
+ type: 'text',
76
+ placeholder: t('sales.returns.reason.placeholder', 'Optional'),
77
+ },
78
+ {
79
+ id: 'returnedAt',
80
+ label: t('sales.returns.returnedAt', 'Returned at'),
81
+ type: 'date',
82
+ },
83
+ {
84
+ id: 'notes',
85
+ label: t('sales.returns.notes', 'Notes'),
86
+ type: 'textarea',
87
+ placeholder: t('sales.returns.notes.placeholder', 'Optional'),
88
+ },
89
+ ],
90
+ [t],
91
+ )
92
+
93
+ const handleSubmit = React.useCallback(
94
+ async (values: ReturnEditFormValues) => {
95
+ if (!returnRecord) return
96
+ const reason = typeof values.reason === 'string' ? values.reason.trim() : ''
97
+ const notes = typeof values.notes === 'string' ? values.notes.trim() : ''
98
+ const returnedAt = typeof values.returnedAt === 'string' ? values.returnedAt.trim() : ''
99
+ try {
100
+ const result = await updateCrud(
101
+ 'sales/returns',
102
+ {
103
+ id: returnRecord.id,
104
+ orderId,
105
+ ...(organizationId ? { organizationId } : {}),
106
+ ...(tenantId ? { tenantId } : {}),
107
+ reason,
108
+ notes,
109
+ ...(returnedAt ? { returnedAt } : {}),
110
+ },
111
+ {
112
+ errorMessage: t('sales.returns.errors.update', 'Failed to update return.'),
113
+ },
114
+ )
115
+ if (result.ok) {
116
+ flash(t('sales.returns.updated', 'Return updated.'), 'success')
117
+ onClose()
118
+ await onSaved()
119
+ }
120
+ } catch (err) {
121
+ if (handleSectionMutationError(err, t, () => void onSaved())) {
122
+ onClose()
123
+ return
124
+ }
125
+ throw err
126
+ }
127
+ },
128
+ [onClose, onSaved, orderId, organizationId, returnRecord, t, tenantId],
129
+ )
130
+
131
+ const handleSubmitForm = React.useCallback(
132
+ () => dialogContentRef.current?.querySelector('form')?.requestSubmit(),
133
+ [],
134
+ )
135
+ const handleKeyDown = useDialogKeyHandler({
136
+ onConfirm: handleSubmitForm,
137
+ onCancel: onClose,
138
+ })
139
+
140
+ return (
141
+ <Dialog open={open} onOpenChange={(next) => (!next ? onClose() : undefined)}>
142
+ <DialogContent className="max-w-2xl" onKeyDown={handleKeyDown} ref={dialogContentRef}>
143
+ <DialogHeader>
144
+ <DialogTitle>{t('sales.returns.edit.title', 'Edit return')}</DialogTitle>
145
+ </DialogHeader>
146
+ <CrudForm<ReturnEditFormValues>
147
+ embedded
148
+ fields={fields}
149
+ entityId={E.sales.sales_return}
150
+ initialValues={initialValues}
151
+ submitLabel={t('sales.returns.edit.submit', 'Save changes')}
152
+ onSubmit={handleSubmit}
153
+ />
154
+ </DialogContent>
155
+ </Dialog>
156
+ )
157
+ }
@@ -5,7 +5,13 @@ import { Undo2, Plus } from 'lucide-react'
5
5
  import { Button } from '@open-mercato/ui/primitives/button'
6
6
  import { Badge } from '@open-mercato/ui/primitives/badge'
7
7
  import { ErrorMessage, LoadingMessage, TabEmptyState } from '@open-mercato/ui/backend/detail'
8
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
8
+ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
9
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
10
+ import { deleteCrud } from '@open-mercato/ui/backend/utils/crud'
11
+ import { flash } from '@open-mercato/ui/backend/FlashMessages'
12
+ import { RowActions } from '@open-mercato/ui/backend/RowActions'
13
+ import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
14
+ import { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
9
15
  import { useT } from '@open-mercato/shared/lib/i18n/context'
10
16
  import {
11
17
  emitSalesDocumentTotalsRefresh,
@@ -13,12 +19,17 @@ import {
13
19
  } from '@open-mercato/core/modules/sales/lib/frontend/documentTotalsEvents'
14
20
  import { formatMoney, normalizeNumber } from './lineItemUtils'
15
21
  import { ReturnDialog, type ReturnOrderLine } from './ReturnDialog'
22
+ import { ReturnEditDialog, type ReturnEditRecord } from './ReturnEditDialog'
23
+ import { handleSectionMutationError, readRowUpdatedAt, rowOptimisticVersion } from './optimisticLock'
16
24
 
17
25
  type ReturnRow = {
18
26
  id: string
19
27
  returnNumber: string
20
28
  status: string | null
29
+ reason: string | null
30
+ notes: string | null
21
31
  returnedAt: string | null
32
+ updatedAt: string | null
22
33
  totalNetAmount: number | null
23
34
  totalGrossAmount: number | null
24
35
  }
@@ -38,11 +49,14 @@ function formatDisplayDate(value: string | null | undefined): string | null {
38
49
 
39
50
  export function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }: SalesReturnsSectionProps) {
40
51
  const t = useT()
52
+ const { organizationId, tenantId } = useOrganizationScopeDetail()
53
+ const { confirm, ConfirmDialogElement } = useConfirmDialog()
41
54
  const [returns, setReturns] = React.useState<ReturnRow[]>([])
42
55
  const [lines, setLines] = React.useState<ReturnOrderLine[]>([])
43
56
  const [loading, setLoading] = React.useState(false)
44
57
  const [error, setError] = React.useState<string | null>(null)
45
58
  const [dialogOpen, setDialogOpen] = React.useState(false)
59
+ const [editRecord, setEditRecord] = React.useState<ReturnEditRecord | null>(null)
46
60
 
47
61
  const loadLines = React.useCallback(async () => {
48
62
  const params = new URLSearchParams({ page: '1', pageSize: '100', orderId })
@@ -142,11 +156,18 @@ export function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }
142
156
  : typeof map.totalGrossAmount === 'number'
143
157
  ? (map.totalGrossAmount as number)
144
158
  : null
159
+ const reason =
160
+ typeof map.reason === 'string' ? (map.reason as string) : null
161
+ const notes =
162
+ typeof map.notes === 'string' ? (map.notes as string) : null
145
163
  return {
146
164
  id,
147
165
  returnNumber,
148
166
  status: typeof map.status === 'string' ? (map.status as string) : null,
167
+ reason,
168
+ notes,
149
169
  returnedAt,
170
+ updatedAt: readRowUpdatedAt(map),
150
171
  totalNetAmount,
151
172
  totalGrossAmount,
152
173
  }
@@ -190,6 +211,53 @@ export function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }
190
211
  })
191
212
  }, [returns])
192
213
 
214
+ const handleEdit = React.useCallback((row: ReturnRow) => {
215
+ setEditRecord({
216
+ id: row.id,
217
+ reason: row.reason,
218
+ notes: row.notes,
219
+ returnedAt: row.returnedAt,
220
+ updatedAt: row.updatedAt,
221
+ })
222
+ }, [])
223
+
224
+ const handleDelete = React.useCallback(
225
+ async (row: ReturnRow) => {
226
+ const confirmed = await confirm({
227
+ title: t('sales.returns.confirmDelete', 'Delete this return?'),
228
+ description: t(
229
+ 'sales.returns.confirmDelete.description',
230
+ 'This reverses the returned quantities and the related credit adjustments.',
231
+ ),
232
+ variant: 'destructive',
233
+ })
234
+ if (!confirmed) return
235
+ try {
236
+ const result = await withScopedApiRequestHeaders(
237
+ buildOptimisticLockHeader(rowOptimisticVersion(row)),
238
+ () =>
239
+ deleteCrud('sales/returns', {
240
+ body: {
241
+ id: row.id,
242
+ orderId,
243
+ ...(organizationId ? { organizationId } : {}),
244
+ ...(tenantId ? { tenantId } : {}),
245
+ },
246
+ errorMessage: t('sales.returns.errors.delete', 'Failed to delete return.'),
247
+ }),
248
+ )
249
+ if (result.ok) {
250
+ emitSalesDocumentTotalsRefresh({ documentId: orderId, kind: 'order' })
251
+ await loadReturns()
252
+ }
253
+ } catch (err) {
254
+ if (handleSectionMutationError(err, t, () => void loadReturns())) return
255
+ flash(t('sales.returns.errors.delete', 'Failed to delete return.'), 'error')
256
+ }
257
+ },
258
+ [confirm, loadReturns, orderId, organizationId, tenantId, t],
259
+ )
260
+
193
261
  if (loading) return <LoadingMessage label={t('sales.returns.loading', 'Loading returns…')} />
194
262
  if (error) return <ErrorMessage label={error} />
195
263
 
@@ -230,14 +298,15 @@ export function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }
230
298
  </div>
231
299
 
232
300
  <div className="overflow-hidden rounded-md border">
233
- <div className="grid grid-cols-[1fr_auto_auto] gap-3 border-b bg-muted/30 px-4 py-2 text-xs font-medium text-muted-foreground">
301
+ <div className="grid grid-cols-[1fr_auto_auto_auto] gap-3 border-b bg-muted/30 px-4 py-2 text-xs font-medium text-muted-foreground">
234
302
  <div>{t('sales.returns.returnNumber', 'Return')}</div>
235
303
  <div className="text-right">{t('sales.returns.returnedAt', 'Returned at')}</div>
236
304
  <div className="text-right">{t('sales.returns.total', 'Total')}</div>
305
+ <div className="sr-only">{t('sales.returns.actions', 'Actions')}</div>
237
306
  </div>
238
307
  <div className="divide-y">
239
308
  {rows.map((ret) => (
240
- <div key={ret.id} className="grid grid-cols-[1fr_auto_auto] items-center gap-3 px-4 py-3">
309
+ <div key={ret.id} className="grid grid-cols-[1fr_auto_auto_auto] items-center gap-3 px-4 py-3">
241
310
  <div className="min-w-0">
242
311
  <div className="flex items-center gap-2">
243
312
  <Undo2 className="h-4 w-4 text-muted-foreground" aria-hidden />
@@ -251,6 +320,23 @@ export function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }
251
320
  <div className="whitespace-nowrap text-right text-sm font-medium">
252
321
  {formatMoney(ret.total, currencyCode ?? null)}
253
322
  </div>
323
+ <div className="flex justify-end">
324
+ <RowActions
325
+ items={[
326
+ {
327
+ id: 'edit',
328
+ label: t('ui.actions.edit', 'Edit'),
329
+ onSelect: () => handleEdit(ret),
330
+ },
331
+ {
332
+ id: 'delete',
333
+ label: t('ui.actions.delete', 'Delete'),
334
+ destructive: true,
335
+ onSelect: () => void handleDelete(ret),
336
+ },
337
+ ]}
338
+ />
339
+ </div>
254
340
  </div>
255
341
  ))}
256
342
  </div>
@@ -267,6 +353,22 @@ export function SalesReturnsSection({ orderId, currencyCode, documentUpdatedAt }
267
353
  await loadReturns()
268
354
  }}
269
355
  />
356
+
357
+ <ReturnEditDialog
358
+ open={editRecord !== null}
359
+ returnRecord={editRecord}
360
+ orderId={orderId}
361
+ organizationId={organizationId ?? null}
362
+ tenantId={tenantId ?? null}
363
+ onClose={() => setEditRecord(null)}
364
+ onSaved={async () => {
365
+ setEditRecord(null)
366
+ emitSalesDocumentTotalsRefresh({ documentId: orderId, kind: 'order' })
367
+ await loadReturns()
368
+ }}
369
+ />
370
+
371
+ {ConfirmDialogElement}
270
372
  </div>
271
373
  )
272
374
  }
@@ -818,6 +818,19 @@ export const returnCreateSchema = scoped.extend({
818
818
  .min(1),
819
819
  })
820
820
 
821
+ export const returnUpdateSchema = scoped.extend({
822
+ id: uuid(),
823
+ orderId: uuid(),
824
+ reason: z.string().trim().max(4000).optional(),
825
+ notes: z.string().trim().max(4000).optional(),
826
+ returnedAt: z.coerce.date().optional(),
827
+ })
828
+
829
+ export const returnDeleteSchema = scoped.extend({
830
+ id: uuid(),
831
+ orderId: uuid(),
832
+ })
833
+
821
834
  export const invoiceCreateSchema = scoped.extend({
822
835
  orderId: uuid().optional(),
823
836
  invoiceNumber: z.string().trim().min(1).max(191).optional(),
@@ -1021,6 +1034,8 @@ export type QuoteAdjustmentUpdateInput = z.infer<typeof quoteAdjustmentUpdateSch
1021
1034
  export type ShipmentCreateInput = z.infer<typeof shipmentCreateSchema>
1022
1035
  export type ShipmentUpdateInput = z.infer<typeof shipmentUpdateSchema>
1023
1036
  export type ReturnCreateInput = z.infer<typeof returnCreateSchema>
1037
+ export type ReturnUpdateInput = z.infer<typeof returnUpdateSchema>
1038
+ export type ReturnDeleteInput = z.infer<typeof returnDeleteSchema>
1024
1039
  export type InvoiceCreateInput = z.infer<typeof invoiceCreateSchema>
1025
1040
  export type InvoiceUpdateInput = z.infer<typeof invoiceUpdateSchema>
1026
1041
  export type CreditMemoCreateInput = z.infer<typeof creditMemoCreateSchema>
@@ -46,6 +46,8 @@
46
46
  "sales.audit.quotes.lines.upsert": "Angebotsposition aktualisieren",
47
47
  "sales.audit.quotes.update": "Angebot aktualisieren",
48
48
  "sales.audit.returns.create": "Rückgabe erstellen",
49
+ "sales.audit.returns.delete": "Rückgabe löschen",
50
+ "sales.audit.returns.update": "Rückgabe aktualisieren",
49
51
  "sales.audit.shipments.create": "Sendung erstellen",
50
52
  "sales.audit.shipments.delete": "Sendung löschen",
51
53
  "sales.audit.shipments.update": "Sendung aktualisieren",
@@ -1289,21 +1291,28 @@
1289
1291
  "sales.quotes.send.title": "Angebot an Kunden senden",
1290
1292
  "sales.quotes.send.validForDays": "Gültig für (Tage)",
1291
1293
  "sales.quotes.update": "Angebot aktualisieren",
1294
+ "sales.returns.actions": "Aktionen",
1292
1295
  "sales.returns.available": "Verfügbar",
1296
+ "sales.returns.confirmDelete": "Diese Rückgabe löschen?",
1297
+ "sales.returns.confirmDelete.description": "Dadurch werden die zurückgegebenen Mengen und die zugehörigen Gutschrift-Anpassungen rückgängig gemacht.",
1293
1298
  "sales.returns.create": "Rückgabe erstellen",
1294
1299
  "sales.returns.create.lines": "Positionen",
1295
1300
  "sales.returns.create.submit": "Rückgabe erstellen",
1296
1301
  "sales.returns.create.title": "Rückgabe erstellen",
1297
1302
  "sales.returns.created": "Rückgabe erstellt.",
1303
+ "sales.returns.edit.submit": "Änderungen speichern",
1304
+ "sales.returns.edit.title": "Rückgabe bearbeiten",
1298
1305
  "sales.returns.empty.available": "Keine Artikel zur Rückgabe verfügbar.",
1299
1306
  "sales.returns.empty.description": "Erstelle eine Rückgabe, um Gutschriften für zurückgesendete Artikel zu erzeugen.",
1300
1307
  "sales.returns.empty.title": "Noch keine Rückgaben.",
1301
1308
  "sales.returns.error": "Rückgabe konnte nicht geladen werden.",
1302
1309
  "sales.returns.errors.create": "Rückgabe konnte nicht erstellt werden.",
1310
+ "sales.returns.errors.delete": "Rückgabe konnte nicht gelöscht werden.",
1303
1311
  "sales.returns.errors.linesRequired": "Wähle mindestens eine Position zur Rückgabe aus.",
1304
1312
  "sales.returns.errors.load": "Rückgaben konnten nicht geladen werden.",
1305
1313
  "sales.returns.errors.quantityExceeded": "Es kann nicht mehr als die verfügbare Menge zurückgegeben werden.",
1306
1314
  "sales.returns.errors.quantityNotInteger": "Die Rückgabemenge muss eine ganze Zahl sein.",
1315
+ "sales.returns.errors.update": "Rückgabe konnte nicht aktualisiert werden.",
1307
1316
  "sales.returns.lineMissing": "Bestellposition nicht gefunden.",
1308
1317
  "sales.returns.linesRequired": "Wähle mindestens eine Position zur Rückgabe aus.",
1309
1318
  "sales.returns.loading": "Rückgaben werden geladen...",
@@ -1311,6 +1320,7 @@
1311
1320
  "sales.returns.notSet": "Nicht festgelegt",
1312
1321
  "sales.returns.notes": "Notizen",
1313
1322
  "sales.returns.notes.placeholder": "Optional",
1323
+ "sales.returns.orderMismatch": "Die Rückgabe gehört nicht zu dieser Bestellung.",
1314
1324
  "sales.returns.orderMissing": "Bestellung nicht gefunden.",
1315
1325
  "sales.returns.qty.decrease": "Menge verringern",
1316
1326
  "sales.returns.qty.increase": "Menge erhöhen",
@@ -1321,6 +1331,7 @@
1321
1331
  "sales.returns.returnNumber": "Rückgabe",
1322
1332
  "sales.returns.returnedAt": "Rückgabedatum",
1323
1333
  "sales.returns.total": "Summe",
1334
+ "sales.returns.updated": "Rückgabe aktualisiert.",
1324
1335
  "sales.search.badge.address": "Adresse",
1325
1336
  "sales.search.badge.channel": "Kanal",
1326
1337
  "sales.search.badge.creditMemo": "Gutschrift",
@@ -46,6 +46,8 @@
46
46
  "sales.audit.quotes.lines.upsert": "Update quote line",
47
47
  "sales.audit.quotes.update": "Update sales quote",
48
48
  "sales.audit.returns.create": "Create return",
49
+ "sales.audit.returns.delete": "Delete return",
50
+ "sales.audit.returns.update": "Update return",
49
51
  "sales.audit.shipments.create": "Create shipment",
50
52
  "sales.audit.shipments.delete": "Delete shipment",
51
53
  "sales.audit.shipments.update": "Update shipment",
@@ -1289,21 +1291,28 @@
1289
1291
  "sales.quotes.send.title": "Send quote to customer",
1290
1292
  "sales.quotes.send.validForDays": "Valid for (days)",
1291
1293
  "sales.quotes.update": "Update sales quote",
1294
+ "sales.returns.actions": "Actions",
1292
1295
  "sales.returns.available": "Available",
1296
+ "sales.returns.confirmDelete": "Delete this return?",
1297
+ "sales.returns.confirmDelete.description": "This reverses the returned quantities and the related credit adjustments.",
1293
1298
  "sales.returns.create": "Create return",
1294
1299
  "sales.returns.create.lines": "Lines",
1295
1300
  "sales.returns.create.submit": "Create return",
1296
1301
  "sales.returns.create.title": "Create return",
1297
1302
  "sales.returns.created": "Return created.",
1303
+ "sales.returns.edit.submit": "Save changes",
1304
+ "sales.returns.edit.title": "Edit return",
1298
1305
  "sales.returns.empty.available": "No items available to return.",
1299
1306
  "sales.returns.empty.description": "Create a return to generate credit adjustments for returned items.",
1300
1307
  "sales.returns.empty.title": "No returns yet.",
1301
1308
  "sales.returns.error": "Failed to load return.",
1302
1309
  "sales.returns.errors.create": "Failed to create return.",
1310
+ "sales.returns.errors.delete": "Failed to delete return.",
1303
1311
  "sales.returns.errors.linesRequired": "Select at least one line to return.",
1304
1312
  "sales.returns.errors.load": "Failed to load returns.",
1305
1313
  "sales.returns.errors.quantityExceeded": "Cannot return more than available quantity.",
1306
1314
  "sales.returns.errors.quantityNotInteger": "Return quantity must be a whole number.",
1315
+ "sales.returns.errors.update": "Failed to update return.",
1307
1316
  "sales.returns.lineMissing": "Order line not found.",
1308
1317
  "sales.returns.linesRequired": "Select at least one line to return.",
1309
1318
  "sales.returns.loading": "Loading returns...",
@@ -1311,6 +1320,7 @@
1311
1320
  "sales.returns.notSet": "Not set",
1312
1321
  "sales.returns.notes": "Notes",
1313
1322
  "sales.returns.notes.placeholder": "Optional",
1323
+ "sales.returns.orderMismatch": "Return does not belong to this order.",
1314
1324
  "sales.returns.orderMissing": "Order not found.",
1315
1325
  "sales.returns.qty.decrease": "Decrease quantity",
1316
1326
  "sales.returns.qty.increase": "Increase quantity",
@@ -1321,6 +1331,7 @@
1321
1331
  "sales.returns.returnNumber": "Return",
1322
1332
  "sales.returns.returnedAt": "Returned at",
1323
1333
  "sales.returns.total": "Total",
1334
+ "sales.returns.updated": "Return updated.",
1324
1335
  "sales.search.badge.address": "Address",
1325
1336
  "sales.search.badge.channel": "Channel",
1326
1337
  "sales.search.badge.creditMemo": "Credit memo",
@@ -46,6 +46,8 @@
46
46
  "sales.audit.quotes.lines.upsert": "Actualizar línea de cotización",
47
47
  "sales.audit.quotes.update": "Actualizar cotización",
48
48
  "sales.audit.returns.create": "Crear devolución",
49
+ "sales.audit.returns.delete": "Eliminar devolución",
50
+ "sales.audit.returns.update": "Actualizar devolución",
49
51
  "sales.audit.shipments.create": "Crear envío",
50
52
  "sales.audit.shipments.delete": "Eliminar envío",
51
53
  "sales.audit.shipments.update": "Actualizar envío",
@@ -1289,21 +1291,28 @@
1289
1291
  "sales.quotes.send.title": "Enviar cotización al cliente",
1290
1292
  "sales.quotes.send.validForDays": "Válida por (días)",
1291
1293
  "sales.quotes.update": "Actualizar cotización",
1294
+ "sales.returns.actions": "Acciones",
1292
1295
  "sales.returns.available": "Disponible",
1296
+ "sales.returns.confirmDelete": "¿Eliminar esta devolución?",
1297
+ "sales.returns.confirmDelete.description": "Esto revierte las cantidades devueltas y los ajustes de crédito relacionados.",
1293
1298
  "sales.returns.create": "Crear devolución",
1294
1299
  "sales.returns.create.lines": "Líneas",
1295
1300
  "sales.returns.create.submit": "Crear devolución",
1296
1301
  "sales.returns.create.title": "Crear devolución",
1297
1302
  "sales.returns.created": "Devolución creada.",
1303
+ "sales.returns.edit.submit": "Guardar cambios",
1304
+ "sales.returns.edit.title": "Editar devolución",
1298
1305
  "sales.returns.empty.available": "No hay artículos disponibles para devolver.",
1299
1306
  "sales.returns.empty.description": "Crea una devolución para generar ajustes de crédito por artículos devueltos.",
1300
1307
  "sales.returns.empty.title": "Aún no hay devoluciones.",
1301
1308
  "sales.returns.error": "No se pudo cargar la devolución.",
1302
1309
  "sales.returns.errors.create": "No se pudo crear la devolución.",
1310
+ "sales.returns.errors.delete": "No se pudo eliminar la devolución.",
1303
1311
  "sales.returns.errors.linesRequired": "Selecciona al menos una línea para devolver.",
1304
1312
  "sales.returns.errors.load": "No se pudieron cargar las devoluciones.",
1305
1313
  "sales.returns.errors.quantityExceeded": "No se puede devolver más que la cantidad disponible.",
1306
1314
  "sales.returns.errors.quantityNotInteger": "La cantidad de devolución debe ser un número entero.",
1315
+ "sales.returns.errors.update": "No se pudo actualizar la devolución.",
1307
1316
  "sales.returns.lineMissing": "No se encontró la línea del pedido.",
1308
1317
  "sales.returns.linesRequired": "Selecciona al menos una línea para devolver.",
1309
1318
  "sales.returns.loading": "Cargando devoluciones...",
@@ -1311,6 +1320,7 @@
1311
1320
  "sales.returns.notSet": "No establecido",
1312
1321
  "sales.returns.notes": "Notas",
1313
1322
  "sales.returns.notes.placeholder": "Opcional",
1323
+ "sales.returns.orderMismatch": "La devolución no pertenece a este pedido.",
1314
1324
  "sales.returns.orderMissing": "Pedido no encontrado.",
1315
1325
  "sales.returns.qty.decrease": "Disminuir cantidad",
1316
1326
  "sales.returns.qty.increase": "Aumentar cantidad",
@@ -1321,6 +1331,7 @@
1321
1331
  "sales.returns.returnNumber": "Devolución",
1322
1332
  "sales.returns.returnedAt": "Devuelto el",
1323
1333
  "sales.returns.total": "Total",
1334
+ "sales.returns.updated": "Devolución actualizada.",
1324
1335
  "sales.search.badge.address": "Dirección",
1325
1336
  "sales.search.badge.channel": "Canal",
1326
1337
  "sales.search.badge.creditMemo": "Nota de crédito",
@@ -46,6 +46,8 @@
46
46
  "sales.audit.quotes.lines.upsert": "Zaktualizuj pozycję oferty",
47
47
  "sales.audit.quotes.update": "Aktualizuj ofertę",
48
48
  "sales.audit.returns.create": "Utwórz zwrot",
49
+ "sales.audit.returns.delete": "Usuń zwrot",
50
+ "sales.audit.returns.update": "Zaktualizuj zwrot",
49
51
  "sales.audit.shipments.create": "Utwórz wysyłkę",
50
52
  "sales.audit.shipments.delete": "Usuń wysyłkę",
51
53
  "sales.audit.shipments.update": "Aktualizuj wysyłkę",
@@ -1289,21 +1291,28 @@
1289
1291
  "sales.quotes.send.title": "Wyślij ofertę do klienta",
1290
1292
  "sales.quotes.send.validForDays": "Ważne przez (dni)",
1291
1293
  "sales.quotes.update": "Aktualizuj ofertę",
1294
+ "sales.returns.actions": "Akcje",
1292
1295
  "sales.returns.available": "Dostępne",
1296
+ "sales.returns.confirmDelete": "Usunąć ten zwrot?",
1297
+ "sales.returns.confirmDelete.description": "Spowoduje to cofnięcie zwróconych ilości oraz powiązanych korekt kredytowych.",
1293
1298
  "sales.returns.create": "Utwórz zwrot",
1294
1299
  "sales.returns.create.lines": "Pozycje",
1295
1300
  "sales.returns.create.submit": "Utwórz zwrot",
1296
1301
  "sales.returns.create.title": "Utwórz zwrot",
1297
1302
  "sales.returns.created": "Zwrot utworzony.",
1303
+ "sales.returns.edit.submit": "Zapisz zmiany",
1304
+ "sales.returns.edit.title": "Edytuj zwrot",
1298
1305
  "sales.returns.empty.available": "Brak pozycji dostępnych do zwrotu.",
1299
1306
  "sales.returns.empty.description": "Utwórz zwrot, aby wygenerować korekty kredytowe dla zwróconych pozycji.",
1300
1307
  "sales.returns.empty.title": "Brak zwrotów.",
1301
1308
  "sales.returns.error": "Nie udało się załadować zwrotu.",
1302
1309
  "sales.returns.errors.create": "Nie udało się utworzyć zwrotu.",
1310
+ "sales.returns.errors.delete": "Nie udało się usunąć zwrotu.",
1303
1311
  "sales.returns.errors.linesRequired": "Wybierz co najmniej jedną pozycję do zwrotu.",
1304
1312
  "sales.returns.errors.load": "Nie udało się załadować zwrotów.",
1305
1313
  "sales.returns.errors.quantityExceeded": "Nie można zwrócić więcej niż dostępna ilość.",
1306
1314
  "sales.returns.errors.quantityNotInteger": "Ilość zwrotu musi być liczbą całkowitą.",
1315
+ "sales.returns.errors.update": "Nie udało się zaktualizować zwrotu.",
1307
1316
  "sales.returns.lineMissing": "Nie znaleziono pozycji zamówienia.",
1308
1317
  "sales.returns.linesRequired": "Wybierz co najmniej jedną pozycję do zwrotu.",
1309
1318
  "sales.returns.loading": "Ładowanie zwrotów...",
@@ -1311,6 +1320,7 @@
1311
1320
  "sales.returns.notSet": "Nie ustawiono",
1312
1321
  "sales.returns.notes": "Notatki",
1313
1322
  "sales.returns.notes.placeholder": "Opcjonalne",
1323
+ "sales.returns.orderMismatch": "Zwrot nie należy do tego zamówienia.",
1314
1324
  "sales.returns.orderMissing": "Nie znaleziono zamówienia.",
1315
1325
  "sales.returns.qty.decrease": "Zmniejsz ilość",
1316
1326
  "sales.returns.qty.increase": "Zwiększ ilość",
@@ -1321,6 +1331,7 @@
1321
1331
  "sales.returns.returnNumber": "Zwrot",
1322
1332
  "sales.returns.returnedAt": "Data zwrotu",
1323
1333
  "sales.returns.total": "Suma",
1334
+ "sales.returns.updated": "Zwrot zaktualizowany.",
1324
1335
  "sales.search.badge.address": "Adres",
1325
1336
  "sales.search.badge.channel": "Kanał",
1326
1337
  "sales.search.badge.creditMemo": "Nota kredytowa",
@@ -63,6 +63,7 @@ export const setup: ModuleSetupConfig = {
63
63
  'sales.payments.manage',
64
64
  'sales.returns.view',
65
65
  'sales.returns.create',
66
+ 'sales.returns.manage',
66
67
  'sales.invoices.manage',
67
68
  'sales.credit_memos.manage',
68
69
  ],