@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.
@@ -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(event) {
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("form", { onSubmit: handleSubmit, className: "space-y-3 mt-2", children: [
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: "submit",
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
  }
@@ -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": "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",
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
- "crud-form:customers:customer_person_profile:fields": [
3
- {
4
- widgetId: "customer_accounts.injection.account-status",
5
- kind: "group",
6
- column: 2,
7
- groupLabel: "customer_accounts.widgets.accountStatus",
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\nexport const injectionTable: ModuleInjectionTable = {\n 'crud-form:customers:customer_person_profile:fields': [\n {\n widgetId: 'customer_accounts.injection.account-status',\n kind: 'group',\n column: 2,\n groupLabel: 'customer_accounts.widgets.accountStatus',\n priority: 200,\n },\n ],\n 'crud-form:customers:customer_company_profile:fields': [\n {\n widgetId: 'customer_accounts.injection.company-users',\n kind: 'group',\n column: 2,\n groupLabel: 'customer_accounts.widgets.portalUsers',\n priority: 200,\n },\n ],\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": "AAEO,MAAM,iBAAuC;AAAA,EAClD,sDAAsD;AAAA,IACpD;AAAA,MACE,UAAU;AAAA,MACV,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,uDAAuD;AAAA,IACrD;AAAA,MACE,UAAU;AAAA,MACV,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,UAAU;AAAA,IACZ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAIA,wBAAwB;AAAA,IACtB;AAAA,MACE,UAAU;AAAA,MACV,UAAU;AAAA,IACZ;AAAA,EACF;AACF;AAEA,IAAO,0BAAQ;",
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.6423.1.f039f73ff4",
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.6423.1.f039f73ff4",
257
- "@open-mercato/shared": "0.6.6-develop.6423.1.f039f73ff4",
258
- "@open-mercato/ui": "0.6.6-develop.6423.1.f039f73ff4",
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.6423.1.f039f73ff4",
264
- "@open-mercato/shared": "0.6.6-develop.6423.1.f039f73ff4",
265
- "@open-mercato/ui": "0.6.6-develop.6423.1.f039f73ff4",
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(event: React.FormEvent) {
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
- <form onSubmit={handleSubmit} className="space-y-3 mt-2">
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="submit"
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
- </form>
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
- 'crud-form:customers:customer_person_profile:fields': [
5
- {
6
- widgetId: 'customer_accounts.injection.account-status',
7
- kind: 'group',
8
- column: 2,
9
- groupLabel: 'customer_accounts.widgets.accountStatus',
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()