@open-mercato/core 0.10.1-develop.7841.1.af87ac614d → 0.10.1-develop.7857.1.259928eb85
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/.turbo/turbo-build.log +1 -1
- package/dist/modules/api_keys/api/keys/route.js +15 -1
- package/dist/modules/api_keys/api/keys/route.js.map +2 -2
- package/dist/modules/attachments/api/file/[id]/route.js +6 -1
- package/dist/modules/attachments/api/file/[id]/route.js.map +2 -2
- package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +6 -1
- package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
- package/dist/modules/attachments/api/library/[id]/route.js +16 -1
- package/dist/modules/attachments/api/library/[id]/route.js.map +2 -2
- package/dist/modules/attachments/api/route.js +11 -3
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/cli.js +2 -0
- package/dist/modules/attachments/cli.js.map +2 -2
- package/dist/modules/attachments/data/validators.js +11 -0
- package/dist/modules/attachments/data/validators.js.map +2 -2
- package/dist/modules/attachments/index.js +10 -0
- package/dist/modules/attachments/index.js.map +2 -2
- package/dist/modules/attachments/lib/access-query.js +5 -1
- package/dist/modules/attachments/lib/access-query.js.map +2 -2
- package/dist/modules/attachments/lib/createFromBuffer.js +2 -0
- package/dist/modules/attachments/lib/createFromBuffer.js.map +2 -2
- package/dist/modules/attachments/lib/drivers/driverFactory.js +54 -33
- package/dist/modules/attachments/lib/drivers/driverFactory.js.map +2 -2
- package/dist/modules/attachments/lib/drivers/index.js +11 -1
- package/dist/modules/attachments/lib/drivers/index.js.map +2 -2
- package/dist/modules/attachments/lib/drivers/storageValidation.js +85 -0
- package/dist/modules/attachments/lib/drivers/storageValidation.js.map +7 -0
- package/dist/modules/attachments/lib/scoped-upload-service.js +3 -1
- package/dist/modules/attachments/lib/scoped-upload-service.js.map +2 -2
- package/dist/modules/attachments/lib/storageErrors.js +22 -0
- package/dist/modules/attachments/lib/storageErrors.js.map +7 -0
- package/dist/modules/auth/api/users/route.js +16 -1
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/users/page.js +13 -2
- package/dist/modules/auth/backend/users/page.js.map +2 -2
- package/dist/modules/catalog/commands/products.js +4 -2
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/route.js +4 -9
- package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/route.js +4 -9
- package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
- package/dist/modules/customers/components/detail/TaskDialog.js +2 -0
- package/dist/modules/customers/components/detail/TaskDialog.js.map +2 -2
- package/dist/modules/customers/lib/todoCompatibility.js +23 -0
- package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
- package/dist/modules/directory/api/organization-branding/route.js +6 -9
- package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
- package/dist/modules/directory/backend/directory/branding/page.js +15 -3
- package/dist/modules/directory/backend/directory/branding/page.js.map +2 -2
- package/dist/modules/wms/api/listEnrichers.js +3 -1
- package/dist/modules/wms/api/listEnrichers.js.map +2 -2
- package/dist/modules/wms/index.js +1 -1
- package/dist/modules/wms/index.js.map +2 -2
- package/jest.config.cjs +1 -1
- package/package.json +8 -8
- package/src/modules/api_keys/api/keys/route.ts +15 -1
- package/src/modules/api_keys/i18n/de.json +1 -0
- package/src/modules/api_keys/i18n/en.json +1 -0
- package/src/modules/api_keys/i18n/es.json +1 -0
- package/src/modules/api_keys/i18n/ko.json +1 -0
- package/src/modules/api_keys/i18n/pl.json +1 -0
- package/src/modules/attachments/api/file/[id]/route.ts +7 -1
- package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +7 -1
- package/src/modules/attachments/api/library/[id]/route.ts +14 -1
- package/src/modules/attachments/api/route.ts +11 -3
- package/src/modules/attachments/cli.ts +2 -0
- package/src/modules/attachments/data/validators.ts +11 -0
- package/src/modules/attachments/i18n/de.json +1 -0
- package/src/modules/attachments/i18n/en.json +1 -0
- package/src/modules/attachments/i18n/es.json +1 -0
- package/src/modules/attachments/i18n/ko.json +1 -0
- package/src/modules/attachments/i18n/pl.json +1 -0
- package/src/modules/attachments/index.ts +12 -0
- package/src/modules/attachments/lib/access-query.ts +5 -1
- package/src/modules/attachments/lib/createFromBuffer.ts +2 -0
- package/src/modules/attachments/lib/drivers/driverFactory.ts +79 -55
- package/src/modules/attachments/lib/drivers/index.ts +13 -0
- package/src/modules/attachments/lib/drivers/storageValidation.ts +114 -0
- package/src/modules/attachments/lib/scoped-upload-service.ts +3 -1
- package/src/modules/attachments/lib/storageErrors.ts +21 -0
- package/src/modules/auth/api/users/route.ts +18 -1
- package/src/modules/auth/backend/users/page.tsx +14 -2
- package/src/modules/auth/i18n/de.json +1 -0
- package/src/modules/auth/i18n/en.json +1 -0
- package/src/modules/auth/i18n/es.json +1 -0
- package/src/modules/auth/i18n/ko.json +1 -0
- package/src/modules/auth/i18n/pl.json +1 -0
- package/src/modules/catalog/commands/products.ts +4 -1
- package/src/modules/customer_accounts/AGENTS.md +1 -1
- package/src/modules/customers/api/companies/[id]/route.ts +6 -13
- package/src/modules/customers/api/people/[id]/route.ts +6 -13
- package/src/modules/customers/components/detail/TaskDialog.tsx +2 -0
- package/src/modules/customers/lib/todoCompatibility.ts +28 -0
- package/src/modules/directory/api/organization-branding/route.ts +5 -8
- package/src/modules/directory/backend/directory/branding/page.tsx +15 -3
- package/src/modules/sales/AGENTS.md +3 -3
- package/src/modules/wms/api/listEnrichers.ts +5 -2
- package/src/modules/wms/index.ts +1 -1
|
@@ -17,7 +17,7 @@ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
|
17
17
|
import { Organization } from '@open-mercato/core/modules/directory/data/entities'
|
|
18
18
|
import { organizationUpdateSchema } from '@open-mercato/core/modules/directory/data/validators'
|
|
19
19
|
import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
|
|
20
|
-
import {
|
|
20
|
+
import { isExplicitlyEmptyOrganizationScope } from '@open-mercato/shared/lib/auth/organizationScope'
|
|
21
21
|
import '@open-mercato/core/modules/directory/commands/organizations'
|
|
22
22
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
23
23
|
import { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'
|
|
@@ -82,15 +82,12 @@ async function resolveCurrentOrganization(req: Request) {
|
|
|
82
82
|
|
|
83
83
|
const container = await createRequestContainer()
|
|
84
84
|
const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
} catch (error) {
|
|
89
|
-
if (isCrudHttpError(error)) {
|
|
90
|
-
return { response: NextResponse.json(error.body, { status: error.status }) }
|
|
85
|
+
if (isExplicitlyEmptyOrganizationScope(scope)) {
|
|
86
|
+
return {
|
|
87
|
+
response: NextResponse.json({ error: translate('api.errors.forbidden', 'Forbidden') }, { status: 403 }),
|
|
91
88
|
}
|
|
92
|
-
throw error
|
|
93
89
|
}
|
|
90
|
+
const organizationId = scope.selectedId
|
|
94
91
|
const tenantId = scope.tenantId ?? auth.tenantId ?? null
|
|
95
92
|
if (!organizationId || !tenantId) {
|
|
96
93
|
return {
|
|
@@ -15,7 +15,8 @@ import { Button } from '@open-mercato/ui/primitives/button'
|
|
|
15
15
|
import { Input } from '@open-mercato/ui/primitives/input'
|
|
16
16
|
import { SwitchField } from '@open-mercato/ui/primitives/switch-field'
|
|
17
17
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
18
|
-
import { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
18
|
+
import { useOrganizationScopeDetail, useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
19
|
+
import { getCurrentOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/organizationEvents'
|
|
19
20
|
|
|
20
21
|
type BrandingPayload = {
|
|
21
22
|
organizationId: string
|
|
@@ -42,6 +43,7 @@ export default function OrganizationBrandingPage() {
|
|
|
42
43
|
const t = useT()
|
|
43
44
|
const queryClient = useQueryClient()
|
|
44
45
|
const { organizationId, tenantId } = useOrganizationScopeDetail()
|
|
46
|
+
const scopeVersion = useOrganizationScopeVersion()
|
|
45
47
|
const [logoUrl, setLogoUrl] = React.useState('')
|
|
46
48
|
const [logoPreserveAspectRatio, setLogoPreserveAspectRatio] = React.useState(false)
|
|
47
49
|
const [selectedFile, setSelectedFile] = React.useState<File | null>(null)
|
|
@@ -67,7 +69,8 @@ export default function OrganizationBrandingPage() {
|
|
|
67
69
|
setLogoUrl(data?.logoUrl ?? '')
|
|
68
70
|
setLogoPreserveAspectRatio(data?.logoPreserveAspectRatio ?? false)
|
|
69
71
|
setSelectedFile(null)
|
|
70
|
-
|
|
72
|
+
if (fileInputRef.current) fileInputRef.current.value = ''
|
|
73
|
+
}, [scopeVersion, organizationId, tenantId, data?.organizationId, data?.logoPreserveAspectRatio, data?.logoUrl])
|
|
71
74
|
|
|
72
75
|
React.useEffect(() => {
|
|
73
76
|
if (!selectedFile || typeof URL === 'undefined') {
|
|
@@ -111,7 +114,13 @@ export default function OrganizationBrandingPage() {
|
|
|
111
114
|
try {
|
|
112
115
|
await runMutation({
|
|
113
116
|
operation: async () => {
|
|
117
|
+
if (getCurrentOrganizationScopeVersion() !== scopeVersion) {
|
|
118
|
+
throw new Error('[internal] Organization scope changed during branding save')
|
|
119
|
+
}
|
|
114
120
|
const uploadedLogoUrl = shouldUpload ? await uploadLogo(data.organizationId) : null
|
|
121
|
+
if (getCurrentOrganizationScopeVersion() !== scopeVersion) {
|
|
122
|
+
throw new Error('[internal] Organization scope changed during branding save')
|
|
123
|
+
}
|
|
115
124
|
const resolvedLogoUrl = uploadedLogoUrl ?? nextLogoUrl ?? logoUrl.trim()
|
|
116
125
|
const response = await withScopedApiRequestHeaders(
|
|
117
126
|
buildOptimisticLockHeader(data.updatedAt),
|
|
@@ -142,12 +151,15 @@ export default function OrganizationBrandingPage() {
|
|
|
142
151
|
hasUpload: shouldUpload,
|
|
143
152
|
},
|
|
144
153
|
})
|
|
154
|
+
if (getCurrentOrganizationScopeVersion() !== scopeVersion) return
|
|
145
155
|
await queryClient.invalidateQueries({ queryKey: ['directory-organization-branding'] })
|
|
156
|
+
if (getCurrentOrganizationScopeVersion() !== scopeVersion) return
|
|
146
157
|
window.dispatchEvent(new Event('om:refresh-sidebar'))
|
|
147
158
|
setSelectedFile(null)
|
|
148
159
|
if (fileInputRef.current) fileInputRef.current.value = ''
|
|
149
160
|
flash(t('directory.branding.flash.saved', 'Organization branding updated'), 'success')
|
|
150
161
|
} catch (err: unknown) {
|
|
162
|
+
if (getCurrentOrganizationScopeVersion() !== scopeVersion) return
|
|
151
163
|
if (surfaceRecordConflict(err, t)) return
|
|
152
164
|
const fallback = t('directory.branding.errors.save', 'Failed to update organization branding')
|
|
153
165
|
const message = err instanceof Error ? err.message : fallback
|
|
@@ -155,7 +167,7 @@ export default function OrganizationBrandingPage() {
|
|
|
155
167
|
} finally {
|
|
156
168
|
setSaving(false)
|
|
157
169
|
}
|
|
158
|
-
}, [data, logoPreserveAspectRatio, logoUrl, queryClient, runMutation, selectedFile, t, uploadLogo])
|
|
170
|
+
}, [data, logoPreserveAspectRatio, logoUrl, queryClient, runMutation, scopeVersion, selectedFile, t, uploadLogo])
|
|
159
171
|
|
|
160
172
|
const handleSubmit = React.useCallback((event: React.FormEvent<HTMLFormElement>) => {
|
|
161
173
|
event.preventDefault()
|
|
@@ -5,7 +5,7 @@ Use the sales module for orders, quotes, invoices, shipments, and payments. This
|
|
|
5
5
|
## Always
|
|
6
6
|
|
|
7
7
|
1. **MUST use `salesCalculationService` from DI** for document math.
|
|
8
|
-
2. **MUST follow document flow**: Quote → Order → Invoice
|
|
8
|
+
2. **MUST follow document flow**: Quote → Order → Invoice (quote optional)
|
|
9
9
|
3. **MUST scope all documents to a channel** — channel selection affects pricing, numbering, and visibility
|
|
10
10
|
|
|
11
11
|
## Ask First
|
|
@@ -36,7 +36,7 @@ Quote → Order → Invoice
|
|
|
36
36
|
Shipments + Payments
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
- Quotes convert
|
|
39
|
+
- Quotes convert via `sales.quotes.convert_to_order`; orders MAY also be created without a quote
|
|
40
40
|
- Orders track shipments and payments independently
|
|
41
41
|
- Each entity has its own status workflow — MUST NOT skip workflow states
|
|
42
42
|
|
|
@@ -56,7 +56,7 @@ const calcService = container.resolve('salesCalculationService')
|
|
|
56
56
|
|
|
57
57
|
### Core Entities
|
|
58
58
|
- **Sales Orders** — confirmed customer orders. MUST have a channel and at least one line
|
|
59
|
-
- **Sales Quotes** — proposed orders
|
|
59
|
+
- **Sales Quotes** — proposed orders; deleted on conversion
|
|
60
60
|
- **Order/Quote Lines** — individual items. MUST reference valid products
|
|
61
61
|
- **Adjustments** — discounts/surcharges. MUST use registered `AdjustmentKind`
|
|
62
62
|
|
|
@@ -277,7 +277,8 @@ const CUSTOMER_FIELDS = ['id', 'display_name']
|
|
|
277
277
|
/**
|
|
278
278
|
* Decorate list items with `vendor_name` when they carry a `vendor_id` column
|
|
279
279
|
* referencing `customers.customer_entity` by UUID only (no ORM cross-module
|
|
280
|
-
* relations).
|
|
280
|
+
* relations). No-op when the optional `customers` module is not enabled.
|
|
281
|
+
* Mutates `payload.items` in place.
|
|
281
282
|
*/
|
|
282
283
|
export async function attachVendorLabelsToListItems(
|
|
283
284
|
payload: AnyListPayload,
|
|
@@ -285,11 +286,13 @@ export async function attachVendorLabelsToListItems(
|
|
|
285
286
|
): Promise<void> {
|
|
286
287
|
const items = Array.isArray(payload?.items) ? payload.items : []
|
|
287
288
|
if (items.length === 0) return
|
|
289
|
+
const customerEntityId = E.customers?.customer_entity
|
|
290
|
+
if (!customerEntityId) return
|
|
288
291
|
const ids = uniqueIdsFromKeys(items, ['vendor_id'])
|
|
289
292
|
if (ids.length === 0) return
|
|
290
293
|
const map = (await batchLoadById(
|
|
291
294
|
ctx,
|
|
292
|
-
|
|
295
|
+
customerEntityId,
|
|
293
296
|
ids,
|
|
294
297
|
CUSTOMER_FIELDS,
|
|
295
298
|
'vendor',
|
package/src/modules/wms/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ export const metadata: ModuleInfo = {
|
|
|
9
9
|
author: 'Open Mercato Team',
|
|
10
10
|
license: 'MIT',
|
|
11
11
|
ejectable: true,
|
|
12
|
-
requires: ['catalog', 'sales', 'feature_toggles'],
|
|
12
|
+
requires: ['catalog', 'customers', 'sales', 'feature_toggles'],
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export { features } from './acl'
|