@open-mercato/core 0.6.8-develop.7043.1.b0199a62c6 → 0.6.8-develop.7047.1.32ab5fcb07
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/helpers/integration/api.js +3 -1
- package/dist/helpers/integration/api.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/domain-mappings.js +9 -0
- package/dist/modules/customer_accounts/api/admin/domain-mappings.js.map +2 -2
- package/dist/modules/customer_accounts/services/domainMappingService.js +10 -0
- package/dist/modules/customer_accounts/services/domainMappingService.js.map +2 -2
- package/dist/modules/sales/api/documents/factory.js +0 -35
- package/dist/modules/sales/api/documents/factory.js.map +2 -2
- package/dist/modules/sales/commands/returns.js.map +2 -2
- package/package.json +7 -7
- package/src/helpers/integration/api.ts +13 -9
- package/src/modules/customer_accounts/api/admin/domain-mappings.ts +7 -0
- package/src/modules/customer_accounts/services/domainMappingService.ts +10 -0
- package/src/modules/sales/api/documents/factory.ts +0 -41
- package/src/modules/sales/commands/returns.ts +4 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.8-develop.
|
|
3
|
+
"version": "0.6.8-develop.7047.1.32ab5fcb07",
|
|
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.
|
|
256
|
-
"@open-mercato/shared": "0.6.8-develop.
|
|
257
|
-
"@open-mercato/ui": "0.6.8-develop.
|
|
255
|
+
"@open-mercato/ai-assistant": "0.6.8-develop.7047.1.32ab5fcb07",
|
|
256
|
+
"@open-mercato/shared": "0.6.8-develop.7047.1.32ab5fcb07",
|
|
257
|
+
"@open-mercato/ui": "0.6.8-develop.7047.1.32ab5fcb07",
|
|
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.
|
|
263
|
-
"@open-mercato/shared": "0.6.8-develop.
|
|
264
|
-
"@open-mercato/ui": "0.6.8-develop.
|
|
262
|
+
"@open-mercato/ai-assistant": "0.6.8-develop.7047.1.32ab5fcb07",
|
|
263
|
+
"@open-mercato/shared": "0.6.8-develop.7047.1.32ab5fcb07",
|
|
264
|
+
"@open-mercato/ui": "0.6.8-develop.7047.1.32ab5fcb07",
|
|
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",
|
|
@@ -148,21 +148,25 @@ export async function postForm(
|
|
|
148
148
|
* The `request` fixture keeps a cookie jar, and `/api/auth/login` sets `auth_token`
|
|
149
149
|
* on it. Every later call through that fixture therefore carries the LAST logged-in
|
|
150
150
|
* user's session, even one that deliberately sends no Authorization header or an
|
|
151
|
-
* `ApiKey` one.
|
|
152
|
-
* the login route marks it `secure` only when `NODE_ENV === 'production'`, so a lane
|
|
153
|
-
* that serves the app with any other NODE_ENV over http keeps it while a production
|
|
154
|
-
* lane silently drops it.
|
|
151
|
+
* `ApiKey` one.
|
|
155
152
|
*
|
|
156
153
|
* A spec asserting "no credentials are rejected" or "this API key alone decides
|
|
157
|
-
* access" must
|
|
158
|
-
*
|
|
159
|
-
* both failed in the standalone lane
|
|
160
|
-
* ephemeral one
|
|
154
|
+
* access" must therefore issue the request from a jar that never saw a login,
|
|
155
|
+
* rather than assuming the fixture's jar is empty. TC-DOCUMENTS-009 and
|
|
156
|
+
* TC-DOCUMENTS-018 both observably failed in the standalone lane — with the
|
|
157
|
+
* replayed cookie visible in the trace — while passing in the ephemeral one, and
|
|
158
|
+
* routing them through this helper fixed both.
|
|
161
159
|
*/
|
|
162
160
|
export async function withCredentialIsolatedRequest<T>(
|
|
163
161
|
use: (request: APIRequestContext) => Promise<T>,
|
|
164
162
|
): Promise<T> {
|
|
165
|
-
|
|
163
|
+
// A hand-built context does not inherit the project's `use.baseURL` the way the
|
|
164
|
+
// `request` fixture does, so it has to repeat the config's own resolution — otherwise
|
|
165
|
+
// a relative path throws here on any run that leaves BASE_URL unset, while the same
|
|
166
|
+
// path works through the fixture.
|
|
167
|
+
const context = await playwrightRequest.newContext({
|
|
168
|
+
baseURL: BASE_URL ?? 'http://localhost:3000',
|
|
169
|
+
});
|
|
166
170
|
try {
|
|
167
171
|
return await use(context);
|
|
168
172
|
} finally {
|
|
@@ -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)
|
|
202
|
-
*
|
|
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,
|