@open-mercato/core 0.6.6-develop.6543.1.49fadb4c31 → 0.6.6
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 +9 -23
- package/dist/modules/api_keys/api/keys/route.js.map +2 -2
- package/dist/modules/api_keys/cli.js +1 -2
- package/dist/modules/api_keys/cli.js.map +2 -2
- package/dist/modules/attachments/api/file/[id]/route.js +7 -9
- package/dist/modules/attachments/api/file/[id]/route.js.map +2 -2
- package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +7 -9
- package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
- package/dist/modules/attachments/api/route.js +17 -21
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +2 -6
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/cli.js +2 -3
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/configs/lib/upgrade-actions.js +0 -22
- package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection-table.js +2 -0
- package/dist/modules/customer_accounts/widgets/injection-table.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +1 -7
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyDetailTabs.js +7 -11
- package/dist/modules/customers/components/detail/CompanyDetailTabs.js.map +2 -2
- package/dist/modules/dashboards/api/roles/widgets/route.js +3 -22
- package/dist/modules/dashboards/api/roles/widgets/route.js.map +2 -2
- package/dist/modules/dashboards/api/users/widgets/route.js +3 -22
- package/dist/modules/dashboards/api/users/widgets/route.js.map +2 -2
- package/dist/modules/dashboards/cli.js +3 -4
- package/dist/modules/dashboards/cli.js.map +2 -2
- package/dist/modules/dashboards/lib/widgetAssignmentScope.js +1 -9
- package/dist/modules/dashboards/lib/widgetAssignmentScope.js.map +2 -2
- package/dist/modules/dictionaries/commands/entries.js +14 -7
- package/dist/modules/dictionaries/commands/entries.js.map +2 -2
- package/dist/modules/dictionaries/commands/entry-operations.js +16 -7
- package/dist/modules/dictionaries/commands/entry-operations.js.map +2 -2
- package/dist/modules/dictionaries/commands/factory.js +3 -9
- package/dist/modules/dictionaries/commands/factory.js.map +2 -2
- package/dist/modules/dictionaries/fields/dictionary.js +41 -56
- package/dist/modules/dictionaries/fields/dictionary.js.map +2 -2
- package/dist/modules/directory/utils/organizationScope.js +3 -10
- package/dist/modules/directory/utils/organizationScope.js.map +2 -2
- package/dist/modules/entities/api/records.js +4 -5
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/system/[entityId]/page.meta.js +0 -1
- package/dist/modules/entities/backend/entities/system/[entityId]/page.meta.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/page.js +49 -49
- package/dist/modules/entities/backend/entities/user/[entityId]/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/page.meta.js +0 -1
- package/dist/modules/entities/backend/entities/user/[entityId]/page.meta.js.map +2 -2
- package/dist/modules/entities/cli.js +1 -2
- package/dist/modules/entities/cli.js.map +2 -2
- package/dist/modules/query_index/api/status.js +8 -2
- package/dist/modules/query_index/api/status.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +0 -44
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/commands/returns.js +27 -2
- package/dist/modules/sales/commands/returns.js.map +2 -2
- package/dist/modules/sales/components/documents/AddressesSection.js +2 -6
- package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +0 -1
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +0 -9
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentForm.js +0 -44
- package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentDialog.js +38 -59
- package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
- package/dist/modules/sales/lib/shipments/snapshots.js +1 -27
- package/dist/modules/sales/lib/shipments/snapshots.js.map +2 -2
- package/dist/modules/workflows/frontend/checkout-demo/page.js +8 -1
- package/dist/modules/workflows/frontend/checkout-demo/page.js.map +2 -2
- package/dist/modules/workflows/lib/activity-executor.js +1 -1
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/find-definition.js +18 -2
- package/dist/modules/workflows/lib/find-definition.js.map +2 -2
- package/dist/modules/workflows/lib/signal-handler.js +3 -2
- package/dist/modules/workflows/lib/signal-handler.js.map +2 -2
- package/dist/modules/workflows/lib/step-handler.js +4 -8
- package/dist/modules/workflows/lib/step-handler.js.map +2 -2
- package/dist/modules/workflows/lib/task-handler.js +3 -5
- package/dist/modules/workflows/lib/task-handler.js.map +2 -2
- package/dist/modules/workflows/lib/timer-handler.js +2 -1
- package/dist/modules/workflows/lib/timer-handler.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +5 -13
- package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
- package/dist/modules/workflows/lib/workflow-executor.js +11 -9
- package/dist/modules/workflows/lib/workflow-executor.js.map +2 -2
- package/dist/modules/workflows/migrations/Migration20260715120000.js +68 -0
- package/dist/modules/workflows/migrations/Migration20260715120000.js.map +7 -0
- package/dist/modules/workflows/migrations/Migration20260716120000.js +24 -0
- package/dist/modules/workflows/migrations/Migration20260716120000.js.map +7 -0
- package/dist/modules/workflows/workflows.js +2 -0
- package/dist/modules/workflows/workflows.js.map +2 -2
- package/package.json +8 -9
- package/src/modules/api_keys/api/keys/route.ts +9 -27
- package/src/modules/api_keys/cli.ts +3 -2
- package/src/modules/attachments/api/file/[id]/route.ts +7 -11
- package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +7 -11
- package/src/modules/attachments/api/route.ts +21 -26
- package/src/modules/auth/api/users/route.ts +2 -11
- package/src/modules/auth/cli.ts +2 -3
- package/src/modules/configs/i18n/de.json +1 -5
- package/src/modules/configs/i18n/en.json +1 -5
- package/src/modules/configs/i18n/es.json +1 -5
- package/src/modules/configs/i18n/pl.json +1 -5
- package/src/modules/configs/lib/upgrade-actions.ts +0 -24
- package/src/modules/customer_accounts/AGENTS.md +6 -8
- package/src/modules/customer_accounts/widgets/injection-table.ts +2 -0
- package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +1 -11
- package/src/modules/customers/components/detail/CompanyDetailTabs.tsx +7 -13
- package/src/modules/dashboards/api/roles/widgets/route.ts +3 -22
- package/src/modules/dashboards/api/users/widgets/route.ts +3 -22
- package/src/modules/dashboards/cli.ts +9 -4
- package/src/modules/dashboards/lib/widgetAssignmentScope.ts +0 -18
- package/src/modules/dictionaries/commands/entries.ts +16 -7
- package/src/modules/dictionaries/commands/entry-operations.ts +18 -8
- package/src/modules/dictionaries/commands/factory.ts +2 -8
- package/src/modules/dictionaries/fields/dictionary.tsx +32 -55
- package/src/modules/dictionaries/i18n/de.json +0 -2
- package/src/modules/dictionaries/i18n/en.json +0 -2
- package/src/modules/dictionaries/i18n/es.json +0 -2
- package/src/modules/dictionaries/i18n/pl.json +0 -2
- package/src/modules/directory/utils/organizationScope.ts +2 -36
- package/src/modules/entities/api/records.ts +7 -5
- package/src/modules/entities/backend/entities/system/[entityId]/page.meta.ts +0 -1
- package/src/modules/entities/backend/entities/user/[entityId]/page.meta.ts +0 -1
- package/src/modules/entities/backend/entities/user/[entityId]/page.tsx +49 -51
- package/src/modules/entities/cli.ts +1 -2
- package/src/modules/entities/i18n/de.json +0 -34
- package/src/modules/entities/i18n/en.json +0 -34
- package/src/modules/entities/i18n/es.json +0 -34
- package/src/modules/entities/i18n/pl.json +0 -34
- package/src/modules/query_index/api/status.ts +8 -2
- package/src/modules/sales/commands/documents.ts +0 -65
- package/src/modules/sales/commands/returns.ts +32 -2
- package/src/modules/sales/components/documents/AddressesSection.tsx +2 -6
- package/src/modules/sales/components/documents/ItemsSection.tsx +0 -5
- package/src/modules/sales/components/documents/LineItemDialog.tsx +0 -10
- package/src/modules/sales/components/documents/SalesDocumentForm.tsx +0 -46
- package/src/modules/sales/components/documents/ShipmentDialog.tsx +40 -65
- package/src/modules/sales/i18n/de.json +0 -4
- package/src/modules/sales/i18n/en.json +0 -4
- package/src/modules/sales/i18n/es.json +0 -4
- package/src/modules/sales/i18n/pl.json +0 -4
- package/src/modules/sales/lib/shipments/snapshots.ts +0 -30
- package/src/modules/workflows/frontend/checkout-demo/page.tsx +12 -1
- package/src/modules/workflows/lib/activity-executor.ts +22 -2
- package/src/modules/workflows/lib/find-definition.ts +53 -1
- package/src/modules/workflows/lib/signal-handler.ts +5 -4
- package/src/modules/workflows/lib/step-handler.ts +4 -8
- package/src/modules/workflows/lib/task-handler.ts +2 -4
- package/src/modules/workflows/lib/timer-handler.ts +3 -2
- package/src/modules/workflows/lib/transition-handler.ts +5 -13
- package/src/modules/workflows/lib/workflow-executor.ts +11 -8
- package/src/modules/workflows/migrations/Migration20260715120000.ts +114 -0
- package/src/modules/workflows/migrations/Migration20260716120000.ts +57 -0
- package/src/modules/workflows/workflows.ts +2 -0
- package/dist/modules/attachments/lib/reconcileOrganization.js +0 -110
- package/dist/modules/attachments/lib/reconcileOrganization.js.map +0 -7
- package/dist/modules/attachments/lib/requestScope.js +0 -10
- package/dist/modules/attachments/lib/requestScope.js.map +0 -7
- package/dist/modules/customers/components/detail/useDealsAccess.js +0 -16
- package/dist/modules/customers/components/detail/useDealsAccess.js.map +0 -7
- package/src/modules/attachments/lib/reconcileOrganization.ts +0 -188
- package/src/modules/attachments/lib/requestScope.ts +0 -27
- package/src/modules/customers/components/detail/useDealsAccess.ts +0 -19
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
2
|
-
async function resolveAttachmentOrganizationId(container, auth, request) {
|
|
3
|
-
if (!auth) return null;
|
|
4
|
-
const scope = await resolveOrganizationScopeForRequest({ container, auth, request });
|
|
5
|
-
return scope?.selectedId ?? auth.orgId ?? null;
|
|
6
|
-
}
|
|
7
|
-
export {
|
|
8
|
-
resolveAttachmentOrganizationId
|
|
9
|
-
};
|
|
10
|
-
//# sourceMappingURL=requestScope.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../src/modules/attachments/lib/requestScope.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\n\n/**\n * Resolve the organization an attachment request should act within.\n *\n * `auth.orgId` alone is NOT selected-organization aware for non-superadmin\n * principals: `applySuperAdminScope` only rewrites `orgId` from the\n * `om_selected_org` cookie for superadmins, so a regular multi-org admin who\n * switches the header organization keeps `auth.orgId` pinned to their own home\n * organization. The CRUD factory and other org-scoped routes derive the active\n * organization via `resolveOrganizationScopeForRequest` (cookie-driven and\n * RBAC-validated for ALL users, falling back to the home org when the selection\n * is absent or inaccessible). Attachments must do the same so uploaded files\n * land under \u2014 and are read back from \u2014 the currently selected organization\n * rather than the uploader's home organization (#3765).\n */\nexport async function resolveAttachmentOrganizationId(\n container: AwilixContainer,\n auth: AuthContext,\n request: Request,\n): Promise<string | null> {\n if (!auth) return null\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request })\n return scope?.selectedId ?? auth.orgId ?? null\n}\n"],
|
|
5
|
-
"mappings": "AAEA,SAAS,0CAA0C;AAgBnD,eAAsB,gCACpB,WACA,MACA,SACwB;AACxB,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,QAAQ,CAAC;AACnF,SAAO,OAAO,cAAc,KAAK,SAAS;AAC5C;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
import { useBackendChrome } from "@open-mercato/ui/backend/BackendChromeProvider";
|
|
4
|
-
import { hasFeature } from "@open-mercato/shared/security/features";
|
|
5
|
-
function useDealsAccess() {
|
|
6
|
-
const { payload, isReady } = useBackendChrome();
|
|
7
|
-
const canViewDeals = React.useMemo(
|
|
8
|
-
() => hasFeature(payload?.grantedFeatures ?? [], "customers.deals.view"),
|
|
9
|
-
[payload?.grantedFeatures]
|
|
10
|
-
);
|
|
11
|
-
return { canViewDeals, isReady };
|
|
12
|
-
}
|
|
13
|
-
export {
|
|
14
|
-
useDealsAccess
|
|
15
|
-
};
|
|
16
|
-
//# sourceMappingURL=useDealsAccess.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../../src/modules/customers/components/detail/useDealsAccess.ts"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useBackendChrome } from '@open-mercato/ui/backend/BackendChromeProvider'\nimport { hasFeature } from '@open-mercato/shared/security/features'\n\nexport type DealsAccess = {\n canViewDeals: boolean\n isReady: boolean\n}\n\nexport function useDealsAccess(): DealsAccess {\n const { payload, isReady } = useBackendChrome()\n const canViewDeals = React.useMemo(\n () => hasFeature(payload?.grantedFeatures ?? [], 'customers.deals.view'),\n [payload?.grantedFeatures],\n )\n return { canViewDeals, isReady }\n}\n"],
|
|
5
|
-
"mappings": ";AAEA,YAAY,WAAW;AACvB,SAAS,wBAAwB;AACjC,SAAS,kBAAkB;AAOpB,SAAS,iBAA8B;AAC5C,QAAM,EAAE,SAAS,QAAQ,IAAI,iBAAiB;AAC9C,QAAM,eAAe,MAAM;AAAA,IACzB,MAAM,WAAW,SAAS,mBAAmB,CAAC,GAAG,sBAAsB;AAAA,IACvE,CAAC,SAAS,eAAe;AAAA,EAC3B;AACA,SAAO,EAAE,cAAc,QAAQ;AACjC;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
2
|
-
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
3
|
-
import type { QueryEngine } from '@open-mercato/shared/lib/query/types'
|
|
4
|
-
import { Attachment } from '../data/entities'
|
|
5
|
-
|
|
6
|
-
const logger = createLogger('attachments').child({ component: 'reconcileOrganization' })
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Virtual entity id for library attachments — these are not attached to a
|
|
10
|
-
* parent record that owns an organization, so there is nothing to reconcile
|
|
11
|
-
* against. They are counted as skipped rather than unresolved.
|
|
12
|
-
*/
|
|
13
|
-
const LIBRARY_ENTITY_ID = 'attachments:library'
|
|
14
|
-
|
|
15
|
-
const DEFAULT_BATCH_SIZE = 500
|
|
16
|
-
|
|
17
|
-
export type AttachmentOrgReconcileEntityStat = {
|
|
18
|
-
scanned: number
|
|
19
|
-
updated: number
|
|
20
|
-
unresolved: number
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type AttachmentOrgReconcileReport = {
|
|
24
|
-
scanned: number
|
|
25
|
-
updated: number
|
|
26
|
-
unresolved: number
|
|
27
|
-
skippedVirtual: number
|
|
28
|
-
byEntity: Record<string, AttachmentOrgReconcileEntityStat>
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
type AttachmentScanRow = {
|
|
32
|
-
id: string
|
|
33
|
-
entity_id: string | null
|
|
34
|
-
record_id: string | null
|
|
35
|
-
organization_id: string | null
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function readParentOrganizationId(record: Record<string, unknown>): string | null {
|
|
39
|
-
const raw = record['organization_id'] ?? record['organizationId']
|
|
40
|
-
if (typeof raw === 'string' && raw.trim().length) return raw.trim()
|
|
41
|
-
return null
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function ensureBucket(
|
|
45
|
-
report: AttachmentOrgReconcileReport,
|
|
46
|
-
entityId: string,
|
|
47
|
-
): AttachmentOrgReconcileEntityStat {
|
|
48
|
-
const existing = report.byEntity[entityId]
|
|
49
|
-
if (existing) return existing
|
|
50
|
-
const bucket: AttachmentOrgReconcileEntityStat = { scanned: 0, updated: 0, unresolved: 0 }
|
|
51
|
-
report.byEntity[entityId] = bucket
|
|
52
|
-
return bucket
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Reconcile the `organization_id` of existing attachments to the organization
|
|
57
|
-
* of the record they are attached to.
|
|
58
|
-
*
|
|
59
|
-
* Background (#3765): before the upload route became selected-organization
|
|
60
|
-
* aware, a multi-org admin who switched the header organization uploaded files
|
|
61
|
-
* that were silently stored under their *home* organization instead of the
|
|
62
|
-
* selected one (the organization of the parent record). Those rows are now
|
|
63
|
-
* invisible to org-scoped reads and stay orphaned. This heals them forward.
|
|
64
|
-
*
|
|
65
|
-
* The parent record's organization is the ground truth: an attachment is not
|
|
66
|
-
* distinguishable from a legitimately home-org one by looking at the attachment
|
|
67
|
-
* alone. Parent organizations are resolved generically through the Query Engine
|
|
68
|
-
* by `entityId` (works for both base and custom entities, tenant-scoped and
|
|
69
|
-
* organization-agnostic so the parent's real org is returned regardless of
|
|
70
|
-
* which org the caller sits in) — the same mechanism the attachments module
|
|
71
|
-
* already uses to enrich assignment details.
|
|
72
|
-
*
|
|
73
|
-
* The reconciliation is:
|
|
74
|
-
* - idempotent — rows already matching their parent are left untouched;
|
|
75
|
-
* - conservative — when the parent org cannot be resolved (unregistered
|
|
76
|
-
* entity id, hard-deleted parent, parent without an org column) the row is
|
|
77
|
-
* counted as `unresolved` and left as-is rather than guessed at;
|
|
78
|
-
* - tenant-scoped — it reconciles every attachment in the tenant across all
|
|
79
|
-
* organizations (the whole point is moving rows between orgs).
|
|
80
|
-
*
|
|
81
|
-
* Writes go through the passed `EntityManager` (which the caller runs inside a
|
|
82
|
-
* transaction) so the whole reconciliation commits atomically.
|
|
83
|
-
*/
|
|
84
|
-
export async function reconcileAttachmentOrganizations(opts: {
|
|
85
|
-
em: EntityManager
|
|
86
|
-
queryEngine: QueryEngine
|
|
87
|
-
tenantId: string
|
|
88
|
-
batchSize?: number
|
|
89
|
-
}): Promise<AttachmentOrgReconcileReport> {
|
|
90
|
-
const { em, queryEngine, tenantId } = opts
|
|
91
|
-
const batchSize = opts.batchSize && opts.batchSize > 0 ? opts.batchSize : DEFAULT_BATCH_SIZE
|
|
92
|
-
const report: AttachmentOrgReconcileReport = {
|
|
93
|
-
scanned: 0,
|
|
94
|
-
updated: 0,
|
|
95
|
-
unresolved: 0,
|
|
96
|
-
skippedVirtual: 0,
|
|
97
|
-
byEntity: {},
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const rows = (await em.getConnection().execute(
|
|
101
|
-
'select id, entity_id, record_id, organization_id from attachments where tenant_id = ?',
|
|
102
|
-
[tenantId],
|
|
103
|
-
)) as AttachmentScanRow[]
|
|
104
|
-
report.scanned = rows.length
|
|
105
|
-
if (!rows.length) return report
|
|
106
|
-
|
|
107
|
-
const groups = new Map<string, AttachmentScanRow[]>()
|
|
108
|
-
for (const row of rows) {
|
|
109
|
-
if (!row.id || !row.entity_id || !row.record_id) continue
|
|
110
|
-
const list = groups.get(row.entity_id) ?? []
|
|
111
|
-
list.push(row)
|
|
112
|
-
groups.set(row.entity_id, list)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const pendingUpdates: Array<{ id: string; organizationId: string }> = []
|
|
116
|
-
|
|
117
|
-
for (const [entityId, group] of groups) {
|
|
118
|
-
const bucket = ensureBucket(report, entityId)
|
|
119
|
-
bucket.scanned += group.length
|
|
120
|
-
|
|
121
|
-
if (entityId === LIBRARY_ENTITY_ID) {
|
|
122
|
-
report.skippedVirtual += group.length
|
|
123
|
-
continue
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const recordIds = Array.from(
|
|
127
|
-
new Set(group.map((row) => row.record_id).filter((value): value is string => !!value)),
|
|
128
|
-
)
|
|
129
|
-
const parentOrgByRecordId = new Map<string, string | null>()
|
|
130
|
-
let resolvable = true
|
|
131
|
-
|
|
132
|
-
for (let index = 0; index < recordIds.length; index += batchSize) {
|
|
133
|
-
const chunk = recordIds.slice(index, index + batchSize)
|
|
134
|
-
try {
|
|
135
|
-
const result = await queryEngine.query(entityId as any, {
|
|
136
|
-
fields: ['id', 'organization_id'],
|
|
137
|
-
filters: { id: chunk.length === 1 ? { $eq: chunk[0] } : { $in: chunk } },
|
|
138
|
-
tenantId,
|
|
139
|
-
withDeleted: true,
|
|
140
|
-
page: { pageSize: Math.max(chunk.length, 1) },
|
|
141
|
-
})
|
|
142
|
-
for (const item of result.items ?? []) {
|
|
143
|
-
const record = item as Record<string, unknown>
|
|
144
|
-
const recordId = record.id != null ? String(record.id) : null
|
|
145
|
-
if (!recordId) continue
|
|
146
|
-
parentOrgByRecordId.set(recordId, readParentOrganizationId(record))
|
|
147
|
-
}
|
|
148
|
-
} catch (error) {
|
|
149
|
-
resolvable = false
|
|
150
|
-
logger.warn('org reconcile: cannot resolve parent organization for entity', {
|
|
151
|
-
entityId,
|
|
152
|
-
error,
|
|
153
|
-
})
|
|
154
|
-
break
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (!resolvable) {
|
|
159
|
-
bucket.unresolved += group.length
|
|
160
|
-
report.unresolved += group.length
|
|
161
|
-
continue
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
for (const row of group) {
|
|
165
|
-
const target = row.record_id ? parentOrgByRecordId.get(row.record_id) : null
|
|
166
|
-
if (!target) {
|
|
167
|
-
bucket.unresolved += 1
|
|
168
|
-
report.unresolved += 1
|
|
169
|
-
continue
|
|
170
|
-
}
|
|
171
|
-
if ((row.organization_id ?? null) === target) continue
|
|
172
|
-
pendingUpdates.push({ id: row.id, organizationId: target })
|
|
173
|
-
bucket.updated += 1
|
|
174
|
-
report.updated += 1
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
for (let index = 0; index < pendingUpdates.length; index += batchSize) {
|
|
179
|
-
const chunk = pendingUpdates.slice(index, index + batchSize)
|
|
180
|
-
for (const update of chunk) {
|
|
181
|
-
const reference = em.getReference(Attachment, update.id)
|
|
182
|
-
reference.organizationId = update.organizationId
|
|
183
|
-
}
|
|
184
|
-
await em.flush()
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return report
|
|
188
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { AwilixContainer } from 'awilix'
|
|
2
|
-
import type { AuthContext } from '@open-mercato/shared/lib/auth/server'
|
|
3
|
-
import { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Resolve the organization an attachment request should act within.
|
|
7
|
-
*
|
|
8
|
-
* `auth.orgId` alone is NOT selected-organization aware for non-superadmin
|
|
9
|
-
* principals: `applySuperAdminScope` only rewrites `orgId` from the
|
|
10
|
-
* `om_selected_org` cookie for superadmins, so a regular multi-org admin who
|
|
11
|
-
* switches the header organization keeps `auth.orgId` pinned to their own home
|
|
12
|
-
* organization. The CRUD factory and other org-scoped routes derive the active
|
|
13
|
-
* organization via `resolveOrganizationScopeForRequest` (cookie-driven and
|
|
14
|
-
* RBAC-validated for ALL users, falling back to the home org when the selection
|
|
15
|
-
* is absent or inaccessible). Attachments must do the same so uploaded files
|
|
16
|
-
* land under — and are read back from — the currently selected organization
|
|
17
|
-
* rather than the uploader's home organization (#3765).
|
|
18
|
-
*/
|
|
19
|
-
export async function resolveAttachmentOrganizationId(
|
|
20
|
-
container: AwilixContainer,
|
|
21
|
-
auth: AuthContext,
|
|
22
|
-
request: Request,
|
|
23
|
-
): Promise<string | null> {
|
|
24
|
-
if (!auth) return null
|
|
25
|
-
const scope = await resolveOrganizationScopeForRequest({ container, auth, request })
|
|
26
|
-
return scope?.selectedId ?? auth.orgId ?? null
|
|
27
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from 'react'
|
|
4
|
-
import { useBackendChrome } from '@open-mercato/ui/backend/BackendChromeProvider'
|
|
5
|
-
import { hasFeature } from '@open-mercato/shared/security/features'
|
|
6
|
-
|
|
7
|
-
export type DealsAccess = {
|
|
8
|
-
canViewDeals: boolean
|
|
9
|
-
isReady: boolean
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function useDealsAccess(): DealsAccess {
|
|
13
|
-
const { payload, isReady } = useBackendChrome()
|
|
14
|
-
const canViewDeals = React.useMemo(
|
|
15
|
-
() => hasFeature(payload?.grantedFeatures ?? [], 'customers.deals.view'),
|
|
16
|
-
[payload?.grantedFeatures],
|
|
17
|
-
)
|
|
18
|
-
return { canViewDeals, isReady }
|
|
19
|
-
}
|