@open-mercato/core 0.6.6-develop.6423.1.f039f73ff4 → 0.6.6-develop.6429.1.2fba7258d1
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/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +4 -4
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection-table.js +20 -18
- package/dist/modules/customer_accounts/widgets/injection-table.js.map +2 -2
- package/dist/modules/sales/commands/returns.js +13 -7
- package/dist/modules/sales/commands/returns.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/customer_accounts/AGENTS.md +4 -0
- package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +5 -6
- package/src/modules/customer_accounts/widgets/injection-table.ts +22 -18
- package/src/modules/sales/commands/returns.ts +20 -4
|
@@ -80,8 +80,7 @@ function InviteForm({ personEntityId, onSuccess }) {
|
|
|
80
80
|
(prev) => prev.includes(roleId) ? prev.filter((id) => id !== roleId) : [...prev, roleId]
|
|
81
81
|
);
|
|
82
82
|
}
|
|
83
|
-
async function handleSubmit(
|
|
84
|
-
event.preventDefault();
|
|
83
|
+
async function handleSubmit() {
|
|
85
84
|
const trimmedEmail = email.trim();
|
|
86
85
|
if (!trimmedEmail) {
|
|
87
86
|
flash(t("customer_accounts.widgets.invite.error.emailRequired", "Email is required"), "error");
|
|
@@ -129,7 +128,7 @@ function InviteForm({ personEntityId, onSuccess }) {
|
|
|
129
128
|
if (isLoading) {
|
|
130
129
|
return /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground py-2", children: t("common.loading", "Loading...") });
|
|
131
130
|
}
|
|
132
|
-
return /* @__PURE__ */ jsxs("
|
|
131
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3 mt-2", children: [
|
|
133
132
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
134
133
|
/* @__PURE__ */ jsx("label", { htmlFor: "invite-email", className: "block text-xs font-medium text-muted-foreground mb-1", children: t("common.email", "Email") }),
|
|
135
134
|
/* @__PURE__ */ jsx(
|
|
@@ -177,8 +176,9 @@ function InviteForm({ personEntityId, onSuccess }) {
|
|
|
177
176
|
/* @__PURE__ */ jsx("div", { className: "flex justify-end gap-2 pt-1", children: /* @__PURE__ */ jsx(
|
|
178
177
|
Button,
|
|
179
178
|
{
|
|
180
|
-
type: "
|
|
179
|
+
type: "button",
|
|
181
180
|
size: "sm",
|
|
181
|
+
onClick: handleSubmit,
|
|
182
182
|
disabled: isSubmitting || !email.trim() || selectedRoleIds.length === 0,
|
|
183
183
|
children: isSubmitting ? t("common.loading", "Loading...") : t("customer_accounts.widgets.invite.submit", "Send Invitation")
|
|
184
184
|
}
|
package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport React from 'react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { EmailInput } from '@open-mercato/ui/primitives/email-input'\nimport { StatusBadge } from '@open-mercato/ui/primitives/status-badge'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\n\ninterface AccountStatusData {\n id: string\n email: string\n isActive: boolean\n emailVerified: boolean\n lastLoginAt: string | null\n}\n\ninterface AccountStatusProps {\n context?: {\n entityId?: string\n recordId?: string\n }\n}\n\ninterface RoleOption {\n id: string\n name: string\n}\n\ninterface PersonData {\n person?: {\n primaryEmail?: string | null\n displayName?: string | null\n }\n profile?: {\n firstName?: string | null\n lastName?: string | null\n } | null\n}\n\nfunction InviteForm({ personEntityId, onSuccess }: { personEntityId: string; onSuccess: () => void }) {\n const t = useT()\n const [isLoadingPerson, setIsLoadingPerson] = React.useState(true)\n const [email, setEmail] = React.useState('')\n const [displayName, setDisplayName] = React.useState('')\n const [selectedRoleIds, setSelectedRoleIds] = React.useState<string[]>([])\n const [availableRoles, setAvailableRoles] = React.useState<RoleOption[]>([])\n const [isLoadingRoles, setIsLoadingRoles] = React.useState(true)\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n\n const { runMutation } = useGuardedMutation<{ entityType: string }>({\n contextId: 'customer_accounts:account-status-invite',\n })\n\n React.useEffect(() => {\n let cancelled = false\n async function loadPerson() {\n try {\n const call = await apiCall<PersonData>(\n `/api/customers/people/${encodeURIComponent(personEntityId)}`,\n )\n if (cancelled) return\n if (call.ok && call.result) {\n const person = call.result.person\n const profile = call.result.profile\n if (person?.primaryEmail) {\n setEmail(person.primaryEmail)\n }\n const nameParts = [profile?.firstName, profile?.lastName].filter(Boolean)\n if (nameParts.length > 0) {\n setDisplayName(nameParts.join(' '))\n } else if (person?.displayName) {\n setDisplayName(person.displayName)\n }\n }\n } catch {\n /* ignore - fields will remain empty for manual entry */\n } finally {\n if (!cancelled) setIsLoadingPerson(false)\n }\n }\n loadPerson()\n return () => { cancelled = true }\n }, [personEntityId])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadRoles() {\n try {\n const call = await apiCall<{ items?: RoleOption[] }>(\n '/api/customer_accounts/admin/roles?pageSize=100',\n )\n if (cancelled) return\n if (call.ok && call.result) {\n const items = Array.isArray(call.result.items) ? call.result.items : []\n setAvailableRoles(items.map((role) => ({ id: role.id, name: role.name })))\n }\n } catch {\n /* ignore */\n } finally {\n if (!cancelled) setIsLoadingRoles(false)\n }\n }\n loadRoles()\n return () => { cancelled = true }\n }, [])\n\n function toggleRole(roleId: string) {\n setSelectedRoleIds((prev) =>\n prev.includes(roleId) ? prev.filter((id) => id !== roleId) : [...prev, roleId],\n )\n }\n\n async function handleSubmit(event: React.FormEvent) {\n event.preventDefault()\n\n const trimmedEmail = email.trim()\n if (!trimmedEmail) {\n flash(t('customer_accounts.widgets.invite.error.emailRequired', 'Email is required'), 'error')\n return\n }\n if (selectedRoleIds.length === 0) {\n flash(t('customer_accounts.widgets.invite.error.roleRequired', 'At least one role must be selected'), 'error')\n return\n }\n\n setIsSubmitting(true)\n try {\n await runMutation({\n context: { entityType: 'customer_accounts:user' },\n mutationPayload: { customerEntityId: personEntityId, roleIds: selectedRoleIds },\n operation: async () => {\n // optimistic-lock-exempt: creates a new portal invitation, not a concurrent record edit\n const call = await apiCall<{ ok: boolean; error?: string }>(\n '/api/customer_accounts/admin/users-invite',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n email: trimmedEmail,\n roleIds: selectedRoleIds,\n displayName: displayName.trim() || undefined,\n customerEntityId: personEntityId,\n }),\n },\n )\n\n if (!call.ok) {\n const errorMessage = (call.result as Record<string, unknown> | null)?.error as string | undefined\n flash(errorMessage || t('customer_accounts.widgets.invite.error.failed', 'Failed to send invitation'), 'error')\n return\n }\n\n flash(t('customer_accounts.widgets.invite.success', 'Invitation sent successfully'), 'success')\n onSuccess()\n },\n })\n } catch {\n flash(t('customer_accounts.widgets.invite.error.failed', 'Failed to send invitation'), 'error')\n } finally {\n setIsSubmitting(false)\n }\n }\n\n const isLoading = isLoadingPerson || isLoadingRoles\n\n if (isLoading) {\n return (\n <div className=\"text-sm text-muted-foreground py-2\">\n {t('common.loading', 'Loading...')}\n </div>\n )\n }\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-3 mt-2\">\n <div>\n <label htmlFor=\"invite-email\" className=\"block text-xs font-medium text-muted-foreground mb-1\">\n {t('common.email', 'Email')}\n </label>\n <EmailInput\n id=\"invite-email\"\n size=\"sm\"\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n required\n disabled={isSubmitting}\n />\n </div>\n\n <div>\n <label htmlFor=\"invite-display-name\" className=\"block text-xs font-medium text-muted-foreground mb-1\">\n {t('customer_accounts.widgets.invite.displayName', 'Display Name')}\n </label>\n <Input\n id=\"invite-display-name\"\n type=\"text\"\n size=\"sm\"\n value={displayName}\n onChange={(event) => setDisplayName(event.target.value)}\n disabled={isSubmitting}\n />\n </div>\n\n <div>\n <div className=\"text-xs font-medium text-muted-foreground mb-1.5\">\n {t('customer_accounts.widgets.invite.roles', 'Roles')}\n </div>\n {availableRoles.length === 0 ? (\n <div className=\"text-xs text-muted-foreground\">\n {t('customer_accounts.widgets.invite.noRoles', 'No roles available')}\n </div>\n ) : (\n <div className=\"flex flex-wrap gap-1.5\">\n {availableRoles.map((role) => (\n <Button\n key={role.id}\n type=\"button\"\n variant={selectedRoleIds.includes(role.id) ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => toggleRole(role.id)}\n disabled={isSubmitting}\n className=\"text-xs h-7\"\n >\n {role.name}\n </Button>\n ))}\n </div>\n )}\n </div>\n\n <div className=\"flex justify-end gap-2 pt-1\">\n <Button\n type=\"submit\"\n size=\"sm\"\n disabled={isSubmitting || !email.trim() || selectedRoleIds.length === 0}\n >\n {isSubmitting\n ? t('common.loading', 'Loading...')\n : t('customer_accounts.widgets.invite.submit', 'Send Invitation')}\n </Button>\n </div>\n </form>\n )\n}\n\nexport default function AccountStatusWidget({ context }: AccountStatusProps) {\n const t = useT()\n const queryClient = useQueryClient()\n const personEntityId = context?.recordId\n const [showInviteForm, setShowInviteForm] = React.useState(false)\n\n const { data, isLoading } = useQuery({\n queryKey: ['customer-account-status', personEntityId],\n queryFn: async (): Promise<AccountStatusData | null> => {\n if (!personEntityId) return null\n const result = await apiCall(`/api/customer_accounts/admin/users?personEntityId=${personEntityId}&pageSize=1`)\n if (!result.ok) return null\n const json = result.result as Record<string, unknown> | null\n const items = json?.items as AccountStatusData[] | undefined\n return items?.[0] || null\n },\n enabled: !!personEntityId,\n })\n\n function handleInviteSuccess() {\n setShowInviteForm(false)\n queryClient.invalidateQueries({ queryKey: ['customer-account-status', personEntityId] })\n }\n\n if (isLoading) {\n return <div className=\"text-sm text-muted-foreground\">{t('common.loading', 'Loading...')}</div>\n }\n\n if (!data) {\n return (\n <div className=\"rounded-md border p-3\">\n <div className=\"text-sm font-medium mb-1\">{t('customer_accounts.widgets.accountStatus', 'Portal Account')}</div>\n <div className=\"text-sm text-muted-foreground\">{t('customer_accounts.widgets.noAccount', 'No portal account linked')}</div>\n {!showInviteForm && personEntityId && (\n <div className=\"mt-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setShowInviteForm(true)}\n >\n {t('customer_accounts.widgets.invite.button', 'Invite to Portal')}\n </Button>\n </div>\n )}\n {showInviteForm && personEntityId && (\n <InviteForm personEntityId={personEntityId} onSuccess={handleInviteSuccess} />\n )}\n </div>\n )\n }\n\n return (\n <div className=\"rounded-md border p-3\">\n <div className=\"text-sm font-medium mb-2\">{t('customer_accounts.widgets.accountStatus', 'Portal Account')}</div>\n <div className=\"space-y-1 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('common.status', 'Status')}</span>\n <StatusBadge variant={data.isActive ? 'success' : 'error'} dot>\n {data.isActive ? t('common.active', 'Active') : t('common.inactive', 'Inactive')}\n </StatusBadge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('common.email', 'Email')}</span>\n <span>{data.email}</span>\n </div>\n {data.emailVerified !== undefined && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('customer_accounts.widgets.emailVerified', 'Email Verified')}</span>\n <span>{data.emailVerified ? '\u2713' : '\u2717'}</span>\n </div>\n )}\n {data.lastLoginAt && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('customer_accounts.widgets.lastLogin', 'Last Login')}</span>\n <span>{new Date(data.lastLoginAt).toLocaleDateString()}</span>\n </div>\n )}\n </div>\n <div className=\"mt-2\">\n <a\n href={`/backend/customer_accounts/users/${data.id}`}\n className=\"text-xs text-primary hover:underline\"\n >\n {t('customer_accounts.widgets.viewAccount', 'View account details \u2192')}\n </a>\n </div>\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport React from 'react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { EmailInput } from '@open-mercato/ui/primitives/email-input'\nimport { StatusBadge } from '@open-mercato/ui/primitives/status-badge'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\n\ninterface AccountStatusData {\n id: string\n email: string\n isActive: boolean\n emailVerified: boolean\n lastLoginAt: string | null\n}\n\ninterface AccountStatusProps {\n context?: {\n entityId?: string\n recordId?: string\n }\n}\n\ninterface RoleOption {\n id: string\n name: string\n}\n\ninterface PersonData {\n person?: {\n primaryEmail?: string | null\n displayName?: string | null\n }\n profile?: {\n firstName?: string | null\n lastName?: string | null\n } | null\n}\n\nfunction InviteForm({ personEntityId, onSuccess }: { personEntityId: string; onSuccess: () => void }) {\n const t = useT()\n const [isLoadingPerson, setIsLoadingPerson] = React.useState(true)\n const [email, setEmail] = React.useState('')\n const [displayName, setDisplayName] = React.useState('')\n const [selectedRoleIds, setSelectedRoleIds] = React.useState<string[]>([])\n const [availableRoles, setAvailableRoles] = React.useState<RoleOption[]>([])\n const [isLoadingRoles, setIsLoadingRoles] = React.useState(true)\n const [isSubmitting, setIsSubmitting] = React.useState(false)\n\n const { runMutation } = useGuardedMutation<{ entityType: string }>({\n contextId: 'customer_accounts:account-status-invite',\n })\n\n React.useEffect(() => {\n let cancelled = false\n async function loadPerson() {\n try {\n const call = await apiCall<PersonData>(\n `/api/customers/people/${encodeURIComponent(personEntityId)}`,\n )\n if (cancelled) return\n if (call.ok && call.result) {\n const person = call.result.person\n const profile = call.result.profile\n if (person?.primaryEmail) {\n setEmail(person.primaryEmail)\n }\n const nameParts = [profile?.firstName, profile?.lastName].filter(Boolean)\n if (nameParts.length > 0) {\n setDisplayName(nameParts.join(' '))\n } else if (person?.displayName) {\n setDisplayName(person.displayName)\n }\n }\n } catch {\n /* ignore - fields will remain empty for manual entry */\n } finally {\n if (!cancelled) setIsLoadingPerson(false)\n }\n }\n loadPerson()\n return () => { cancelled = true }\n }, [personEntityId])\n\n React.useEffect(() => {\n let cancelled = false\n async function loadRoles() {\n try {\n const call = await apiCall<{ items?: RoleOption[] }>(\n '/api/customer_accounts/admin/roles?pageSize=100',\n )\n if (cancelled) return\n if (call.ok && call.result) {\n const items = Array.isArray(call.result.items) ? call.result.items : []\n setAvailableRoles(items.map((role) => ({ id: role.id, name: role.name })))\n }\n } catch {\n /* ignore */\n } finally {\n if (!cancelled) setIsLoadingRoles(false)\n }\n }\n loadRoles()\n return () => { cancelled = true }\n }, [])\n\n function toggleRole(roleId: string) {\n setSelectedRoleIds((prev) =>\n prev.includes(roleId) ? prev.filter((id) => id !== roleId) : [...prev, roleId],\n )\n }\n\n async function handleSubmit() {\n const trimmedEmail = email.trim()\n if (!trimmedEmail) {\n flash(t('customer_accounts.widgets.invite.error.emailRequired', 'Email is required'), 'error')\n return\n }\n if (selectedRoleIds.length === 0) {\n flash(t('customer_accounts.widgets.invite.error.roleRequired', 'At least one role must be selected'), 'error')\n return\n }\n\n setIsSubmitting(true)\n try {\n await runMutation({\n context: { entityType: 'customer_accounts:user' },\n mutationPayload: { customerEntityId: personEntityId, roleIds: selectedRoleIds },\n operation: async () => {\n // optimistic-lock-exempt: creates a new portal invitation, not a concurrent record edit\n const call = await apiCall<{ ok: boolean; error?: string }>(\n '/api/customer_accounts/admin/users-invite',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n email: trimmedEmail,\n roleIds: selectedRoleIds,\n displayName: displayName.trim() || undefined,\n customerEntityId: personEntityId,\n }),\n },\n )\n\n if (!call.ok) {\n const errorMessage = (call.result as Record<string, unknown> | null)?.error as string | undefined\n flash(errorMessage || t('customer_accounts.widgets.invite.error.failed', 'Failed to send invitation'), 'error')\n return\n }\n\n flash(t('customer_accounts.widgets.invite.success', 'Invitation sent successfully'), 'success')\n onSuccess()\n },\n })\n } catch {\n flash(t('customer_accounts.widgets.invite.error.failed', 'Failed to send invitation'), 'error')\n } finally {\n setIsSubmitting(false)\n }\n }\n\n const isLoading = isLoadingPerson || isLoadingRoles\n\n if (isLoading) {\n return (\n <div className=\"text-sm text-muted-foreground py-2\">\n {t('common.loading', 'Loading...')}\n </div>\n )\n }\n\n return (\n <div className=\"space-y-3 mt-2\">\n <div>\n <label htmlFor=\"invite-email\" className=\"block text-xs font-medium text-muted-foreground mb-1\">\n {t('common.email', 'Email')}\n </label>\n <EmailInput\n id=\"invite-email\"\n size=\"sm\"\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n required\n disabled={isSubmitting}\n />\n </div>\n\n <div>\n <label htmlFor=\"invite-display-name\" className=\"block text-xs font-medium text-muted-foreground mb-1\">\n {t('customer_accounts.widgets.invite.displayName', 'Display Name')}\n </label>\n <Input\n id=\"invite-display-name\"\n type=\"text\"\n size=\"sm\"\n value={displayName}\n onChange={(event) => setDisplayName(event.target.value)}\n disabled={isSubmitting}\n />\n </div>\n\n <div>\n <div className=\"text-xs font-medium text-muted-foreground mb-1.5\">\n {t('customer_accounts.widgets.invite.roles', 'Roles')}\n </div>\n {availableRoles.length === 0 ? (\n <div className=\"text-xs text-muted-foreground\">\n {t('customer_accounts.widgets.invite.noRoles', 'No roles available')}\n </div>\n ) : (\n <div className=\"flex flex-wrap gap-1.5\">\n {availableRoles.map((role) => (\n <Button\n key={role.id}\n type=\"button\"\n variant={selectedRoleIds.includes(role.id) ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => toggleRole(role.id)}\n disabled={isSubmitting}\n className=\"text-xs h-7\"\n >\n {role.name}\n </Button>\n ))}\n </div>\n )}\n </div>\n\n <div className=\"flex justify-end gap-2 pt-1\">\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={handleSubmit}\n disabled={isSubmitting || !email.trim() || selectedRoleIds.length === 0}\n >\n {isSubmitting\n ? t('common.loading', 'Loading...')\n : t('customer_accounts.widgets.invite.submit', 'Send Invitation')}\n </Button>\n </div>\n </div>\n )\n}\n\nexport default function AccountStatusWidget({ context }: AccountStatusProps) {\n const t = useT()\n const queryClient = useQueryClient()\n const personEntityId = context?.recordId\n const [showInviteForm, setShowInviteForm] = React.useState(false)\n\n const { data, isLoading } = useQuery({\n queryKey: ['customer-account-status', personEntityId],\n queryFn: async (): Promise<AccountStatusData | null> => {\n if (!personEntityId) return null\n const result = await apiCall(`/api/customer_accounts/admin/users?personEntityId=${personEntityId}&pageSize=1`)\n if (!result.ok) return null\n const json = result.result as Record<string, unknown> | null\n const items = json?.items as AccountStatusData[] | undefined\n return items?.[0] || null\n },\n enabled: !!personEntityId,\n })\n\n function handleInviteSuccess() {\n setShowInviteForm(false)\n queryClient.invalidateQueries({ queryKey: ['customer-account-status', personEntityId] })\n }\n\n if (isLoading) {\n return <div className=\"text-sm text-muted-foreground\">{t('common.loading', 'Loading...')}</div>\n }\n\n if (!data) {\n return (\n <div className=\"rounded-md border p-3\">\n <div className=\"text-sm font-medium mb-1\">{t('customer_accounts.widgets.accountStatus', 'Portal Account')}</div>\n <div className=\"text-sm text-muted-foreground\">{t('customer_accounts.widgets.noAccount', 'No portal account linked')}</div>\n {!showInviteForm && personEntityId && (\n <div className=\"mt-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setShowInviteForm(true)}\n >\n {t('customer_accounts.widgets.invite.button', 'Invite to Portal')}\n </Button>\n </div>\n )}\n {showInviteForm && personEntityId && (\n <InviteForm personEntityId={personEntityId} onSuccess={handleInviteSuccess} />\n )}\n </div>\n )\n }\n\n return (\n <div className=\"rounded-md border p-3\">\n <div className=\"text-sm font-medium mb-2\">{t('customer_accounts.widgets.accountStatus', 'Portal Account')}</div>\n <div className=\"space-y-1 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('common.status', 'Status')}</span>\n <StatusBadge variant={data.isActive ? 'success' : 'error'} dot>\n {data.isActive ? t('common.active', 'Active') : t('common.inactive', 'Inactive')}\n </StatusBadge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('common.email', 'Email')}</span>\n <span>{data.email}</span>\n </div>\n {data.emailVerified !== undefined && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('customer_accounts.widgets.emailVerified', 'Email Verified')}</span>\n <span>{data.emailVerified ? '\u2713' : '\u2717'}</span>\n </div>\n )}\n {data.lastLoginAt && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">{t('customer_accounts.widgets.lastLogin', 'Last Login')}</span>\n <span>{new Date(data.lastLoginAt).toLocaleDateString()}</span>\n </div>\n )}\n </div>\n <div className=\"mt-2\">\n <a\n href={`/backend/customer_accounts/users/${data.id}`}\n className=\"text-xs text-primary hover:underline\"\n >\n {t('customer_accounts.widgets.viewAccount', 'View account details \u2192')}\n </a>\n </div>\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,22 +1,24 @@
|
|
|
1
|
+
const accountStatusWidget = {
|
|
2
|
+
widgetId: "customer_accounts.injection.account-status",
|
|
3
|
+
kind: "group",
|
|
4
|
+
column: 2,
|
|
5
|
+
groupLabel: "customer_accounts.widgets.accountStatus",
|
|
6
|
+
priority: 200
|
|
7
|
+
};
|
|
8
|
+
const companyUsersWidget = {
|
|
9
|
+
widgetId: "customer_accounts.injection.company-users",
|
|
10
|
+
kind: "group",
|
|
11
|
+
column: 2,
|
|
12
|
+
groupLabel: "customer_accounts.widgets.portalUsers",
|
|
13
|
+
priority: 200
|
|
14
|
+
};
|
|
1
15
|
const injectionTable = {
|
|
2
|
-
"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
priority: 200
|
|
9
|
-
}
|
|
10
|
-
],
|
|
11
|
-
"crud-form:customers:customer_company_profile:fields": [
|
|
12
|
-
{
|
|
13
|
-
widgetId: "customer_accounts.injection.company-users",
|
|
14
|
-
kind: "group",
|
|
15
|
-
column: 2,
|
|
16
|
-
groupLabel: "customer_accounts.widgets.portalUsers",
|
|
17
|
-
priority: 200
|
|
18
|
-
}
|
|
19
|
-
],
|
|
16
|
+
"customers.person": [accountStatusWidget],
|
|
17
|
+
"crud-form:customers.person": [accountStatusWidget],
|
|
18
|
+
"crud-form:customers:customer_person_profile:fields": [accountStatusWidget],
|
|
19
|
+
"customers.company": [companyUsersWidget],
|
|
20
|
+
"crud-form:customers.company": [companyUsersWidget],
|
|
21
|
+
"crud-form:customers:customer_company_profile:fields": [companyUsersWidget],
|
|
20
22
|
// Step 4.10 — Portal AiChat injection example.
|
|
21
23
|
// Mapped to the portal profile page's `pageAfter('profile')` spot;
|
|
22
24
|
// third-party modules targeting other portal pages can copy this entry.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/customer_accounts/widgets/injection-table.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'\n\
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type { ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'\n\nconst accountStatusWidget = {\n widgetId: 'customer_accounts.injection.account-status',\n kind: 'group',\n column: 2,\n groupLabel: 'customer_accounts.widgets.accountStatus',\n priority: 200,\n} as const\n\nconst companyUsersWidget = {\n widgetId: 'customer_accounts.injection.company-users',\n kind: 'group',\n column: 2,\n groupLabel: 'customer_accounts.widgets.portalUsers',\n priority: 200,\n} as const\n\nexport const injectionTable: ModuleInjectionTable = {\n 'customers.person': [accountStatusWidget],\n 'crud-form:customers.person': [accountStatusWidget],\n 'crud-form:customers:customer_person_profile:fields': [accountStatusWidget],\n 'customers.company': [companyUsersWidget],\n 'crud-form:customers.company': [companyUsersWidget],\n 'crud-form:customers:customer_company_profile:fields': [companyUsersWidget],\n // Step 4.10 \u2014 Portal AiChat injection example.\n // Mapped to the portal profile page's `pageAfter('profile')` spot;\n // third-party modules targeting other portal pages can copy this entry.\n 'portal:profile:after': [\n {\n widgetId: 'customer_accounts.injection.portal-ai-assistant-trigger',\n priority: 100,\n },\n ],\n}\n\nexport default injectionTable\n"],
|
|
5
|
+
"mappings": "AAEA,MAAM,sBAAsB;AAAA,EAC1B,UAAU;AAAA,EACV,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,UAAU;AACZ;AAEA,MAAM,qBAAqB;AAAA,EACzB,UAAU;AAAA,EACV,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,UAAU;AACZ;AAEO,MAAM,iBAAuC;AAAA,EAClD,oBAAoB,CAAC,mBAAmB;AAAA,EACxC,8BAA8B,CAAC,mBAAmB;AAAA,EAClD,sDAAsD,CAAC,mBAAmB;AAAA,EAC1E,qBAAqB,CAAC,kBAAkB;AAAA,EACxC,+BAA+B,CAAC,kBAAkB;AAAA,EAClD,uDAAuD,CAAC,kBAAkB;AAAA;AAAA;AAAA;AAAA,EAI1E,wBAAwB;AAAA,IACtB;AAAA,MACE,UAAU;AAAA,MACV,UAAU;AAAA,IACZ;AAAA,EACF;AACF;AAEA,IAAO,0BAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -39,6 +39,12 @@ function toNumeric(value) {
|
|
|
39
39
|
function round(value) {
|
|
40
40
|
return Math.round((value + Number.EPSILON) * 1e4) / 1e4;
|
|
41
41
|
}
|
|
42
|
+
function resolveExistingPaymentTotals(order) {
|
|
43
|
+
return {
|
|
44
|
+
paidTotalAmount: toNumeric(order.paidTotalAmount),
|
|
45
|
+
refundedTotalAmount: toNumeric(order.refundedTotalAmount)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
42
48
|
function applyOrderTotals(order, totals, lineCount) {
|
|
43
49
|
order.subtotalNetAmount = toNumericString(totals.subtotalNetAmount) ?? "0";
|
|
44
50
|
order.subtotalGrossAmount = toNumericString(totals.subtotalGrossAmount) ?? "0";
|
|
@@ -140,10 +146,7 @@ async function recalculateOrderTotalsForDisplay(em, container, orderId, scope) {
|
|
|
140
146
|
lines: lineSnapshots,
|
|
141
147
|
adjustments: adjustmentDrafts,
|
|
142
148
|
context: buildCalculationContext(order),
|
|
143
|
-
existingTotals:
|
|
144
|
-
paidTotalAmount: toNumeric(order.paidTotalAmount),
|
|
145
|
-
refundedTotalAmount: toNumeric(order.refundedTotalAmount)
|
|
146
|
-
}
|
|
149
|
+
existingTotals: resolveExistingPaymentTotals(order)
|
|
147
150
|
});
|
|
148
151
|
return calculation.totals;
|
|
149
152
|
}
|
|
@@ -282,7 +285,8 @@ async function reverseReturnEffects(em, salesCalculationService, snapshot) {
|
|
|
282
285
|
documentKind: "order",
|
|
283
286
|
lines: lineSnapshots,
|
|
284
287
|
adjustments: adjustmentDrafts,
|
|
285
|
-
context: buildCalculationContext(order)
|
|
288
|
+
context: buildCalculationContext(order),
|
|
289
|
+
existingTotals: resolveExistingPaymentTotals(order)
|
|
286
290
|
});
|
|
287
291
|
applyOrderTotals(order, calculation.totals, calculation.lines.length);
|
|
288
292
|
order.updatedAt = /* @__PURE__ */ new Date();
|
|
@@ -408,7 +412,8 @@ async function restoreReturnEffects(em, salesCalculationService, snapshot) {
|
|
|
408
412
|
documentKind: "order",
|
|
409
413
|
lines: lineSnapshots,
|
|
410
414
|
adjustments: adjustmentDrafts,
|
|
411
|
-
context: buildCalculationContext(order)
|
|
415
|
+
context: buildCalculationContext(order),
|
|
416
|
+
existingTotals: resolveExistingPaymentTotals(order)
|
|
412
417
|
});
|
|
413
418
|
applyOrderTotals(order, calculation.totals, calculation.lines.length);
|
|
414
419
|
order.updatedAt = /* @__PURE__ */ new Date();
|
|
@@ -595,7 +600,8 @@ const createReturnCommand = {
|
|
|
595
600
|
documentKind: "order",
|
|
596
601
|
lines: lineSnapshots,
|
|
597
602
|
adjustments: adjustmentDrafts,
|
|
598
|
-
context: buildCalculationContext(order)
|
|
603
|
+
context: buildCalculationContext(order),
|
|
604
|
+
existingTotals: resolveExistingPaymentTotals(order)
|
|
599
605
|
});
|
|
600
606
|
applyOrderTotals(order, calculation.totals, calculation.lines.length);
|
|
601
607
|
order.updatedAt = /* @__PURE__ */ new Date();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/sales/commands/returns.ts"],
|
|
4
|
-
"sourcesContent": ["import { randomUUID } from 'crypto'\nimport { registerCommand, type CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { LockMode } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { SalesDocumentNumberGenerator } from '../services/salesDocumentNumberGenerator'\nimport type { SalesCalculationService } from '../services/salesCalculationService'\nimport type { SalesAdjustmentDraft, SalesLineSnapshot, SalesDocumentCalculationResult } from '../lib/types'\nimport { cloneJson, deriveLineNetFromGross, ensureOrganizationScope, ensureSameScope, ensureTenantScope, extractUndoPayload, toNumericString, enforceSalesDocumentOptimisticLock, SALES_RESOURCE_KIND_ORDER, SALES_RESOURCE_KIND_RETURN } from './shared'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport { SalesOrder, SalesOrderAdjustment, SalesOrderLine, SalesReturn, SalesReturnLine, SalesShipment, SalesShipmentItem } from '../data/entities'\nimport { coerceShipmentQuantity } from '../lib/shipments/snapshots'\nimport { computeAvailableReturnQuantity } from '../lib/returnQuantity'\nimport {\n returnCreateSchema,\n returnUpdateSchema,\n returnDeleteSchema,\n type ReturnCreateInput,\n type ReturnUpdateInput,\n type ReturnDeleteInput,\n} from '../data/validators'\nimport { E } from '#generated/entities.ids.generated'\n\ntype ReturnLineInput = { orderLineId: string; quantity: number }\n\ntype ReturnSnapshot = {\n id: string\n orderId: string\n organizationId: string\n tenantId: string\n returnNumber: string\n returnedAt: string | null\n reason: string | null\n notes: string | null\n lines: Array<{\n id: string\n orderLineId: string\n quantityReturned: number\n unitPriceNet: number\n unitPriceGross: number\n totalNetAmount: number\n totalGrossAmount: number\n }>\n adjustmentIds: string[]\n}\n\ntype ReturnUndoPayload = {\n after?: ReturnSnapshot | null\n}\n\nconst returnCrudEvents: CrudEventsConfig = {\n module: 'sales',\n entity: 'return',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nfunction toNumeric(value: unknown): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length) {\n const parsed = Number(value)\n if (Number.isFinite(parsed)) return parsed\n }\n return 0\n}\n\nfunction round(value: number): number {\n return Math.round((value + Number.EPSILON) * 1e4) / 1e4\n}\n\nfunction applyOrderTotals(order: SalesOrder, totals: SalesDocumentCalculationResult['totals'], lineCount: number): void {\n order.subtotalNetAmount = toNumericString(totals.subtotalNetAmount) ?? '0'\n order.subtotalGrossAmount = toNumericString(totals.subtotalGrossAmount) ?? '0'\n order.discountTotalAmount = toNumericString(totals.discountTotalAmount) ?? '0'\n order.taxTotalAmount = toNumericString(totals.taxTotalAmount) ?? '0'\n order.shippingNetAmount = toNumericString(totals.shippingNetAmount) ?? '0'\n order.shippingGrossAmount = toNumericString(totals.shippingGrossAmount) ?? '0'\n order.surchargeTotalAmount = toNumericString(totals.surchargeTotalAmount) ?? '0'\n order.grandTotalNetAmount = toNumericString(totals.grandTotalNetAmount) ?? '0'\n order.grandTotalGrossAmount = toNumericString(totals.grandTotalGrossAmount) ?? '0'\n order.paidTotalAmount = toNumericString(totals.paidTotalAmount) ?? '0'\n order.refundedTotalAmount = toNumericString(totals.refundedTotalAmount) ?? '0'\n order.outstandingAmount = toNumericString(totals.outstandingAmount) ?? '0'\n order.totalsSnapshot = cloneJson(totals)\n order.lineItemCount = lineCount\n}\n\nfunction mapOrderLineEntityToSnapshot(line: SalesOrderLine): SalesLineSnapshot {\n return {\n id: line.id,\n lineNumber: line.lineNumber,\n kind: line.kind,\n productId: line.productId ?? null,\n productVariantId: line.productVariantId ?? null,\n name: line.name ?? null,\n description: line.description ?? null,\n comment: line.comment ?? null,\n quantity: toNumeric(line.quantity),\n quantityUnit: line.quantityUnit ?? null,\n normalizedQuantity: toNumeric(line.normalizedQuantity ?? line.quantity),\n normalizedUnit: line.normalizedUnit ?? line.quantityUnit ?? null,\n uomSnapshot: line.uomSnapshot ? cloneJson(line.uomSnapshot) : null,\n currencyCode: line.currencyCode,\n unitPriceNet: toNumeric(line.unitPriceNet),\n unitPriceGross: toNumeric(line.unitPriceGross),\n discountAmount: toNumeric(line.discountAmount),\n discountPercent: toNumeric(line.discountPercent),\n taxRate: toNumeric(line.taxRate),\n taxAmount: toNumeric(line.taxAmount),\n totalNetAmount: toNumeric(line.totalNetAmount),\n totalGrossAmount: toNumeric(line.totalGrossAmount),\n configuration: line.configuration ? cloneJson(line.configuration) : null,\n promotionCode: line.promotionCode ?? null,\n metadata: line.metadata ? cloneJson(line.metadata) : null,\n customFieldSetId: line.customFieldSetId ?? null,\n }\n}\n\nfunction mapOrderAdjustmentToDraft(adjustment: SalesOrderAdjustment): SalesAdjustmentDraft {\n return {\n id: adjustment.id,\n scope: adjustment.scope ?? 'order',\n kind: adjustment.kind,\n code: adjustment.code ?? null,\n label: adjustment.label ?? null,\n calculatorKey: adjustment.calculatorKey ?? null,\n promotionId: adjustment.promotionId ?? null,\n rate: toNumeric(adjustment.rate),\n amountNet: toNumeric(adjustment.amountNet),\n amountGross: toNumeric(adjustment.amountGross),\n currencyCode: adjustment.currencyCode ?? null,\n metadata: adjustment.metadata ? cloneJson(adjustment.metadata) : null,\n position: adjustment.position ?? 0,\n }\n}\n\nfunction buildCalculationContext(order: SalesOrder) {\n return {\n tenantId: order.tenantId,\n organizationId: order.organizationId,\n currencyCode: order.currencyCode,\n metadata: {\n shippingMethod: order.shippingMethodSnapshot\n ? cloneJson(order.shippingMethodSnapshot as Record<string, unknown>)\n : null,\n paymentMethod: order.paymentMethodSnapshot ? cloneJson(order.paymentMethodSnapshot as Record<string, unknown>) : null,\n },\n }\n}\n\n/**\n * Recalculates order totals (including line-scoped return adjustments) for display.\n * Returns the totals object to merge into an order API response, or null if order not found.\n */\nexport async function recalculateOrderTotalsForDisplay(\n em: EntityManager,\n container: { resolve: (key: string) => unknown },\n orderId: string,\n scope: { tenantId: string; organizationId: string },\n): Promise<SalesDocumentCalculationResult['totals'] | null> {\n const order = await findOneWithDecryption(\n em,\n SalesOrder,\n { id: orderId, deletedAt: null },\n {},\n scope,\n )\n if (!order) return null\n const [orderLines, adjustments] = await Promise.all([\n findWithDecryption(em, SalesOrderLine, { order: order.id, deletedAt: null }, {}, scope),\n findWithDecryption(\n em,\n SalesOrderAdjustment,\n { order: order.id, deletedAt: null },\n { orderBy: { position: 'asc' } },\n scope,\n ),\n ])\n const lineSnapshots: SalesLineSnapshot[] = orderLines.map(mapOrderLineEntityToSnapshot)\n const adjustmentDrafts: SalesAdjustmentDraft[] = adjustments.map(mapOrderAdjustmentToDraft)\n const salesCalculationService = container.resolve('salesCalculationService') as SalesCalculationService\n const calculation = await salesCalculationService.calculateDocumentTotals({\n documentKind: 'order',\n lines: lineSnapshots,\n adjustments: adjustmentDrafts,\n context: buildCalculationContext(order),\n existingTotals: {\n paidTotalAmount: toNumeric(order.paidTotalAmount),\n refundedTotalAmount: toNumeric(order.refundedTotalAmount),\n },\n })\n return calculation.totals\n}\n\nexport async function loadReturnSnapshot(em: EntityManager, id: string): Promise<ReturnSnapshot | null> {\n const header = await findOneWithDecryption(\n em,\n SalesReturn,\n { id, deletedAt: null },\n { populate: ['order'] },\n {},\n )\n if (!header || !header.order) return null\n const orderId = typeof header.order === 'string' ? header.order : header.order.id\n const lines = await findWithDecryption(\n em,\n SalesReturnLine,\n { salesReturn: header.id, deletedAt: null },\n { populate: ['orderLine'] },\n { tenantId: header.tenantId, organizationId: header.organizationId },\n )\n const adjustmentIds: string[] = []\n const adjustments = await findWithDecryption(\n em,\n SalesOrderAdjustment,\n { order: orderId, kind: 'return', deletedAt: null },\n {},\n { tenantId: header.tenantId, organizationId: header.organizationId },\n )\n adjustments.forEach((adj) => {\n const meta = adj.metadata as Record<string, unknown> | null | undefined\n if (meta && meta.returnId === header.id) adjustmentIds.push(adj.id)\n })\n\n return {\n id: header.id,\n orderId,\n organizationId: header.organizationId,\n tenantId: header.tenantId,\n returnNumber: header.returnNumber,\n returnedAt: header.returnedAt ? header.returnedAt.toISOString() : null,\n reason: header.reason ?? null,\n notes: header.notes ?? null,\n lines: lines.map((line) => ({\n id: line.id,\n orderLineId: typeof line.orderLine === 'string' ? line.orderLine : line.orderLine?.id ?? null,\n quantityReturned: toNumeric(line.quantityReturned),\n unitPriceNet: toNumeric(line.unitPriceNet),\n unitPriceGross: toNumeric(line.unitPriceGross),\n totalNetAmount: toNumeric(line.totalNetAmount),\n totalGrossAmount: toNumeric(line.totalGrossAmount),\n })),\n adjustmentIds,\n }\n}\n\ntype ReturnHeaderSnapshot = {\n id: string\n orderId: string\n organizationId: string\n tenantId: string\n reason: string | null\n notes: string | null\n returnedAt: string | null\n}\n\ntype ReturnHeaderUndoPayload = {\n before?: ReturnHeaderSnapshot | null\n after?: ReturnHeaderSnapshot | null\n}\n\ntype ReturnDeleteUndoPayload = {\n before?: ReturnSnapshot | null\n}\n\nasync function loadReturnHeaderSnapshot(em: EntityManager, id: string): Promise<ReturnHeaderSnapshot | null> {\n const header = await findOneWithDecryption(em, SalesReturn, { id, deletedAt: null }, { populate: ['order'] }, {})\n if (!header || !header.order) return null\n const orderId = typeof header.order === 'string' ? header.order : header.order.id\n return {\n id: header.id,\n orderId,\n organizationId: header.organizationId,\n tenantId: header.tenantId,\n reason: header.reason ?? null,\n notes: header.notes ?? null,\n returnedAt: header.returnedAt ? header.returnedAt.toISOString() : null,\n }\n}\n\n/**\n * Reverse the order-level effects of a return: restore each order line's\n * `returnedQuantity`, drop the return's line-scoped credit adjustments, remove\n * the return header + lines, and recalculate the order totals. Shared by the\n * create command's undo and the delete command's execute \u2014 both need the exact\n * same teardown. No-op when the order is gone.\n *\n * The line reversals, adjustment/return removals, and the order-total recompute\n * interleave queries on the same EntityManager with scalar mutations, so they\n * run inside an atomic flush to avoid lost updates and partial commits\n * (SPEC-018): the per-phase flush boundary persists the line `returnedQuantity`\n * reversals before the adjustment/header/return-line lookups in the next phase\n * run any query, which under MikroORM v7 would otherwise silently discard the\n * pending scalar changes on the managed lines.\n */\nasync function reverseReturnEffects(\n em: EntityManager,\n salesCalculationService: SalesCalculationService,\n snapshot: ReturnSnapshot,\n): Promise<void> {\n const order = await findOneWithDecryption(\n em,\n SalesOrder,\n { id: snapshot.orderId, deletedAt: null },\n {},\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n if (!order) return\n\n let lines: SalesOrderLine[] = []\n await withAtomicFlush(\n em,\n [\n async () => {\n lines = await findWithDecryption(\n em,\n SalesOrderLine,\n { order: order.id, deletedAt: null },\n {},\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n const lineMap = new Map(lines.map((line) => [line.id, line]))\n snapshot.lines.forEach((entry) => {\n const line = lineMap.get(entry.orderLineId)\n if (!line) return\n const next = Math.max(0, toNumeric(line.returnedQuantity) - entry.quantityReturned)\n line.returnedQuantity = next.toString()\n line.updatedAt = new Date()\n em.persist(line)\n })\n },\n async () => {\n if (snapshot.adjustmentIds.length) {\n const adjustments = await findWithDecryption(\n em,\n SalesOrderAdjustment,\n { id: { $in: snapshot.adjustmentIds }, deletedAt: null },\n {},\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n adjustments.forEach((adj) => em.remove(adj))\n }\n\n const header = await findOneWithDecryption(\n em,\n SalesReturn,\n { id: snapshot.id, deletedAt: null },\n {},\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n const returnLines = await findWithDecryption(\n em,\n SalesReturnLine,\n { salesReturn: snapshot.id, deletedAt: null },\n {},\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n returnLines.forEach((line) => em.remove(line))\n if (header) em.remove(header)\n\n const existingAdjustments = await findWithDecryption(\n em,\n SalesOrderAdjustment,\n { order: order.id, deletedAt: null },\n { orderBy: { position: 'asc' } },\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n const lineSnapshots: SalesLineSnapshot[] = lines.map(mapOrderLineEntityToSnapshot)\n const adjustmentDrafts: SalesAdjustmentDraft[] = existingAdjustments.map(mapOrderAdjustmentToDraft)\n const calculation = await salesCalculationService.calculateDocumentTotals({\n documentKind: 'order',\n lines: lineSnapshots,\n adjustments: adjustmentDrafts,\n context: buildCalculationContext(order),\n })\n applyOrderTotals(order, calculation.totals, calculation.lines.length)\n order.updatedAt = new Date()\n em.persist(order)\n },\n ],\n { transaction: true },\n )\n}\n\n/**\n * Re-apply a return from a snapshot: recreate the return header + lines and the\n * line-scoped credit adjustments, bump each order line's `returnedQuantity`, and\n * recalculate the order totals. Shared by the create command's redo and the\n * delete command's undo. Returns the recreated return lines so callers can emit\n * index side effects. Throws a 404 when the order is gone.\n */\nasync function restoreReturnEffects(\n em: EntityManager,\n salesCalculationService: SalesCalculationService,\n snapshot: ReturnSnapshot,\n): Promise<SalesReturnLine[]> {\n const returnId = snapshot.id\n const createdLines: SalesReturnLine[] = []\n\n await withAtomicFlush(\n em,\n [\n async () => {\n const order = await findOneWithDecryption(\n em,\n SalesOrder,\n { id: snapshot.orderId, deletedAt: null },\n {},\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n if (!order) {\n throw new CrudHttpError(404, { error: 'sales.returns.orderMissing' })\n }\n ensureSameScope(order, snapshot.organizationId, snapshot.tenantId)\n\n const orderLines = await findWithDecryption(\n em,\n SalesOrderLine,\n { order: order.id, deletedAt: null },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n const lineMap = new Map(orderLines.map((line) => [line.id, line]))\n\n const existingAdjustments = await findWithDecryption(\n em,\n SalesOrderAdjustment,\n { order: order.id, deletedAt: null },\n { orderBy: { position: 'asc' } },\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n const positionStart = existingAdjustments.reduce((acc, adj) => Math.max(acc, adj.position ?? 0), 0) + 1\n\n const restoredHeader =\n (await findOneWithDecryption(\n em,\n SalesReturn,\n { id: snapshot.id },\n {},\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )) ??\n em.create(SalesReturn, {\n id: snapshot.id,\n order,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n returnNumber: snapshot.returnNumber,\n reason: snapshot.reason ?? null,\n notes: snapshot.notes ?? null,\n returnedAt: snapshot.returnedAt ? new Date(snapshot.returnedAt) : new Date(),\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n restoredHeader.order = order\n restoredHeader.deletedAt = null\n restoredHeader.organizationId = snapshot.organizationId\n restoredHeader.tenantId = snapshot.tenantId\n restoredHeader.returnNumber = snapshot.returnNumber\n restoredHeader.reason = snapshot.reason ?? null\n restoredHeader.notes = snapshot.notes ?? null\n restoredHeader.returnedAt = snapshot.returnedAt ? new Date(snapshot.returnedAt) : new Date()\n restoredHeader.updatedAt = new Date()\n em.persist(restoredHeader)\n\n const createdAdjustments: SalesOrderAdjustment[] = []\n snapshot.lines.forEach((lineSnapshot, index) => {\n const line = lineMap.get(lineSnapshot.orderLineId)\n if (!line) return\n const totalNet = lineSnapshot.totalNetAmount\n const totalGross = lineSnapshot.totalGrossAmount\n const adjustmentId = snapshot.adjustmentIds[index] ?? randomUUID()\n\n const returnLine = em.create(SalesReturnLine, {\n id: lineSnapshot.id,\n salesReturn: restoredHeader,\n orderLine: em.getReference(SalesOrderLine, line.id),\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n quantityReturned: lineSnapshot.quantityReturned.toString(),\n unitPriceNet: lineSnapshot.unitPriceNet.toString(),\n unitPriceGross: lineSnapshot.unitPriceGross.toString(),\n totalNetAmount: totalNet.toString(),\n totalGrossAmount: totalGross.toString(),\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n createdLines.push(returnLine)\n em.persist(returnLine)\n\n const adjustment = em.create(SalesOrderAdjustment, {\n id: adjustmentId,\n order,\n orderLine: em.getReference(SalesOrderLine, line.id),\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n scope: 'line',\n kind: 'return',\n rate: '0',\n amountNet: totalNet.toString(),\n amountGross: totalGross.toString(),\n currencyCode: order.currencyCode,\n metadata: { returnId, returnLineId: lineSnapshot.id },\n position: positionStart + index,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n createdAdjustments.push(adjustment)\n em.persist(adjustment)\n\n line.returnedQuantity = (toNumeric(line.returnedQuantity) + lineSnapshot.quantityReturned).toString()\n line.updatedAt = new Date()\n em.persist(line)\n })\n\n const lineSnapshots: SalesLineSnapshot[] = orderLines.map(mapOrderLineEntityToSnapshot)\n const adjustmentDrafts: SalesAdjustmentDraft[] = [...existingAdjustments, ...createdAdjustments].map(\n mapOrderAdjustmentToDraft,\n )\n const calculation = await salesCalculationService.calculateDocumentTotals({\n documentKind: 'order',\n lines: lineSnapshots,\n adjustments: adjustmentDrafts,\n context: buildCalculationContext(order),\n })\n applyOrderTotals(order, calculation.totals, calculation.lines.length)\n order.updatedAt = new Date()\n em.persist(order)\n },\n ],\n { transaction: true },\n )\n\n return createdLines\n}\n\nasync function loadShippedQuantityByLine(\n em: EntityManager,\n orderId: string,\n scope: { tenantId: string; organizationId: string },\n): Promise<Map<string, number>> {\n const shipments = await findWithDecryption(\n em,\n SalesShipment,\n { order: orderId, deletedAt: null },\n {},\n scope,\n )\n const shippedByLine = new Map<string, number>()\n if (!shipments.length) return shippedByLine\n const items = await findWithDecryption(\n em,\n SalesShipmentItem,\n { shipment: { $in: shipments.map((shipment) => shipment.id) } },\n {},\n scope,\n )\n items.forEach((item) => {\n const orderLineId = typeof item.orderLine === 'string' ? item.orderLine : item.orderLine?.id ?? null\n if (!orderLineId) return\n const next = (shippedByLine.get(orderLineId) ?? 0) + coerceShipmentQuantity(item.quantity)\n shippedByLine.set(orderLineId, next)\n })\n return shippedByLine\n}\n\nfunction normalizeLinesInput(lines: ReturnCreateInput['lines']): ReturnLineInput[] {\n const seen = new Set<string>()\n const result: ReturnLineInput[] = []\n for (const line of lines) {\n const orderLineId = line.orderLineId\n if (!orderLineId || seen.has(orderLineId)) continue\n const quantity = toNumeric(line.quantity)\n if (!Number.isFinite(quantity) || quantity <= 0) continue\n seen.add(orderLineId)\n result.push({ orderLineId, quantity })\n }\n return result\n}\n\nconst createReturnCommand: CommandHandler<ReturnCreateInput, { returnId: string }> = {\n id: 'sales.returns.create',\n async execute(rawInput, ctx) {\n const input = returnCreateSchema.parse(rawInput ?? {})\n ensureTenantScope(ctx, input.tenantId)\n ensureOrganizationScope(ctx, input.organizationId)\n\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n const requested = normalizeLinesInput(input.lines)\n if (!requested.length) {\n throw new CrudHttpError(400, { error: translate('sales.returns.linesRequired', 'Select at least one line to return.') })\n }\n\n const salesCalculationService = ctx.container.resolve<SalesCalculationService>('salesCalculationService')\n const { header, createdLines } = await em.transactional(async (tx) => {\n const order = await findOneWithDecryption(\n tx,\n SalesOrder,\n { id: input.orderId, deletedAt: null },\n {},\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n if (!order) {\n throw new CrudHttpError(404, { error: translate('sales.returns.orderMissing', 'Order not found.') })\n }\n ensureSameScope(order, input.organizationId, input.tenantId)\n await enforceSalesDocumentOptimisticLock(ctx, order, SALES_RESOURCE_KIND_ORDER)\n\n const orderLines = await findWithDecryption(\n tx,\n SalesOrderLine,\n { order: order.id, deletedAt: null },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n const lineMap = new Map(orderLines.map((line) => [line.id, line]))\n\n const shippedByLine = await loadShippedQuantityByLine(tx, order.id, {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n\n requested.forEach(({ orderLineId, quantity }) => {\n const line = lineMap.get(orderLineId)\n if (!line) {\n throw new CrudHttpError(404, { error: translate('sales.returns.lineMissing', 'Order line not found.') })\n }\n const available = computeAvailableReturnQuantity({\n quantity: toNumeric(line.quantity),\n returnedQuantity: toNumeric(line.returnedQuantity),\n shippedQuantity: shippedByLine.get(orderLineId) ?? 0,\n })\n if (quantity - 1e-6 > available) {\n throw new CrudHttpError(400, { error: translate('sales.returns.quantityExceedsShipped', 'Cannot return more than the shipped quantity. Ship the items before recording a return.') })\n }\n })\n\n const existingAdjustments = await findWithDecryption(\n tx,\n SalesOrderAdjustment,\n { order: order.id, deletedAt: null },\n { orderBy: { position: 'asc' } },\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n const positionStart = existingAdjustments.reduce((acc, adj) => Math.max(acc, adj.position ?? 0), 0) + 1\n\n const numberGenerator = new SalesDocumentNumberGenerator(tx)\n const generated = await numberGenerator.generate({\n kind: 'return',\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n const returnId = randomUUID()\n const entity = tx.create(SalesReturn, {\n id: returnId,\n order,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n returnNumber: generated.number,\n reason: input.reason ?? null,\n notes: input.notes ?? null,\n returnedAt: input.returnedAt ?? new Date(),\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n tx.persist(entity)\n\n const createdAdjustments: SalesOrderAdjustment[] = []\n const createdReturnLines: SalesReturnLine[] = []\n requested.forEach((lineInput, index) => {\n const line = lineMap.get(lineInput.orderLineId)\n if (!line) return\n const quantity = lineInput.quantity\n const lineQuantity = Math.max(toNumeric(line.quantity), 0)\n // `total_net_amount = 0` while `total_gross_amount > 0` is not a representable\n // priced state (gross = net * (1 + taxRate) \u21D2 net = 0 \u21D2 gross = 0). When a line\n // carries a positive gross but a zeroed/missing net, reconstruct the net from the\n // line's gross and tax rate so the return credits both sides and the order's net\n // grand total moves in lockstep with gross (#3036). A genuinely free line\n // (gross = 0, e.g. a 100% discount / comp) keeps net 0, so the return is not\n // over-credited at the discount-ignoring unit price (#3521).\n const lineTotalNet = deriveLineNetFromGross(line.totalNetAmount, line.totalGrossAmount, line.taxRate)\n const unitNet = lineQuantity > 0 ? lineTotalNet / lineQuantity : toNumeric(line.unitPriceNet)\n const unitGross = lineQuantity > 0 ? toNumeric(line.totalGrossAmount) / lineQuantity : toNumeric(line.unitPriceGross)\n const totalNet = -round(Math.max(unitNet, 0) * quantity)\n const totalGross = -round(Math.max(unitGross, 0) * quantity)\n\n const returnLineId = randomUUID()\n const returnLine = tx.create(SalesReturnLine, {\n id: returnLineId,\n salesReturn: entity,\n orderLine: tx.getReference(SalesOrderLine, line.id),\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n quantityReturned: quantity.toString(),\n unitPriceNet: round(unitNet).toString(),\n unitPriceGross: round(unitGross).toString(),\n totalNetAmount: totalNet.toString(),\n totalGrossAmount: totalGross.toString(),\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n createdReturnLines.push(returnLine)\n tx.persist(returnLine)\n\n const adjustment = tx.create(SalesOrderAdjustment, {\n id: randomUUID(),\n order,\n orderLine: tx.getReference(SalesOrderLine, line.id),\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n scope: 'line',\n kind: 'return',\n rate: '0',\n amountNet: totalNet.toString(),\n amountGross: totalGross.toString(),\n currencyCode: order.currencyCode,\n metadata: { returnId, returnLineId },\n position: positionStart + index,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n createdAdjustments.push(adjustment)\n tx.persist(adjustment)\n\n line.returnedQuantity = (toNumeric(line.returnedQuantity) + quantity).toString()\n line.updatedAt = new Date()\n tx.persist(line)\n })\n\n const lineSnapshots: SalesLineSnapshot[] = orderLines.map(mapOrderLineEntityToSnapshot)\n const adjustmentDrafts: SalesAdjustmentDraft[] = [...existingAdjustments, ...createdAdjustments].map(mapOrderAdjustmentToDraft)\n const calculation = await salesCalculationService.calculateDocumentTotals({\n documentKind: 'order',\n lines: lineSnapshots,\n adjustments: adjustmentDrafts,\n context: buildCalculationContext(order),\n })\n applyOrderTotals(order, calculation.totals, calculation.lines.length)\n order.updatedAt = new Date()\n tx.persist(order)\n\n await tx.flush()\n\n return { header: entity, createdLines: createdReturnLines }\n })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: header,\n identifiers: { id: header.id, organizationId: header.organizationId, tenantId: header.tenantId },\n indexer: { entityType: E.sales.sales_return },\n events: returnCrudEvents,\n })\n\n if (createdLines.length) {\n await Promise.all(\n createdLines.map((line) =>\n emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: line,\n identifiers: { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n indexer: { entityType: E.sales.sales_return_line },\n }),\n ),\n )\n }\n\n return { returnId: header.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadReturnSnapshot(em, result.returnId)\n },\n buildLog: async ({ result, snapshots }) => {\n const after = snapshots.after as ReturnSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('sales.audit.returns.create', 'Create return'),\n resourceKind: 'sales.return',\n resourceId: result.returnId,\n parentResourceKind: 'sales.order',\n parentResourceId: after.orderId ?? null,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: {\n undo: { after } satisfies ReturnUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ReturnUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const salesCalculationService = ctx.container.resolve<SalesCalculationService>('salesCalculationService')\n await reverseReturnEffects(em, salesCalculationService, after)\n },\n redo: async ({ ctx, logEntry }) => {\n const after = resolveRedoSnapshot<ReturnSnapshot>(logEntry)\n if (!after || !after.id) {\n throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for sales.returns.create' })\n }\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const salesCalculationService = ctx.container.resolve<SalesCalculationService>('salesCalculationService')\n\n const createdLines = await restoreReturnEffects(em, salesCalculationService, after)\n\n const header = await findOneWithDecryption(\n em,\n SalesReturn,\n { id: after.id, deletedAt: null },\n {},\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n if (!header) {\n throw new CrudHttpError(404, { error: 'sales.returns.orderMissing' })\n }\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: header,\n identifiers: { id: header.id, organizationId: header.organizationId, tenantId: header.tenantId },\n indexer: { entityType: E.sales.sales_return },\n events: returnCrudEvents,\n })\n\n if (createdLines.length) {\n await Promise.all(\n createdLines.map((line) =>\n emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: line,\n identifiers: { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n indexer: { entityType: E.sales.sales_return_line },\n }),\n ),\n )\n }\n\n return { returnId: header.id }\n },\n}\n\nconst updateReturnCommand: CommandHandler<ReturnUpdateInput, { returnId: string }> = {\n id: 'sales.returns.update',\n async prepare(rawInput, ctx) {\n const parsed = returnUpdateSchema.parse(rawInput ?? {})\n const em = ctx.container.resolve('em') as EntityManager\n const snapshot = await loadReturnHeaderSnapshot(em, parsed.id)\n if (snapshot) {\n ensureTenantScope(ctx, snapshot.tenantId)\n ensureOrganizationScope(ctx, snapshot.organizationId)\n }\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const input = returnUpdateSchema.parse(rawInput ?? {})\n ensureTenantScope(ctx, input.tenantId)\n ensureOrganizationScope(ctx, input.organizationId)\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n const header = await em.transactional(async (tx) => {\n const entity = await findOneWithDecryption(\n tx,\n SalesReturn,\n { id: input.id, deletedAt: null },\n { populate: ['order'] },\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n if (!entity || !entity.order) {\n throw new CrudHttpError(404, { error: translate('sales.returns.notFound', 'Return not found.') })\n }\n ensureSameScope(entity, input.organizationId, input.tenantId)\n const orderId = typeof entity.order === 'string' ? entity.order : entity.order.id\n if (input.orderId !== orderId) {\n throw new CrudHttpError(400, { error: translate('sales.returns.orderMismatch', 'Return does not belong to this order.') })\n }\n // Lock on the return's own version \u2014 editing header fields (reason / notes /\n // returnedAt) only touches the return, not the order totals.\n await enforceSalesDocumentOptimisticLock(ctx, entity, SALES_RESOURCE_KIND_RETURN)\n\n if (input.reason !== undefined) entity.reason = input.reason.length ? input.reason : null\n if (input.notes !== undefined) entity.notes = input.notes.length ? input.notes : null\n if (input.returnedAt !== undefined) entity.returnedAt = input.returnedAt ?? null\n entity.updatedAt = new Date()\n tx.persist(entity)\n await tx.flush()\n return entity\n })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: header,\n identifiers: { id: header.id, organizationId: header.organizationId, tenantId: header.tenantId },\n indexer: { entityType: E.sales.sales_return },\n events: returnCrudEvents,\n })\n\n return { returnId: header.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadReturnHeaderSnapshot(em, result.returnId)\n },\n buildLog: async ({ snapshots, result }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as ReturnHeaderSnapshot | undefined\n const after = snapshots.after as ReturnHeaderSnapshot | undefined\n return {\n actionLabel: translate('sales.audit.returns.update', 'Update return'),\n resourceKind: 'sales.return',\n resourceId: result.returnId,\n parentResourceKind: 'sales.order',\n parentResourceId: after?.orderId ?? before?.orderId ?? null,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: { before, after } satisfies ReturnHeaderUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ReturnHeaderUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await em.transactional(async (tx) => {\n const entity = await findOneWithDecryption(\n tx,\n SalesReturn,\n { id: before.id, deletedAt: null },\n {},\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!entity) return\n entity.reason = before.reason\n entity.notes = before.notes\n entity.returnedAt = before.returnedAt ? new Date(before.returnedAt) : null\n entity.updatedAt = new Date()\n tx.persist(entity)\n await tx.flush()\n })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n const restored = await findOneWithDecryption(\n em,\n SalesReturn,\n { id: before.id, deletedAt: null },\n {},\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (restored) {\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: restored,\n identifiers: { id: restored.id, organizationId: restored.organizationId, tenantId: restored.tenantId },\n indexer: { entityType: E.sales.sales_return },\n events: returnCrudEvents,\n })\n }\n },\n}\n\nconst deleteReturnCommand: CommandHandler<ReturnDeleteInput, { returnId: string }> = {\n id: 'sales.returns.delete',\n async prepare(rawInput, ctx) {\n const parsed = returnDeleteSchema.parse(rawInput ?? {})\n const em = ctx.container.resolve('em') as EntityManager\n const snapshot = await loadReturnSnapshot(em, parsed.id)\n if (snapshot) {\n ensureTenantScope(ctx, snapshot.tenantId)\n ensureOrganizationScope(ctx, snapshot.organizationId)\n }\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const input = returnDeleteSchema.parse(rawInput ?? {})\n ensureTenantScope(ctx, input.tenantId)\n ensureOrganizationScope(ctx, input.organizationId)\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const salesCalculationService = ctx.container.resolve<SalesCalculationService>('salesCalculationService')\n\n const snapshot = await loadReturnSnapshot(em, input.id)\n if (!snapshot) {\n throw new CrudHttpError(404, { error: translate('sales.returns.notFound', 'Return not found.') })\n }\n ensureSameScope(snapshot, input.organizationId, input.tenantId)\n if (input.orderId !== snapshot.orderId) {\n throw new CrudHttpError(400, { error: translate('sales.returns.orderMismatch', 'Return does not belong to this order.') })\n }\n\n const header = await findOneWithDecryption(\n em,\n SalesReturn,\n { id: input.id, deletedAt: null },\n {},\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n if (!header) {\n throw new CrudHttpError(404, { error: translate('sales.returns.notFound', 'Return not found.') })\n }\n ensureSameScope(header, input.organizationId, input.tenantId)\n // Lock on the return's own version, captured before any mutation.\n await enforceSalesDocumentOptimisticLock(ctx, header, SALES_RESOURCE_KIND_RETURN)\n\n await reverseReturnEffects(em, salesCalculationService, snapshot)\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: header,\n identifiers: { id: snapshot.id, organizationId: snapshot.organizationId, tenantId: snapshot.tenantId },\n indexer: { entityType: E.sales.sales_return },\n events: returnCrudEvents,\n })\n\n if (snapshot.lines.length) {\n await Promise.all(\n snapshot.lines.map((line) =>\n emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: { id: line.id, organizationId: snapshot.organizationId, tenantId: snapshot.tenantId },\n identifiers: { id: line.id, organizationId: snapshot.organizationId, tenantId: snapshot.tenantId },\n indexer: { entityType: E.sales.sales_return_line },\n }),\n ),\n )\n }\n\n return { returnId: snapshot.id }\n },\n buildLog: async ({ snapshots, result }) => {\n const before = snapshots.before as ReturnSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('sales.audit.returns.delete', 'Delete return'),\n resourceKind: 'sales.return',\n resourceId: result.returnId,\n parentResourceKind: 'sales.order',\n parentResourceId: before.orderId ?? null,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: { before } satisfies ReturnDeleteUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ReturnDeleteUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const salesCalculationService = ctx.container.resolve<SalesCalculationService>('salesCalculationService')\n\n const createdLines = await restoreReturnEffects(em, salesCalculationService, before)\n\n const header = await findOneWithDecryption(\n em,\n SalesReturn,\n { id: before.id, deletedAt: null },\n {},\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!header) return\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: header,\n identifiers: { id: header.id, organizationId: header.organizationId, tenantId: header.tenantId },\n indexer: { entityType: E.sales.sales_return },\n events: returnCrudEvents,\n })\n\n if (createdLines.length) {\n await Promise.all(\n createdLines.map((line) =>\n emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: line,\n identifiers: { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n indexer: { entityType: E.sales.sales_return_line },\n }),\n ),\n )\n }\n },\n}\n\nregisterCommand(createReturnCommand)\nregisterCommand(updateReturnCommand)\nregisterCommand(deleteReturnCommand)\n\nexport const returnCommands = [createReturnCommand, updateReturnCommand, deleteReturnCommand]\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { randomUUID } from 'crypto'\nimport { registerCommand, type CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { LockMode } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { SalesDocumentNumberGenerator } from '../services/salesDocumentNumberGenerator'\nimport type { SalesCalculationService } from '../services/salesCalculationService'\nimport type { SalesAdjustmentDraft, SalesLineSnapshot, SalesDocumentCalculationResult } from '../lib/types'\nimport { cloneJson, deriveLineNetFromGross, ensureOrganizationScope, ensureSameScope, ensureTenantScope, extractUndoPayload, toNumericString, enforceSalesDocumentOptimisticLock, SALES_RESOURCE_KIND_ORDER, SALES_RESOURCE_KIND_RETURN } from './shared'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport { SalesOrder, SalesOrderAdjustment, SalesOrderLine, SalesReturn, SalesReturnLine, SalesShipment, SalesShipmentItem } from '../data/entities'\nimport { coerceShipmentQuantity } from '../lib/shipments/snapshots'\nimport { computeAvailableReturnQuantity } from '../lib/returnQuantity'\nimport {\n returnCreateSchema,\n returnUpdateSchema,\n returnDeleteSchema,\n type ReturnCreateInput,\n type ReturnUpdateInput,\n type ReturnDeleteInput,\n} from '../data/validators'\nimport { E } from '#generated/entities.ids.generated'\n\ntype ReturnLineInput = { orderLineId: string; quantity: number }\n\ntype ReturnSnapshot = {\n id: string\n orderId: string\n organizationId: string\n tenantId: string\n returnNumber: string\n returnedAt: string | null\n reason: string | null\n notes: string | null\n lines: Array<{\n id: string\n orderLineId: string\n quantityReturned: number\n unitPriceNet: number\n unitPriceGross: number\n totalNetAmount: number\n totalGrossAmount: number\n }>\n adjustmentIds: string[]\n}\n\ntype ReturnUndoPayload = {\n after?: ReturnSnapshot | null\n}\n\nconst returnCrudEvents: CrudEventsConfig = {\n module: 'sales',\n entity: 'return',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nfunction toNumeric(value: unknown): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length) {\n const parsed = Number(value)\n if (Number.isFinite(parsed)) return parsed\n }\n return 0\n}\n\nfunction round(value: number): number {\n return Math.round((value + Number.EPSILON) * 1e4) / 1e4\n}\n\n/**\n * Payment totals live on the order, not in the line/adjustment math a return\n * recalculates. Every `calculateDocumentTotals` call that writes order totals\n * back to the order MUST seed these so `buildBaseDocumentResult` preserves the\n * recorded `paidTotalAmount` / `refundedTotalAmount` instead of defaulting them\n * to 0 \u2014 otherwise creating/undoing/redoing a return silently zeroes the paid\n * amount and the order's outstanding balance goes wrong on any paid order\n * (#3756). Mirrors `resolveExistingPaymentTotals` in `commands/documents.ts`.\n */\nfunction resolveExistingPaymentTotals(order: SalesOrder): { paidTotalAmount: number; refundedTotalAmount: number } {\n return {\n paidTotalAmount: toNumeric(order.paidTotalAmount),\n refundedTotalAmount: toNumeric(order.refundedTotalAmount),\n }\n}\n\nfunction applyOrderTotals(order: SalesOrder, totals: SalesDocumentCalculationResult['totals'], lineCount: number): void {\n order.subtotalNetAmount = toNumericString(totals.subtotalNetAmount) ?? '0'\n order.subtotalGrossAmount = toNumericString(totals.subtotalGrossAmount) ?? '0'\n order.discountTotalAmount = toNumericString(totals.discountTotalAmount) ?? '0'\n order.taxTotalAmount = toNumericString(totals.taxTotalAmount) ?? '0'\n order.shippingNetAmount = toNumericString(totals.shippingNetAmount) ?? '0'\n order.shippingGrossAmount = toNumericString(totals.shippingGrossAmount) ?? '0'\n order.surchargeTotalAmount = toNumericString(totals.surchargeTotalAmount) ?? '0'\n order.grandTotalNetAmount = toNumericString(totals.grandTotalNetAmount) ?? '0'\n order.grandTotalGrossAmount = toNumericString(totals.grandTotalGrossAmount) ?? '0'\n order.paidTotalAmount = toNumericString(totals.paidTotalAmount) ?? '0'\n order.refundedTotalAmount = toNumericString(totals.refundedTotalAmount) ?? '0'\n order.outstandingAmount = toNumericString(totals.outstandingAmount) ?? '0'\n order.totalsSnapshot = cloneJson(totals)\n order.lineItemCount = lineCount\n}\n\nfunction mapOrderLineEntityToSnapshot(line: SalesOrderLine): SalesLineSnapshot {\n return {\n id: line.id,\n lineNumber: line.lineNumber,\n kind: line.kind,\n productId: line.productId ?? null,\n productVariantId: line.productVariantId ?? null,\n name: line.name ?? null,\n description: line.description ?? null,\n comment: line.comment ?? null,\n quantity: toNumeric(line.quantity),\n quantityUnit: line.quantityUnit ?? null,\n normalizedQuantity: toNumeric(line.normalizedQuantity ?? line.quantity),\n normalizedUnit: line.normalizedUnit ?? line.quantityUnit ?? null,\n uomSnapshot: line.uomSnapshot ? cloneJson(line.uomSnapshot) : null,\n currencyCode: line.currencyCode,\n unitPriceNet: toNumeric(line.unitPriceNet),\n unitPriceGross: toNumeric(line.unitPriceGross),\n discountAmount: toNumeric(line.discountAmount),\n discountPercent: toNumeric(line.discountPercent),\n taxRate: toNumeric(line.taxRate),\n taxAmount: toNumeric(line.taxAmount),\n totalNetAmount: toNumeric(line.totalNetAmount),\n totalGrossAmount: toNumeric(line.totalGrossAmount),\n configuration: line.configuration ? cloneJson(line.configuration) : null,\n promotionCode: line.promotionCode ?? null,\n metadata: line.metadata ? cloneJson(line.metadata) : null,\n customFieldSetId: line.customFieldSetId ?? null,\n }\n}\n\nfunction mapOrderAdjustmentToDraft(adjustment: SalesOrderAdjustment): SalesAdjustmentDraft {\n return {\n id: adjustment.id,\n scope: adjustment.scope ?? 'order',\n kind: adjustment.kind,\n code: adjustment.code ?? null,\n label: adjustment.label ?? null,\n calculatorKey: adjustment.calculatorKey ?? null,\n promotionId: adjustment.promotionId ?? null,\n rate: toNumeric(adjustment.rate),\n amountNet: toNumeric(adjustment.amountNet),\n amountGross: toNumeric(adjustment.amountGross),\n currencyCode: adjustment.currencyCode ?? null,\n metadata: adjustment.metadata ? cloneJson(adjustment.metadata) : null,\n position: adjustment.position ?? 0,\n }\n}\n\nfunction buildCalculationContext(order: SalesOrder) {\n return {\n tenantId: order.tenantId,\n organizationId: order.organizationId,\n currencyCode: order.currencyCode,\n metadata: {\n shippingMethod: order.shippingMethodSnapshot\n ? cloneJson(order.shippingMethodSnapshot as Record<string, unknown>)\n : null,\n paymentMethod: order.paymentMethodSnapshot ? cloneJson(order.paymentMethodSnapshot as Record<string, unknown>) : null,\n },\n }\n}\n\n/**\n * Recalculates order totals (including line-scoped return adjustments) for display.\n * Returns the totals object to merge into an order API response, or null if order not found.\n */\nexport async function recalculateOrderTotalsForDisplay(\n em: EntityManager,\n container: { resolve: (key: string) => unknown },\n orderId: string,\n scope: { tenantId: string; organizationId: string },\n): Promise<SalesDocumentCalculationResult['totals'] | null> {\n const order = await findOneWithDecryption(\n em,\n SalesOrder,\n { id: orderId, deletedAt: null },\n {},\n scope,\n )\n if (!order) return null\n const [orderLines, adjustments] = await Promise.all([\n findWithDecryption(em, SalesOrderLine, { order: order.id, deletedAt: null }, {}, scope),\n findWithDecryption(\n em,\n SalesOrderAdjustment,\n { order: order.id, deletedAt: null },\n { orderBy: { position: 'asc' } },\n scope,\n ),\n ])\n const lineSnapshots: SalesLineSnapshot[] = orderLines.map(mapOrderLineEntityToSnapshot)\n const adjustmentDrafts: SalesAdjustmentDraft[] = adjustments.map(mapOrderAdjustmentToDraft)\n const salesCalculationService = container.resolve('salesCalculationService') as SalesCalculationService\n const calculation = await salesCalculationService.calculateDocumentTotals({\n documentKind: 'order',\n lines: lineSnapshots,\n adjustments: adjustmentDrafts,\n context: buildCalculationContext(order),\n existingTotals: resolveExistingPaymentTotals(order),\n })\n return calculation.totals\n}\n\nexport async function loadReturnSnapshot(em: EntityManager, id: string): Promise<ReturnSnapshot | null> {\n const header = await findOneWithDecryption(\n em,\n SalesReturn,\n { id, deletedAt: null },\n { populate: ['order'] },\n {},\n )\n if (!header || !header.order) return null\n const orderId = typeof header.order === 'string' ? header.order : header.order.id\n const lines = await findWithDecryption(\n em,\n SalesReturnLine,\n { salesReturn: header.id, deletedAt: null },\n { populate: ['orderLine'] },\n { tenantId: header.tenantId, organizationId: header.organizationId },\n )\n const adjustmentIds: string[] = []\n const adjustments = await findWithDecryption(\n em,\n SalesOrderAdjustment,\n { order: orderId, kind: 'return', deletedAt: null },\n {},\n { tenantId: header.tenantId, organizationId: header.organizationId },\n )\n adjustments.forEach((adj) => {\n const meta = adj.metadata as Record<string, unknown> | null | undefined\n if (meta && meta.returnId === header.id) adjustmentIds.push(adj.id)\n })\n\n return {\n id: header.id,\n orderId,\n organizationId: header.organizationId,\n tenantId: header.tenantId,\n returnNumber: header.returnNumber,\n returnedAt: header.returnedAt ? header.returnedAt.toISOString() : null,\n reason: header.reason ?? null,\n notes: header.notes ?? null,\n lines: lines.map((line) => ({\n id: line.id,\n orderLineId: typeof line.orderLine === 'string' ? line.orderLine : line.orderLine?.id ?? null,\n quantityReturned: toNumeric(line.quantityReturned),\n unitPriceNet: toNumeric(line.unitPriceNet),\n unitPriceGross: toNumeric(line.unitPriceGross),\n totalNetAmount: toNumeric(line.totalNetAmount),\n totalGrossAmount: toNumeric(line.totalGrossAmount),\n })),\n adjustmentIds,\n }\n}\n\ntype ReturnHeaderSnapshot = {\n id: string\n orderId: string\n organizationId: string\n tenantId: string\n reason: string | null\n notes: string | null\n returnedAt: string | null\n}\n\ntype ReturnHeaderUndoPayload = {\n before?: ReturnHeaderSnapshot | null\n after?: ReturnHeaderSnapshot | null\n}\n\ntype ReturnDeleteUndoPayload = {\n before?: ReturnSnapshot | null\n}\n\nasync function loadReturnHeaderSnapshot(em: EntityManager, id: string): Promise<ReturnHeaderSnapshot | null> {\n const header = await findOneWithDecryption(em, SalesReturn, { id, deletedAt: null }, { populate: ['order'] }, {})\n if (!header || !header.order) return null\n const orderId = typeof header.order === 'string' ? header.order : header.order.id\n return {\n id: header.id,\n orderId,\n organizationId: header.organizationId,\n tenantId: header.tenantId,\n reason: header.reason ?? null,\n notes: header.notes ?? null,\n returnedAt: header.returnedAt ? header.returnedAt.toISOString() : null,\n }\n}\n\n/**\n * Reverse the order-level effects of a return: restore each order line's\n * `returnedQuantity`, drop the return's line-scoped credit adjustments, remove\n * the return header + lines, and recalculate the order totals. Shared by the\n * create command's undo and the delete command's execute \u2014 both need the exact\n * same teardown. No-op when the order is gone.\n *\n * The line reversals, adjustment/return removals, and the order-total recompute\n * interleave queries on the same EntityManager with scalar mutations, so they\n * run inside an atomic flush to avoid lost updates and partial commits\n * (SPEC-018): the per-phase flush boundary persists the line `returnedQuantity`\n * reversals before the adjustment/header/return-line lookups in the next phase\n * run any query, which under MikroORM v7 would otherwise silently discard the\n * pending scalar changes on the managed lines.\n */\nasync function reverseReturnEffects(\n em: EntityManager,\n salesCalculationService: SalesCalculationService,\n snapshot: ReturnSnapshot,\n): Promise<void> {\n const order = await findOneWithDecryption(\n em,\n SalesOrder,\n { id: snapshot.orderId, deletedAt: null },\n {},\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n if (!order) return\n\n let lines: SalesOrderLine[] = []\n await withAtomicFlush(\n em,\n [\n async () => {\n lines = await findWithDecryption(\n em,\n SalesOrderLine,\n { order: order.id, deletedAt: null },\n {},\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n const lineMap = new Map(lines.map((line) => [line.id, line]))\n snapshot.lines.forEach((entry) => {\n const line = lineMap.get(entry.orderLineId)\n if (!line) return\n const next = Math.max(0, toNumeric(line.returnedQuantity) - entry.quantityReturned)\n line.returnedQuantity = next.toString()\n line.updatedAt = new Date()\n em.persist(line)\n })\n },\n async () => {\n if (snapshot.adjustmentIds.length) {\n const adjustments = await findWithDecryption(\n em,\n SalesOrderAdjustment,\n { id: { $in: snapshot.adjustmentIds }, deletedAt: null },\n {},\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n adjustments.forEach((adj) => em.remove(adj))\n }\n\n const header = await findOneWithDecryption(\n em,\n SalesReturn,\n { id: snapshot.id, deletedAt: null },\n {},\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n const returnLines = await findWithDecryption(\n em,\n SalesReturnLine,\n { salesReturn: snapshot.id, deletedAt: null },\n {},\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n returnLines.forEach((line) => em.remove(line))\n if (header) em.remove(header)\n\n const existingAdjustments = await findWithDecryption(\n em,\n SalesOrderAdjustment,\n { order: order.id, deletedAt: null },\n { orderBy: { position: 'asc' } },\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n const lineSnapshots: SalesLineSnapshot[] = lines.map(mapOrderLineEntityToSnapshot)\n const adjustmentDrafts: SalesAdjustmentDraft[] = existingAdjustments.map(mapOrderAdjustmentToDraft)\n const calculation = await salesCalculationService.calculateDocumentTotals({\n documentKind: 'order',\n lines: lineSnapshots,\n adjustments: adjustmentDrafts,\n context: buildCalculationContext(order),\n existingTotals: resolveExistingPaymentTotals(order),\n })\n applyOrderTotals(order, calculation.totals, calculation.lines.length)\n order.updatedAt = new Date()\n em.persist(order)\n },\n ],\n { transaction: true },\n )\n}\n\n/**\n * Re-apply a return from a snapshot: recreate the return header + lines and the\n * line-scoped credit adjustments, bump each order line's `returnedQuantity`, and\n * recalculate the order totals. Shared by the create command's redo and the\n * delete command's undo. Returns the recreated return lines so callers can emit\n * index side effects. Throws a 404 when the order is gone.\n */\nasync function restoreReturnEffects(\n em: EntityManager,\n salesCalculationService: SalesCalculationService,\n snapshot: ReturnSnapshot,\n): Promise<SalesReturnLine[]> {\n const returnId = snapshot.id\n const createdLines: SalesReturnLine[] = []\n\n await withAtomicFlush(\n em,\n [\n async () => {\n const order = await findOneWithDecryption(\n em,\n SalesOrder,\n { id: snapshot.orderId, deletedAt: null },\n {},\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n if (!order) {\n throw new CrudHttpError(404, { error: 'sales.returns.orderMissing' })\n }\n ensureSameScope(order, snapshot.organizationId, snapshot.tenantId)\n\n const orderLines = await findWithDecryption(\n em,\n SalesOrderLine,\n { order: order.id, deletedAt: null },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n const lineMap = new Map(orderLines.map((line) => [line.id, line]))\n\n const existingAdjustments = await findWithDecryption(\n em,\n SalesOrderAdjustment,\n { order: order.id, deletedAt: null },\n { orderBy: { position: 'asc' } },\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )\n const positionStart = existingAdjustments.reduce((acc, adj) => Math.max(acc, adj.position ?? 0), 0) + 1\n\n const restoredHeader =\n (await findOneWithDecryption(\n em,\n SalesReturn,\n { id: snapshot.id },\n {},\n { tenantId: snapshot.tenantId, organizationId: snapshot.organizationId },\n )) ??\n em.create(SalesReturn, {\n id: snapshot.id,\n order,\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n returnNumber: snapshot.returnNumber,\n reason: snapshot.reason ?? null,\n notes: snapshot.notes ?? null,\n returnedAt: snapshot.returnedAt ? new Date(snapshot.returnedAt) : new Date(),\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n restoredHeader.order = order\n restoredHeader.deletedAt = null\n restoredHeader.organizationId = snapshot.organizationId\n restoredHeader.tenantId = snapshot.tenantId\n restoredHeader.returnNumber = snapshot.returnNumber\n restoredHeader.reason = snapshot.reason ?? null\n restoredHeader.notes = snapshot.notes ?? null\n restoredHeader.returnedAt = snapshot.returnedAt ? new Date(snapshot.returnedAt) : new Date()\n restoredHeader.updatedAt = new Date()\n em.persist(restoredHeader)\n\n const createdAdjustments: SalesOrderAdjustment[] = []\n snapshot.lines.forEach((lineSnapshot, index) => {\n const line = lineMap.get(lineSnapshot.orderLineId)\n if (!line) return\n const totalNet = lineSnapshot.totalNetAmount\n const totalGross = lineSnapshot.totalGrossAmount\n const adjustmentId = snapshot.adjustmentIds[index] ?? randomUUID()\n\n const returnLine = em.create(SalesReturnLine, {\n id: lineSnapshot.id,\n salesReturn: restoredHeader,\n orderLine: em.getReference(SalesOrderLine, line.id),\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n quantityReturned: lineSnapshot.quantityReturned.toString(),\n unitPriceNet: lineSnapshot.unitPriceNet.toString(),\n unitPriceGross: lineSnapshot.unitPriceGross.toString(),\n totalNetAmount: totalNet.toString(),\n totalGrossAmount: totalGross.toString(),\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n createdLines.push(returnLine)\n em.persist(returnLine)\n\n const adjustment = em.create(SalesOrderAdjustment, {\n id: adjustmentId,\n order,\n orderLine: em.getReference(SalesOrderLine, line.id),\n organizationId: snapshot.organizationId,\n tenantId: snapshot.tenantId,\n scope: 'line',\n kind: 'return',\n rate: '0',\n amountNet: totalNet.toString(),\n amountGross: totalGross.toString(),\n currencyCode: order.currencyCode,\n metadata: { returnId, returnLineId: lineSnapshot.id },\n position: positionStart + index,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n createdAdjustments.push(adjustment)\n em.persist(adjustment)\n\n line.returnedQuantity = (toNumeric(line.returnedQuantity) + lineSnapshot.quantityReturned).toString()\n line.updatedAt = new Date()\n em.persist(line)\n })\n\n const lineSnapshots: SalesLineSnapshot[] = orderLines.map(mapOrderLineEntityToSnapshot)\n const adjustmentDrafts: SalesAdjustmentDraft[] = [...existingAdjustments, ...createdAdjustments].map(\n mapOrderAdjustmentToDraft,\n )\n const calculation = await salesCalculationService.calculateDocumentTotals({\n documentKind: 'order',\n lines: lineSnapshots,\n adjustments: adjustmentDrafts,\n context: buildCalculationContext(order),\n existingTotals: resolveExistingPaymentTotals(order),\n })\n applyOrderTotals(order, calculation.totals, calculation.lines.length)\n order.updatedAt = new Date()\n em.persist(order)\n },\n ],\n { transaction: true },\n )\n\n return createdLines\n}\n\nasync function loadShippedQuantityByLine(\n em: EntityManager,\n orderId: string,\n scope: { tenantId: string; organizationId: string },\n): Promise<Map<string, number>> {\n const shipments = await findWithDecryption(\n em,\n SalesShipment,\n { order: orderId, deletedAt: null },\n {},\n scope,\n )\n const shippedByLine = new Map<string, number>()\n if (!shipments.length) return shippedByLine\n const items = await findWithDecryption(\n em,\n SalesShipmentItem,\n { shipment: { $in: shipments.map((shipment) => shipment.id) } },\n {},\n scope,\n )\n items.forEach((item) => {\n const orderLineId = typeof item.orderLine === 'string' ? item.orderLine : item.orderLine?.id ?? null\n if (!orderLineId) return\n const next = (shippedByLine.get(orderLineId) ?? 0) + coerceShipmentQuantity(item.quantity)\n shippedByLine.set(orderLineId, next)\n })\n return shippedByLine\n}\n\nfunction normalizeLinesInput(lines: ReturnCreateInput['lines']): ReturnLineInput[] {\n const seen = new Set<string>()\n const result: ReturnLineInput[] = []\n for (const line of lines) {\n const orderLineId = line.orderLineId\n if (!orderLineId || seen.has(orderLineId)) continue\n const quantity = toNumeric(line.quantity)\n if (!Number.isFinite(quantity) || quantity <= 0) continue\n seen.add(orderLineId)\n result.push({ orderLineId, quantity })\n }\n return result\n}\n\nconst createReturnCommand: CommandHandler<ReturnCreateInput, { returnId: string }> = {\n id: 'sales.returns.create',\n async execute(rawInput, ctx) {\n const input = returnCreateSchema.parse(rawInput ?? {})\n ensureTenantScope(ctx, input.tenantId)\n ensureOrganizationScope(ctx, input.organizationId)\n\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n const requested = normalizeLinesInput(input.lines)\n if (!requested.length) {\n throw new CrudHttpError(400, { error: translate('sales.returns.linesRequired', 'Select at least one line to return.') })\n }\n\n const salesCalculationService = ctx.container.resolve<SalesCalculationService>('salesCalculationService')\n const { header, createdLines } = await em.transactional(async (tx) => {\n const order = await findOneWithDecryption(\n tx,\n SalesOrder,\n { id: input.orderId, deletedAt: null },\n {},\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n if (!order) {\n throw new CrudHttpError(404, { error: translate('sales.returns.orderMissing', 'Order not found.') })\n }\n ensureSameScope(order, input.organizationId, input.tenantId)\n await enforceSalesDocumentOptimisticLock(ctx, order, SALES_RESOURCE_KIND_ORDER)\n\n const orderLines = await findWithDecryption(\n tx,\n SalesOrderLine,\n { order: order.id, deletedAt: null },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n const lineMap = new Map(orderLines.map((line) => [line.id, line]))\n\n const shippedByLine = await loadShippedQuantityByLine(tx, order.id, {\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n\n requested.forEach(({ orderLineId, quantity }) => {\n const line = lineMap.get(orderLineId)\n if (!line) {\n throw new CrudHttpError(404, { error: translate('sales.returns.lineMissing', 'Order line not found.') })\n }\n const available = computeAvailableReturnQuantity({\n quantity: toNumeric(line.quantity),\n returnedQuantity: toNumeric(line.returnedQuantity),\n shippedQuantity: shippedByLine.get(orderLineId) ?? 0,\n })\n if (quantity - 1e-6 > available) {\n throw new CrudHttpError(400, { error: translate('sales.returns.quantityExceedsShipped', 'Cannot return more than the shipped quantity. Ship the items before recording a return.') })\n }\n })\n\n const existingAdjustments = await findWithDecryption(\n tx,\n SalesOrderAdjustment,\n { order: order.id, deletedAt: null },\n { orderBy: { position: 'asc' } },\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n const positionStart = existingAdjustments.reduce((acc, adj) => Math.max(acc, adj.position ?? 0), 0) + 1\n\n const numberGenerator = new SalesDocumentNumberGenerator(tx)\n const generated = await numberGenerator.generate({\n kind: 'return',\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n const returnId = randomUUID()\n const entity = tx.create(SalesReturn, {\n id: returnId,\n order,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n returnNumber: generated.number,\n reason: input.reason ?? null,\n notes: input.notes ?? null,\n returnedAt: input.returnedAt ?? new Date(),\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n tx.persist(entity)\n\n const createdAdjustments: SalesOrderAdjustment[] = []\n const createdReturnLines: SalesReturnLine[] = []\n requested.forEach((lineInput, index) => {\n const line = lineMap.get(lineInput.orderLineId)\n if (!line) return\n const quantity = lineInput.quantity\n const lineQuantity = Math.max(toNumeric(line.quantity), 0)\n // `total_net_amount = 0` while `total_gross_amount > 0` is not a representable\n // priced state (gross = net * (1 + taxRate) \u21D2 net = 0 \u21D2 gross = 0). When a line\n // carries a positive gross but a zeroed/missing net, reconstruct the net from the\n // line's gross and tax rate so the return credits both sides and the order's net\n // grand total moves in lockstep with gross (#3036). A genuinely free line\n // (gross = 0, e.g. a 100% discount / comp) keeps net 0, so the return is not\n // over-credited at the discount-ignoring unit price (#3521).\n const lineTotalNet = deriveLineNetFromGross(line.totalNetAmount, line.totalGrossAmount, line.taxRate)\n const unitNet = lineQuantity > 0 ? lineTotalNet / lineQuantity : toNumeric(line.unitPriceNet)\n const unitGross = lineQuantity > 0 ? toNumeric(line.totalGrossAmount) / lineQuantity : toNumeric(line.unitPriceGross)\n const totalNet = -round(Math.max(unitNet, 0) * quantity)\n const totalGross = -round(Math.max(unitGross, 0) * quantity)\n\n const returnLineId = randomUUID()\n const returnLine = tx.create(SalesReturnLine, {\n id: returnLineId,\n salesReturn: entity,\n orderLine: tx.getReference(SalesOrderLine, line.id),\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n quantityReturned: quantity.toString(),\n unitPriceNet: round(unitNet).toString(),\n unitPriceGross: round(unitGross).toString(),\n totalNetAmount: totalNet.toString(),\n totalGrossAmount: totalGross.toString(),\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n createdReturnLines.push(returnLine)\n tx.persist(returnLine)\n\n const adjustment = tx.create(SalesOrderAdjustment, {\n id: randomUUID(),\n order,\n orderLine: tx.getReference(SalesOrderLine, line.id),\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n scope: 'line',\n kind: 'return',\n rate: '0',\n amountNet: totalNet.toString(),\n amountGross: totalGross.toString(),\n currencyCode: order.currencyCode,\n metadata: { returnId, returnLineId },\n position: positionStart + index,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n createdAdjustments.push(adjustment)\n tx.persist(adjustment)\n\n line.returnedQuantity = (toNumeric(line.returnedQuantity) + quantity).toString()\n line.updatedAt = new Date()\n tx.persist(line)\n })\n\n const lineSnapshots: SalesLineSnapshot[] = orderLines.map(mapOrderLineEntityToSnapshot)\n const adjustmentDrafts: SalesAdjustmentDraft[] = [...existingAdjustments, ...createdAdjustments].map(mapOrderAdjustmentToDraft)\n const calculation = await salesCalculationService.calculateDocumentTotals({\n documentKind: 'order',\n lines: lineSnapshots,\n adjustments: adjustmentDrafts,\n context: buildCalculationContext(order),\n existingTotals: resolveExistingPaymentTotals(order),\n })\n applyOrderTotals(order, calculation.totals, calculation.lines.length)\n order.updatedAt = new Date()\n tx.persist(order)\n\n await tx.flush()\n\n return { header: entity, createdLines: createdReturnLines }\n })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: header,\n identifiers: { id: header.id, organizationId: header.organizationId, tenantId: header.tenantId },\n indexer: { entityType: E.sales.sales_return },\n events: returnCrudEvents,\n })\n\n if (createdLines.length) {\n await Promise.all(\n createdLines.map((line) =>\n emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: line,\n identifiers: { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n indexer: { entityType: E.sales.sales_return_line },\n }),\n ),\n )\n }\n\n return { returnId: header.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadReturnSnapshot(em, result.returnId)\n },\n buildLog: async ({ result, snapshots }) => {\n const after = snapshots.after as ReturnSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('sales.audit.returns.create', 'Create return'),\n resourceKind: 'sales.return',\n resourceId: result.returnId,\n parentResourceKind: 'sales.order',\n parentResourceId: after.orderId ?? null,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: {\n undo: { after } satisfies ReturnUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ReturnUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const salesCalculationService = ctx.container.resolve<SalesCalculationService>('salesCalculationService')\n await reverseReturnEffects(em, salesCalculationService, after)\n },\n redo: async ({ ctx, logEntry }) => {\n const after = resolveRedoSnapshot<ReturnSnapshot>(logEntry)\n if (!after || !after.id) {\n throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for sales.returns.create' })\n }\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const salesCalculationService = ctx.container.resolve<SalesCalculationService>('salesCalculationService')\n\n const createdLines = await restoreReturnEffects(em, salesCalculationService, after)\n\n const header = await findOneWithDecryption(\n em,\n SalesReturn,\n { id: after.id, deletedAt: null },\n {},\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n if (!header) {\n throw new CrudHttpError(404, { error: 'sales.returns.orderMissing' })\n }\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: header,\n identifiers: { id: header.id, organizationId: header.organizationId, tenantId: header.tenantId },\n indexer: { entityType: E.sales.sales_return },\n events: returnCrudEvents,\n })\n\n if (createdLines.length) {\n await Promise.all(\n createdLines.map((line) =>\n emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: line,\n identifiers: { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n indexer: { entityType: E.sales.sales_return_line },\n }),\n ),\n )\n }\n\n return { returnId: header.id }\n },\n}\n\nconst updateReturnCommand: CommandHandler<ReturnUpdateInput, { returnId: string }> = {\n id: 'sales.returns.update',\n async prepare(rawInput, ctx) {\n const parsed = returnUpdateSchema.parse(rawInput ?? {})\n const em = ctx.container.resolve('em') as EntityManager\n const snapshot = await loadReturnHeaderSnapshot(em, parsed.id)\n if (snapshot) {\n ensureTenantScope(ctx, snapshot.tenantId)\n ensureOrganizationScope(ctx, snapshot.organizationId)\n }\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const input = returnUpdateSchema.parse(rawInput ?? {})\n ensureTenantScope(ctx, input.tenantId)\n ensureOrganizationScope(ctx, input.organizationId)\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n const header = await em.transactional(async (tx) => {\n const entity = await findOneWithDecryption(\n tx,\n SalesReturn,\n { id: input.id, deletedAt: null },\n { populate: ['order'] },\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n if (!entity || !entity.order) {\n throw new CrudHttpError(404, { error: translate('sales.returns.notFound', 'Return not found.') })\n }\n ensureSameScope(entity, input.organizationId, input.tenantId)\n const orderId = typeof entity.order === 'string' ? entity.order : entity.order.id\n if (input.orderId !== orderId) {\n throw new CrudHttpError(400, { error: translate('sales.returns.orderMismatch', 'Return does not belong to this order.') })\n }\n // Lock on the return's own version \u2014 editing header fields (reason / notes /\n // returnedAt) only touches the return, not the order totals.\n await enforceSalesDocumentOptimisticLock(ctx, entity, SALES_RESOURCE_KIND_RETURN)\n\n if (input.reason !== undefined) entity.reason = input.reason.length ? input.reason : null\n if (input.notes !== undefined) entity.notes = input.notes.length ? input.notes : null\n if (input.returnedAt !== undefined) entity.returnedAt = input.returnedAt ?? null\n entity.updatedAt = new Date()\n tx.persist(entity)\n await tx.flush()\n return entity\n })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: header,\n identifiers: { id: header.id, organizationId: header.organizationId, tenantId: header.tenantId },\n indexer: { entityType: E.sales.sales_return },\n events: returnCrudEvents,\n })\n\n return { returnId: header.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadReturnHeaderSnapshot(em, result.returnId)\n },\n buildLog: async ({ snapshots, result }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as ReturnHeaderSnapshot | undefined\n const after = snapshots.after as ReturnHeaderSnapshot | undefined\n return {\n actionLabel: translate('sales.audit.returns.update', 'Update return'),\n resourceKind: 'sales.return',\n resourceId: result.returnId,\n parentResourceKind: 'sales.order',\n parentResourceId: after?.orderId ?? before?.orderId ?? null,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: { before, after } satisfies ReturnHeaderUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ReturnHeaderUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await em.transactional(async (tx) => {\n const entity = await findOneWithDecryption(\n tx,\n SalesReturn,\n { id: before.id, deletedAt: null },\n {},\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!entity) return\n entity.reason = before.reason\n entity.notes = before.notes\n entity.returnedAt = before.returnedAt ? new Date(before.returnedAt) : null\n entity.updatedAt = new Date()\n tx.persist(entity)\n await tx.flush()\n })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n const restored = await findOneWithDecryption(\n em,\n SalesReturn,\n { id: before.id, deletedAt: null },\n {},\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (restored) {\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: restored,\n identifiers: { id: restored.id, organizationId: restored.organizationId, tenantId: restored.tenantId },\n indexer: { entityType: E.sales.sales_return },\n events: returnCrudEvents,\n })\n }\n },\n}\n\nconst deleteReturnCommand: CommandHandler<ReturnDeleteInput, { returnId: string }> = {\n id: 'sales.returns.delete',\n async prepare(rawInput, ctx) {\n const parsed = returnDeleteSchema.parse(rawInput ?? {})\n const em = ctx.container.resolve('em') as EntityManager\n const snapshot = await loadReturnSnapshot(em, parsed.id)\n if (snapshot) {\n ensureTenantScope(ctx, snapshot.tenantId)\n ensureOrganizationScope(ctx, snapshot.organizationId)\n }\n return snapshot ? { before: snapshot } : {}\n },\n async execute(rawInput, ctx) {\n const input = returnDeleteSchema.parse(rawInput ?? {})\n ensureTenantScope(ctx, input.tenantId)\n ensureOrganizationScope(ctx, input.organizationId)\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const salesCalculationService = ctx.container.resolve<SalesCalculationService>('salesCalculationService')\n\n const snapshot = await loadReturnSnapshot(em, input.id)\n if (!snapshot) {\n throw new CrudHttpError(404, { error: translate('sales.returns.notFound', 'Return not found.') })\n }\n ensureSameScope(snapshot, input.organizationId, input.tenantId)\n if (input.orderId !== snapshot.orderId) {\n throw new CrudHttpError(400, { error: translate('sales.returns.orderMismatch', 'Return does not belong to this order.') })\n }\n\n const header = await findOneWithDecryption(\n em,\n SalesReturn,\n { id: input.id, deletedAt: null },\n {},\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n if (!header) {\n throw new CrudHttpError(404, { error: translate('sales.returns.notFound', 'Return not found.') })\n }\n ensureSameScope(header, input.organizationId, input.tenantId)\n // Lock on the return's own version, captured before any mutation.\n await enforceSalesDocumentOptimisticLock(ctx, header, SALES_RESOURCE_KIND_RETURN)\n\n await reverseReturnEffects(em, salesCalculationService, snapshot)\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: header,\n identifiers: { id: snapshot.id, organizationId: snapshot.organizationId, tenantId: snapshot.tenantId },\n indexer: { entityType: E.sales.sales_return },\n events: returnCrudEvents,\n })\n\n if (snapshot.lines.length) {\n await Promise.all(\n snapshot.lines.map((line) =>\n emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: { id: line.id, organizationId: snapshot.organizationId, tenantId: snapshot.tenantId },\n identifiers: { id: line.id, organizationId: snapshot.organizationId, tenantId: snapshot.tenantId },\n indexer: { entityType: E.sales.sales_return_line },\n }),\n ),\n )\n }\n\n return { returnId: snapshot.id }\n },\n buildLog: async ({ snapshots, result }) => {\n const before = snapshots.before as ReturnSnapshot | undefined\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('sales.audit.returns.delete', 'Delete return'),\n resourceKind: 'sales.return',\n resourceId: result.returnId,\n parentResourceKind: 'sales.order',\n parentResourceId: before.orderId ?? null,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n payload: {\n undo: { before } satisfies ReturnDeleteUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ReturnDeleteUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const salesCalculationService = ctx.container.resolve<SalesCalculationService>('salesCalculationService')\n\n const createdLines = await restoreReturnEffects(em, salesCalculationService, before)\n\n const header = await findOneWithDecryption(\n em,\n SalesReturn,\n { id: before.id, deletedAt: null },\n {},\n { tenantId: before.tenantId, organizationId: before.organizationId },\n )\n if (!header) return\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: header,\n identifiers: { id: header.id, organizationId: header.organizationId, tenantId: header.tenantId },\n indexer: { entityType: E.sales.sales_return },\n events: returnCrudEvents,\n })\n\n if (createdLines.length) {\n await Promise.all(\n createdLines.map((line) =>\n emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: line,\n identifiers: { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n indexer: { entityType: E.sales.sales_return_line },\n }),\n ),\n )\n }\n },\n}\n\nregisterCommand(createReturnCommand)\nregisterCommand(updateReturnCommand)\nregisterCommand(deleteReturnCommand)\n\nexport const returnCommands = [createReturnCommand, updateReturnCommand, deleteReturnCommand]\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.6-develop.
|
|
3
|
+
"version": "0.6.6-develop.6429.1.2fba7258d1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -253,16 +253,16 @@
|
|
|
253
253
|
"zod": "^4.4.3"
|
|
254
254
|
},
|
|
255
255
|
"peerDependencies": {
|
|
256
|
-
"@open-mercato/ai-assistant": "0.6.6-develop.
|
|
257
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
258
|
-
"@open-mercato/ui": "0.6.6-develop.
|
|
256
|
+
"@open-mercato/ai-assistant": "0.6.6-develop.6429.1.2fba7258d1",
|
|
257
|
+
"@open-mercato/shared": "0.6.6-develop.6429.1.2fba7258d1",
|
|
258
|
+
"@open-mercato/ui": "0.6.6-develop.6429.1.2fba7258d1",
|
|
259
259
|
"react": "^19.0.0",
|
|
260
260
|
"react-dom": "^19.0.0"
|
|
261
261
|
},
|
|
262
262
|
"devDependencies": {
|
|
263
|
-
"@open-mercato/ai-assistant": "0.6.6-develop.
|
|
264
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
265
|
-
"@open-mercato/ui": "0.6.6-develop.
|
|
263
|
+
"@open-mercato/ai-assistant": "0.6.6-develop.6429.1.2fba7258d1",
|
|
264
|
+
"@open-mercato/shared": "0.6.6-develop.6429.1.2fba7258d1",
|
|
265
|
+
"@open-mercato/ui": "0.6.6-develop.6429.1.2fba7258d1",
|
|
266
266
|
"@testing-library/dom": "^10.4.1",
|
|
267
267
|
"@testing-library/jest-dom": "^6.9.1",
|
|
268
268
|
"@testing-library/react": "^16.3.1",
|
|
@@ -306,7 +306,11 @@ Both link to `/backend/customer_accounts/{sourceEntityId}` for staff review.
|
|
|
306
306
|
|
|
307
307
|
| Spot ID | Widget | Purpose |
|
|
308
308
|
|---------|--------|---------|
|
|
309
|
+
| `customers.person` | `account-status` | Shows portal account status on Customers v2 person detail forms |
|
|
310
|
+
| `crud-form:customers.person` | `account-status` | Alias for CrudForm hosts deriving the spot from `customers.person` |
|
|
309
311
|
| `crud-form:customers:customer_person_profile:fields` | `account-status` | Shows portal account status on CRM person detail page |
|
|
312
|
+
| `customers.company` | `company-users` | Shows portal users on Customers v2 company detail forms |
|
|
313
|
+
| `crud-form:customers.company` | `company-users` | Alias for CrudForm hosts deriving the spot from `customers.company` |
|
|
310
314
|
| `crud-form:customers:customer_company_profile:fields` | `company-users` | Shows portal users linked to a CRM company |
|
|
311
315
|
|
|
312
316
|
Both inject as column 2 groups with priority 200, gated by `customer_accounts.view` feature.
|
|
@@ -115,9 +115,7 @@ function InviteForm({ personEntityId, onSuccess }: { personEntityId: string; onS
|
|
|
115
115
|
)
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
async function handleSubmit(
|
|
119
|
-
event.preventDefault()
|
|
120
|
-
|
|
118
|
+
async function handleSubmit() {
|
|
121
119
|
const trimmedEmail = email.trim()
|
|
122
120
|
if (!trimmedEmail) {
|
|
123
121
|
flash(t('customer_accounts.widgets.invite.error.emailRequired', 'Email is required'), 'error')
|
|
@@ -177,7 +175,7 @@ function InviteForm({ personEntityId, onSuccess }: { personEntityId: string; onS
|
|
|
177
175
|
}
|
|
178
176
|
|
|
179
177
|
return (
|
|
180
|
-
<
|
|
178
|
+
<div className="space-y-3 mt-2">
|
|
181
179
|
<div>
|
|
182
180
|
<label htmlFor="invite-email" className="block text-xs font-medium text-muted-foreground mb-1">
|
|
183
181
|
{t('common.email', 'Email')}
|
|
@@ -235,8 +233,9 @@ function InviteForm({ personEntityId, onSuccess }: { personEntityId: string; onS
|
|
|
235
233
|
|
|
236
234
|
<div className="flex justify-end gap-2 pt-1">
|
|
237
235
|
<Button
|
|
238
|
-
type="
|
|
236
|
+
type="button"
|
|
239
237
|
size="sm"
|
|
238
|
+
onClick={handleSubmit}
|
|
240
239
|
disabled={isSubmitting || !email.trim() || selectedRoleIds.length === 0}
|
|
241
240
|
>
|
|
242
241
|
{isSubmitting
|
|
@@ -244,7 +243,7 @@ function InviteForm({ personEntityId, onSuccess }: { personEntityId: string; onS
|
|
|
244
243
|
: t('customer_accounts.widgets.invite.submit', 'Send Invitation')}
|
|
245
244
|
</Button>
|
|
246
245
|
</div>
|
|
247
|
-
</
|
|
246
|
+
</div>
|
|
248
247
|
)
|
|
249
248
|
}
|
|
250
249
|
|
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
import type { ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'
|
|
2
2
|
|
|
3
|
+
const accountStatusWidget = {
|
|
4
|
+
widgetId: 'customer_accounts.injection.account-status',
|
|
5
|
+
kind: 'group',
|
|
6
|
+
column: 2,
|
|
7
|
+
groupLabel: 'customer_accounts.widgets.accountStatus',
|
|
8
|
+
priority: 200,
|
|
9
|
+
} as const
|
|
10
|
+
|
|
11
|
+
const companyUsersWidget = {
|
|
12
|
+
widgetId: 'customer_accounts.injection.company-users',
|
|
13
|
+
kind: 'group',
|
|
14
|
+
column: 2,
|
|
15
|
+
groupLabel: 'customer_accounts.widgets.portalUsers',
|
|
16
|
+
priority: 200,
|
|
17
|
+
} as const
|
|
18
|
+
|
|
3
19
|
export const injectionTable: ModuleInjectionTable = {
|
|
4
|
-
'
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
priority: 200,
|
|
11
|
-
},
|
|
12
|
-
],
|
|
13
|
-
'crud-form:customers:customer_company_profile:fields': [
|
|
14
|
-
{
|
|
15
|
-
widgetId: 'customer_accounts.injection.company-users',
|
|
16
|
-
kind: 'group',
|
|
17
|
-
column: 2,
|
|
18
|
-
groupLabel: 'customer_accounts.widgets.portalUsers',
|
|
19
|
-
priority: 200,
|
|
20
|
-
},
|
|
21
|
-
],
|
|
20
|
+
'customers.person': [accountStatusWidget],
|
|
21
|
+
'crud-form:customers.person': [accountStatusWidget],
|
|
22
|
+
'crud-form:customers:customer_person_profile:fields': [accountStatusWidget],
|
|
23
|
+
'customers.company': [companyUsersWidget],
|
|
24
|
+
'crud-form:customers.company': [companyUsersWidget],
|
|
25
|
+
'crud-form:customers:customer_company_profile:fields': [companyUsersWidget],
|
|
22
26
|
// Step 4.10 — Portal AiChat injection example.
|
|
23
27
|
// Mapped to the portal profile page's `pageAfter('profile')` spot;
|
|
24
28
|
// third-party modules targeting other portal pages can copy this entry.
|
|
@@ -78,6 +78,22 @@ function round(value: number): number {
|
|
|
78
78
|
return Math.round((value + Number.EPSILON) * 1e4) / 1e4
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Payment totals live on the order, not in the line/adjustment math a return
|
|
83
|
+
* recalculates. Every `calculateDocumentTotals` call that writes order totals
|
|
84
|
+
* back to the order MUST seed these so `buildBaseDocumentResult` preserves the
|
|
85
|
+
* recorded `paidTotalAmount` / `refundedTotalAmount` instead of defaulting them
|
|
86
|
+
* to 0 — otherwise creating/undoing/redoing a return silently zeroes the paid
|
|
87
|
+
* amount and the order's outstanding balance goes wrong on any paid order
|
|
88
|
+
* (#3756). Mirrors `resolveExistingPaymentTotals` in `commands/documents.ts`.
|
|
89
|
+
*/
|
|
90
|
+
function resolveExistingPaymentTotals(order: SalesOrder): { paidTotalAmount: number; refundedTotalAmount: number } {
|
|
91
|
+
return {
|
|
92
|
+
paidTotalAmount: toNumeric(order.paidTotalAmount),
|
|
93
|
+
refundedTotalAmount: toNumeric(order.refundedTotalAmount),
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
81
97
|
function applyOrderTotals(order: SalesOrder, totals: SalesDocumentCalculationResult['totals'], lineCount: number): void {
|
|
82
98
|
order.subtotalNetAmount = toNumericString(totals.subtotalNetAmount) ?? '0'
|
|
83
99
|
order.subtotalGrossAmount = toNumericString(totals.subtotalGrossAmount) ?? '0'
|
|
@@ -194,10 +210,7 @@ export async function recalculateOrderTotalsForDisplay(
|
|
|
194
210
|
lines: lineSnapshots,
|
|
195
211
|
adjustments: adjustmentDrafts,
|
|
196
212
|
context: buildCalculationContext(order),
|
|
197
|
-
existingTotals:
|
|
198
|
-
paidTotalAmount: toNumeric(order.paidTotalAmount),
|
|
199
|
-
refundedTotalAmount: toNumeric(order.refundedTotalAmount),
|
|
200
|
-
},
|
|
213
|
+
existingTotals: resolveExistingPaymentTotals(order),
|
|
201
214
|
})
|
|
202
215
|
return calculation.totals
|
|
203
216
|
}
|
|
@@ -382,6 +395,7 @@ async function reverseReturnEffects(
|
|
|
382
395
|
lines: lineSnapshots,
|
|
383
396
|
adjustments: adjustmentDrafts,
|
|
384
397
|
context: buildCalculationContext(order),
|
|
398
|
+
existingTotals: resolveExistingPaymentTotals(order),
|
|
385
399
|
})
|
|
386
400
|
applyOrderTotals(order, calculation.totals, calculation.lines.length)
|
|
387
401
|
order.updatedAt = new Date()
|
|
@@ -531,6 +545,7 @@ async function restoreReturnEffects(
|
|
|
531
545
|
lines: lineSnapshots,
|
|
532
546
|
adjustments: adjustmentDrafts,
|
|
533
547
|
context: buildCalculationContext(order),
|
|
548
|
+
existingTotals: resolveExistingPaymentTotals(order),
|
|
534
549
|
})
|
|
535
550
|
applyOrderTotals(order, calculation.totals, calculation.lines.length)
|
|
536
551
|
order.updatedAt = new Date()
|
|
@@ -746,6 +761,7 @@ const createReturnCommand: CommandHandler<ReturnCreateInput, { returnId: string
|
|
|
746
761
|
lines: lineSnapshots,
|
|
747
762
|
adjustments: adjustmentDrafts,
|
|
748
763
|
context: buildCalculationContext(order),
|
|
764
|
+
existingTotals: resolveExistingPaymentTotals(order),
|
|
749
765
|
})
|
|
750
766
|
applyOrderTotals(order, calculation.totals, calculation.lines.length)
|
|
751
767
|
order.updatedAt = new Date()
|