@open-mercato/core 0.6.8-develop.7042.1.413df59570 → 0.6.8-develop.7046.1.153faed87a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.6.8-develop.7042.1.413df59570",
3
+ "version": "0.6.8-develop.7046.1.153faed87a",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -252,16 +252,16 @@
252
252
  "zod": "^4.4.3"
253
253
  },
254
254
  "peerDependencies": {
255
- "@open-mercato/ai-assistant": "0.6.8-develop.7042.1.413df59570",
256
- "@open-mercato/shared": "0.6.8-develop.7042.1.413df59570",
257
- "@open-mercato/ui": "0.6.8-develop.7042.1.413df59570",
255
+ "@open-mercato/ai-assistant": "0.6.8-develop.7046.1.153faed87a",
256
+ "@open-mercato/shared": "0.6.8-develop.7046.1.153faed87a",
257
+ "@open-mercato/ui": "0.6.8-develop.7046.1.153faed87a",
258
258
  "react": "^19.0.0",
259
259
  "react-dom": "^19.0.0"
260
260
  },
261
261
  "devDependencies": {
262
- "@open-mercato/ai-assistant": "0.6.8-develop.7042.1.413df59570",
263
- "@open-mercato/shared": "0.6.8-develop.7042.1.413df59570",
264
- "@open-mercato/ui": "0.6.8-develop.7042.1.413df59570",
262
+ "@open-mercato/ai-assistant": "0.6.8-develop.7046.1.153faed87a",
263
+ "@open-mercato/shared": "0.6.8-develop.7046.1.153faed87a",
264
+ "@open-mercato/ui": "0.6.8-develop.7046.1.153faed87a",
265
265
  "@testing-library/dom": "^10.4.1",
266
266
  "@testing-library/jest-dom": "^7.0.0",
267
267
  "@testing-library/react": "^16.3.1",
@@ -33,9 +33,9 @@ export function renderValue(value: unknown, fallback: string) {
33
33
  if (typeof value === 'boolean') return <span>{value ? 'true' : 'false'}</span>
34
34
  if (typeof value === 'number' || typeof value === 'bigint') return <span>{String(value)}</span>
35
35
  if (value instanceof Date) return <span>{value.toISOString()}</span>
36
- if (typeof value === 'string') return <span className="break-words">{value}</span>
36
+ if (typeof value === 'string') return <span className="wrap-anywhere">{value}</span>
37
37
  return (
38
- <pre className="max-h-40 overflow-y-auto whitespace-pre-wrap break-words rounded-md bg-muted/50 px-2 py-1 text-xs leading-5 text-muted-foreground">
38
+ <pre className="max-h-40 overflow-y-auto whitespace-pre-wrap wrap-anywhere rounded-md bg-muted/50 px-2 py-1 text-xs leading-5 text-muted-foreground">
39
39
  {safeStringify(value)}
40
40
  </pre>
41
41
  )
@@ -76,43 +76,53 @@ export type ChangedFieldsTableProps = {
76
76
  }
77
77
 
78
78
  export function ChangedFieldsTable({ changeRows, noneLabel, t, beforeLabel, afterLabel }: ChangedFieldsTableProps) {
79
+ const beforeHeading = beforeLabel ?? t('audit_logs.actions.details.before')
80
+ const afterHeading = afterLabel ?? t('audit_logs.actions.details.after')
79
81
  return (
80
82
  <section>
81
83
  <h3 className="text-sm font-semibold">
82
84
  {t('audit_logs.actions.details.changed_fields')}
83
85
  </h3>
84
86
  {changeRows.length ? (
85
- <div className="mt-2 overflow-x-auto rounded-lg border">
86
- <table className="min-w-full divide-y text-sm">
87
- <thead className="bg-muted/50">
88
- <tr>
89
- <th scope="col" className="px-4 py-2 text-left font-medium text-muted-foreground">
90
- {t('audit_logs.actions.details.field')}
91
- </th>
92
- <th scope="col" className="px-4 py-2 text-left font-medium text-muted-foreground">
93
- {beforeLabel ?? t('audit_logs.actions.details.before')}
94
- </th>
95
- <th scope="col" className="px-4 py-2 text-left font-medium text-muted-foreground">
96
- {afterLabel ?? t('audit_logs.actions.details.after')}
97
- </th>
98
- </tr>
99
- </thead>
100
- <tbody className="divide-y">
101
- {changeRows.map((row) => (
102
- <tr key={row.field} className="align-top">
103
- <td className="px-4 py-2 align-top font-medium">
104
- {humanizeField(normalizeChangeField(row.field))}
105
- </td>
106
- <td className="px-4 py-2">
107
- {renderValue(row.from, noneLabel)}
108
- </td>
109
- <td className="px-4 py-2">
110
- {renderValue(row.to, noneLabel)}
111
- </td>
87
+ <div className="@container/changes mt-2">
88
+ <div className="overflow-x-auto rounded-lg border">
89
+ <table className="block w-full divide-y text-sm @lg/changes:table @lg/changes:min-w-full">
90
+ <thead className="hidden bg-muted/50 @lg/changes:table-header-group">
91
+ <tr>
92
+ <th scope="col" className="px-4 py-2 text-left font-medium text-muted-foreground">
93
+ {t('audit_logs.actions.details.field')}
94
+ </th>
95
+ <th scope="col" className="px-4 py-2 text-left font-medium text-muted-foreground">
96
+ {beforeHeading}
97
+ </th>
98
+ <th scope="col" className="px-4 py-2 text-left font-medium text-muted-foreground">
99
+ {afterHeading}
100
+ </th>
112
101
  </tr>
113
- ))}
114
- </tbody>
115
- </table>
102
+ </thead>
103
+ <tbody className="block divide-y @lg/changes:table-row-group">
104
+ {changeRows.map((row) => (
105
+ <tr key={row.field} className="block px-4 py-3 align-top @lg/changes:table-row @lg/changes:p-0">
106
+ <td className="block font-medium @lg/changes:table-cell @lg/changes:px-4 @lg/changes:py-2 @lg/changes:align-top">
107
+ {humanizeField(normalizeChangeField(row.field))}
108
+ </td>
109
+ <td className="mt-2 block @lg/changes:mt-0 @lg/changes:table-cell @lg/changes:px-4 @lg/changes:py-2">
110
+ <span className="mb-0.5 block text-xs font-medium uppercase tracking-wide text-muted-foreground @lg/changes:hidden">
111
+ {beforeHeading}
112
+ </span>
113
+ {renderValue(row.from, noneLabel)}
114
+ </td>
115
+ <td className="mt-2 block @lg/changes:mt-0 @lg/changes:table-cell @lg/changes:px-4 @lg/changes:py-2">
116
+ <span className="mb-0.5 block text-xs font-medium uppercase tracking-wide text-muted-foreground @lg/changes:hidden">
117
+ {afterHeading}
118
+ </span>
119
+ {renderValue(row.to, noneLabel)}
120
+ </td>
121
+ </tr>
122
+ ))}
123
+ </tbody>
124
+ </table>
125
+ </div>
116
126
  </div>
117
127
  ) : (
118
128
  <p className="mt-2 text-sm text-muted-foreground">
@@ -14,6 +14,7 @@ import {
14
14
  import { registerDomainSchema } from '@open-mercato/core/modules/customer_accounts/data/validators'
15
15
  import {
16
16
  DomainMappingService,
17
+ DomainMappingOrgScopeError,
17
18
  type ResolveResult,
18
19
  } from '@open-mercato/core/modules/customer_accounts/services/domainMappingService'
19
20
  import { DomainMapping } from '@open-mercato/core/modules/customer_accounts/data/entities'
@@ -142,6 +143,12 @@ export async function POST(req: Request) {
142
143
  { status: 409 },
143
144
  )
144
145
  }
146
+ if (err instanceof DomainMappingOrgScopeError) {
147
+ return NextResponse.json(
148
+ { ok: false, error: 'Organization was not found in the current tenant.' },
149
+ { status: 400 },
150
+ )
151
+ }
145
152
  const message = err instanceof Error ? err.message : 'Failed to register domain'
146
153
  return NextResponse.json({ ok: false, error: message }, { status: 500 })
147
154
  }
@@ -9,9 +9,17 @@ import {
9
9
  import { Organization } from '@open-mercato/core/modules/directory/data/entities'
10
10
  import { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'
11
11
  import { normalizeHostname, tryNormalizeHostname } from '@open-mercato/core/modules/customer_accounts/lib/hostname'
12
+ import { findOrganizationInTenant } from '@open-mercato/core/modules/customer_accounts/lib/organizationLookup'
12
13
  import { platformDomains } from '@open-mercato/core/modules/customer_accounts/lib/platformDomains'
13
14
  import { detectProxy, isInKnownProxyRange } from '@open-mercato/core/modules/customer_accounts/lib/proxyRanges'
14
15
 
16
+ export class DomainMappingOrgScopeError extends Error {
17
+ constructor(organizationId: string) {
18
+ super(`[internal] organizationId ${organizationId} does not belong to the caller's tenant`)
19
+ this.name = 'DomainMappingOrgScopeError'
20
+ }
21
+ }
22
+
15
23
  const DOMAIN_ROUTING_TAG = 'domain_routing'
16
24
  const RESOLVE_KEY_PREFIX = 'domain_routing:resolve'
17
25
  const ACTIVE_BY_ORG_KEY_PREFIX = 'domain_routing:active-by-org'
@@ -269,6 +277,8 @@ export class DomainMappingService {
269
277
 
270
278
  async register(input: RegisterInput): Promise<DomainMapping> {
271
279
  const hostname = normalizeHostname(input.hostname)
280
+ const organization = await findOrganizationInTenant(this.em, input.organizationId, input.tenantId)
281
+ if (!organization) throw new DomainMappingOrgScopeError(input.organizationId)
272
282
 
273
283
  let replacesDomain: DomainMapping | null = null
274
284
  if (input.replacesDomainId) {
@@ -26,7 +26,6 @@ import { buildIlikeTerm } from '@open-mercato/shared/lib/db/buildIlikeTerm'
26
26
  import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'
27
27
  import { parseIdsParam } from '@open-mercato/shared/lib/crud/ids'
28
28
  import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
29
- import { recalculateOrderTotalsForDisplay } from '../../commands/returns'
30
29
  import { parseDecryptedFieldValue } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'
31
30
 
32
31
  type DocumentKind = 'order' | 'quote'
@@ -616,46 +615,6 @@ export function buildDocumentCrudOptions(binding: DocumentBinding) {
616
615
  afterList: async (payload: any, ctx: CrudCtx) => {
617
616
  await attachTags(payload, { ...ctx, bindingKind: binding.kind })
618
617
  await attachChannelNames(payload, ctx)
619
- if (binding.kind === 'order' && Array.isArray(payload?.items) && payload.items.length === 1) {
620
- const item = payload.items[0] as Record<string, unknown>
621
- const orderId = typeof item?.id === 'string' ? item.id : null
622
- const tenantId = typeof item?.tenantId === 'string' ? item.tenantId : ctx?.auth?.tenantId ?? null
623
- const organizationId =
624
- typeof item?.organizationId === 'string' ? item.organizationId : ctx?.selectedOrganizationId ?? ctx?.auth?.orgId ?? null
625
- if (orderId && tenantId && organizationId) {
626
- const requestEm = ctx?.container?.resolve?.('em') as import('@mikro-orm/postgresql').EntityManager | undefined
627
- // Display-only totals recalculation: run on a forked EntityManager so
628
- // the order/line/adjustment entities loaded here never enter the
629
- // request's Unit of Work. This guarantees a GET can never flush an
630
- // UPDATE (and thus never advance `updated_at`), which would otherwise
631
- // surface as a spurious optimistic-lock 409 in another tab.
632
- const em = requestEm?.fork()
633
- if (em) {
634
- const totals = await recalculateOrderTotalsForDisplay(
635
- em,
636
- ctx.container,
637
- orderId,
638
- { tenantId, organizationId },
639
- )
640
- if (totals) {
641
- Object.assign(item, {
642
- subtotalNetAmount: totals.subtotalNetAmount,
643
- subtotalGrossAmount: totals.subtotalGrossAmount,
644
- discountTotalAmount: totals.discountTotalAmount,
645
- taxTotalAmount: totals.taxTotalAmount,
646
- shippingNetAmount: totals.shippingNetAmount,
647
- shippingGrossAmount: totals.shippingGrossAmount,
648
- surchargeTotalAmount: totals.surchargeTotalAmount,
649
- grandTotalNetAmount: totals.grandTotalNetAmount,
650
- grandTotalGrossAmount: totals.grandTotalGrossAmount,
651
- paidTotalAmount: totals.paidTotalAmount,
652
- refundedTotalAmount: totals.refundedTotalAmount,
653
- outstandingAmount: totals.outstandingAmount,
654
- })
655
- }
656
- }
657
- }
658
- }
659
618
  },
660
619
  },
661
620
  }
@@ -198,8 +198,10 @@ function buildCalculationContext(order: SalesOrder) {
198
198
  }
199
199
 
200
200
  /**
201
- * Recalculates order totals (including line-scoped return adjustments) for display.
202
- * Returns the totals object to merge into an order API response, or null if order not found.
201
+ * Recalculates order totals (including line-scoped return adjustments) from lines and
202
+ * adjustments. Do not merge this into GET /api/sales/orders responses: provider
203
+ * calculators re-emit shipping/payment fees on top of already-persisted adjustments
204
+ * and make list vs detail totals diverge (#5438). Persist via commands instead.
203
205
  */
204
206
  export async function recalculateOrderTotalsForDisplay(
205
207
  em: EntityManager,