@open-mercato/core 0.6.5-develop.5337.1.534b781eac → 0.6.5
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/AGENTS.md +1 -1
- package/dist/bootstrap.js +46 -6
- package/dist/bootstrap.js.map +2 -2
- package/dist/generated/entities/organization/index.js +2 -0
- package/dist/generated/entities/organization/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +1 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/crmFixtures.js +4 -0
- package/dist/helpers/integration/crmFixtures.js.map +2 -2
- package/dist/modules/attachments/api/library/route.js +2 -2
- package/dist/modules/attachments/api/library/route.js.map +2 -2
- package/dist/modules/attachments/api/route.js +2 -0
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentContentPreview.js +9 -5
- package/dist/modules/attachments/components/AttachmentContentPreview.js.map +2 -2
- package/dist/modules/attachments/lib/access.js +18 -0
- package/dist/modules/attachments/lib/access.js.map +2 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +3 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
- package/dist/modules/audit_logs/data/entities.js +2 -1
- package/dist/modules/audit_logs/data/entities.js.map +2 -2
- package/dist/modules/audit_logs/migrations/Migration20260611104500.js +13 -0
- package/dist/modules/audit_logs/migrations/Migration20260611104500.js.map +7 -0
- package/dist/modules/audit_logs/services/accessLogService.js +10 -0
- package/dist/modules/audit_logs/services/accessLogService.js.map +2 -2
- package/dist/modules/auth/api/admin/nav.js +9 -0
- package/dist/modules/auth/api/admin/nav.js.map +2 -2
- package/dist/modules/auth/api/login.js +4 -13
- package/dist/modules/auth/api/login.js.map +2 -2
- package/dist/modules/auth/commands/users.js +20 -14
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/data/entities.js +4 -2
- package/dist/modules/auth/data/entities.js.map +2 -2
- package/dist/modules/auth/lib/backendChrome.js +35 -2
- package/dist/modules/auth/lib/backendChrome.js.map +2 -2
- package/dist/modules/auth/lib/consentIntegrity.js +3 -3
- package/dist/modules/auth/lib/consentIntegrity.js.map +2 -2
- package/dist/modules/auth/migrations/Migration20260610120000.js +30 -0
- package/dist/modules/auth/migrations/Migration20260610120000.js.map +7 -0
- package/dist/modules/auth/migrations/Migration20260611103000.js +15 -0
- package/dist/modules/auth/migrations/Migration20260611103000.js.map +7 -0
- package/dist/modules/auth/services/authService.js +5 -3
- package/dist/modules/auth/services/authService.js.map +2 -2
- package/dist/modules/auth/services/rbacService.js +3 -2
- package/dist/modules/auth/services/rbacService.js.map +2 -2
- package/dist/modules/catalog/ai-tools/configuration-pack.js.map +1 -1
- package/dist/modules/catalog/ai-tools/prices-offers-pack.js.map +1 -1
- package/dist/modules/catalog/ai-tools/products-pack.js.map +1 -1
- package/dist/modules/catalog/ai-tools/variants-pack.js.map +1 -1
- package/dist/modules/communication_channels/data/entities.js.map +1 -1
- package/dist/modules/communication_channels/encryption.js.map +1 -1
- package/dist/modules/communication_channels/lib/thread-matcher.js.map +1 -1
- package/dist/modules/communication_channels/lib/thread-token.js.map +1 -1
- package/dist/modules/currencies/api/currencies/route.js +4 -3
- package/dist/modules/currencies/api/currencies/route.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/roles.js +2 -1
- package/dist/modules/customer_accounts/api/admin/roles.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +0 -3
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +2 -2
- package/dist/modules/customer_accounts/events.js +1 -1
- package/dist/modules/customer_accounts/events.js.map +1 -1
- package/dist/modules/customer_accounts/lib/resolveTenantContext.js.map +1 -1
- package/dist/modules/customers/acl.js +1 -1
- package/dist/modules/customers/acl.js.map +1 -1
- package/dist/modules/customers/ai-tools/companies-pack.js.map +1 -1
- package/dist/modules/customers/ai-tools/deals-pack.js.map +1 -1
- package/dist/modules/customers/ai-tools/people-pack.js.map +1 -1
- package/dist/modules/customers/api/companies/route.js +4 -4
- package/dist/modules/customers/api/companies/route.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +43 -2
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/deals/summary/route.js +402 -0
- package/dist/modules/customers/api/deals/summary/route.js.map +7 -0
- package/dist/modules/customers/api/people/route.js +4 -4
- package/dist/modules/customers/api/people/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js +16 -5
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js +22 -5
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +12 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +221 -56
- package/dist/modules/customers/backend/customers/deals/page.js.map +3 -3
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +1 -1
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +18 -0
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/cli.js +15 -9
- package/dist/modules/customers/cli.js.map +2 -2
- package/dist/modules/customers/commands/addresses.js +5 -5
- package/dist/modules/customers/commands/addresses.js.map +2 -2
- package/dist/modules/customers/commands/comments.js +5 -5
- package/dist/modules/customers/commands/comments.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +2 -2
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/entity-roles.js +2 -1
- package/dist/modules/customers/commands/entity-roles.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +8 -5
- package/dist/modules/customers/commands/interactions.js.map +2 -2
- package/dist/modules/customers/commands/shared.js +21 -6
- package/dist/modules/customers/commands/shared.js.map +2 -2
- package/dist/modules/customers/commands/tags.js +3 -3
- package/dist/modules/customers/commands/tags.js.map +2 -2
- package/dist/modules/customers/components/DealsKpiStrip.js +282 -0
- package/dist/modules/customers/components/DealsKpiStrip.js.map +7 -0
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js +0 -1
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +100 -17
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonDetailTabs.js +11 -3
- package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +1 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/assignableStaff.js +21 -8
- package/dist/modules/customers/components/detail/assignableStaff.js.map +2 -2
- package/dist/modules/customers/components/kpi/PipelineStageBar.js +63 -0
- package/dist/modules/customers/components/kpi/PipelineStageBar.js.map +7 -0
- package/dist/modules/customers/lib/dealsMetrics.js +82 -0
- package/dist/modules/customers/lib/dealsMetrics.js.map +7 -0
- package/dist/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.js.map +1 -1
- package/dist/modules/data_sync/api/run.js +1 -1
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/directory/api/organization-branding/route.js +214 -0
- package/dist/modules/directory/api/organization-branding/route.js.map +7 -0
- package/dist/modules/directory/api/organizations/route.js +7 -0
- package/dist/modules/directory/api/organizations/route.js.map +3 -3
- package/dist/modules/directory/backend/directory/branding/page.js +214 -0
- package/dist/modules/directory/backend/directory/branding/page.js.map +7 -0
- package/dist/modules/directory/backend/directory/branding/page.meta.js +26 -0
- package/dist/modules/directory/backend/directory/branding/page.meta.js.map +7 -0
- package/dist/modules/directory/commands/organizations.js +8 -1
- package/dist/modules/directory/commands/organizations.js.map +2 -2
- package/dist/modules/directory/data/entities.js +3 -0
- package/dist/modules/directory/data/entities.js.map +2 -2
- package/dist/modules/directory/data/validators.js +9 -0
- package/dist/modules/directory/data/validators.js.map +2 -2
- package/dist/modules/directory/migrations/Migration20260607222259_directory.js +13 -0
- package/dist/modules/directory/migrations/Migration20260607222259_directory.js.map +7 -0
- package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js +2 -1
- package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js.map +2 -2
- package/dist/modules/directory/utils/organizationScope.js +59 -27
- package/dist/modules/directory/utils/organizationScope.js.map +2 -2
- package/dist/modules/entities/api/definitions.batch.js +2 -1
- package/dist/modules/entities/api/definitions.batch.js.map +2 -2
- package/dist/modules/entities/api/entities.js +7 -0
- package/dist/modules/entities/api/entities.js.map +2 -2
- package/dist/modules/entities/api/records.js +26 -15
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js +14 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js +14 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +12 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/entities/components/useRecordsEntityGuard.js +30 -0
- package/dist/modules/entities/components/useRecordsEntityGuard.js.map +7 -0
- package/dist/modules/payment_gateways/api/transactions/route.js +2 -4
- package/dist/modules/payment_gateways/api/transactions/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/[id]/route.js +7 -2
- package/dist/modules/progress/api/jobs/[id]/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/route.js +1 -1
- package/dist/modules/progress/api/jobs/route.js.map +2 -2
- package/dist/modules/progress/lib/progressServiceImpl.js +8 -2
- package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
- package/dist/modules/query_index/data/entities.js +2 -1
- package/dist/modules/query_index/data/entities.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +4 -2
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/migrations/Migration20260611103000_query_index.js +16 -0
- package/dist/modules/query_index/migrations/Migration20260611103000_query_index.js.map +7 -0
- package/dist/modules/resources/api/resources.js +2 -3
- package/dist/modules/resources/api/resources.js.map +2 -2
- package/dist/modules/sales/api/documents/factory.js +2 -2
- package/dist/modules/sales/api/documents/factory.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +7 -5
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js +2 -1
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
- package/dist/modules/sales/components/documents/salesDocumentsColumns.js +10 -0
- package/dist/modules/sales/components/documents/salesDocumentsColumns.js.map +7 -0
- package/dist/modules/staff/api/team-members.js +9 -2
- package/dist/modules/staff/api/team-members.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +24 -1
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js +11 -6
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
- package/dist/modules/staff/commands/team-members.js +1 -1
- package/dist/modules/staff/commands/team-members.js.map +2 -2
- package/dist/modules/staff/components/TeamMemberForm.js +1 -1
- package/dist/modules/staff/components/TeamMemberForm.js.map +2 -2
- package/dist/modules/staff/lib/scheduleSwitch.js +23 -0
- package/dist/modules/staff/lib/scheduleSwitch.js.map +7 -0
- package/dist/modules/sync_excel/api/import/route.js +1 -1
- package/dist/modules/sync_excel/api/import/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/route.js +3 -2
- package/dist/modules/workflows/api/definitions/route.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/create/page.js +1 -2
- package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +1 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialog.js +4 -13
- package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js +4 -13
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowGraphImpl.js +1 -4
- package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +2 -2
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js +2 -5
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js.map +2 -2
- package/generated/entities/organization/index.ts +1 -0
- package/generated/entity-fields-registry.ts +1 -0
- package/package.json +11 -12
- package/src/bootstrap.ts +65 -7
- package/src/helpers/integration/crmFixtures.ts +21 -1
- package/src/modules/attachments/AGENTS.md +79 -0
- package/src/modules/attachments/api/library/route.ts +2 -2
- package/src/modules/attachments/api/route.ts +2 -0
- package/src/modules/attachments/components/AttachmentContentPreview.tsx +6 -6
- package/src/modules/attachments/lib/access.ts +36 -0
- package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +14 -2
- package/src/modules/audit_logs/data/entities.ts +1 -0
- package/src/modules/audit_logs/migrations/.snapshot-open-mercato.json +10 -0
- package/src/modules/audit_logs/migrations/Migration20260611104500.ts +13 -0
- package/src/modules/audit_logs/services/accessLogService.ts +15 -0
- package/src/modules/auth/api/admin/nav.ts +9 -0
- package/src/modules/auth/api/login.ts +13 -13
- package/src/modules/auth/commands/users.ts +32 -15
- package/src/modules/auth/data/entities.ts +13 -1
- package/src/modules/auth/i18n/de.json +0 -1
- package/src/modules/auth/i18n/en.json +0 -1
- package/src/modules/auth/i18n/es.json +0 -1
- package/src/modules/auth/i18n/pl.json +0 -1
- package/src/modules/auth/lib/backendChrome.tsx +37 -1
- package/src/modules/auth/lib/consentIntegrity.ts +6 -3
- package/src/modules/auth/migrations/.snapshot-open-mercato.json +20 -10
- package/src/modules/auth/migrations/Migration20260610120000.ts +53 -0
- package/src/modules/auth/migrations/Migration20260611103000.ts +21 -0
- package/src/modules/auth/services/authService.ts +24 -4
- package/src/modules/auth/services/rbacService.ts +11 -2
- package/src/modules/catalog/ai-tools/configuration-pack.ts +1 -1
- package/src/modules/catalog/ai-tools/prices-offers-pack.ts +1 -1
- package/src/modules/catalog/ai-tools/products-pack.ts +1 -1
- package/src/modules/catalog/ai-tools/variants-pack.ts +1 -1
- package/src/modules/communication_channels/data/entities.ts +2 -2
- package/src/modules/communication_channels/encryption.ts +1 -1
- package/src/modules/communication_channels/lib/adapter.ts +1 -1
- package/src/modules/communication_channels/lib/thread-matcher.ts +1 -1
- package/src/modules/communication_channels/lib/thread-token.ts +1 -1
- package/src/modules/currencies/api/currencies/route.ts +4 -3
- package/src/modules/customer_accounts/api/admin/roles.ts +2 -1
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +0 -3
- package/src/modules/customer_accounts/events.ts +1 -1
- package/src/modules/customer_accounts/lib/resolveTenantContext.ts +2 -2
- package/src/modules/customers/acl.ts +1 -1
- package/src/modules/customers/ai-tools/companies-pack.ts +1 -1
- package/src/modules/customers/ai-tools/deals-pack.ts +1 -1
- package/src/modules/customers/ai-tools/people-pack.ts +1 -1
- package/src/modules/customers/api/companies/route.ts +4 -4
- package/src/modules/customers/api/deals/route.ts +51 -2
- package/src/modules/customers/api/deals/summary/route.ts +496 -0
- package/src/modules/customers/api/people/route.ts +4 -4
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.ts +28 -6
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealData.ts +33 -6
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +17 -2
- package/src/modules/customers/backend/customers/deals/page.tsx +254 -66
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +1 -2
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +18 -0
- package/src/modules/customers/cli.ts +15 -15
- package/src/modules/customers/commands/addresses.ts +5 -5
- package/src/modules/customers/commands/comments.ts +5 -5
- package/src/modules/customers/commands/deals.ts +2 -2
- package/src/modules/customers/commands/entity-roles.ts +2 -1
- package/src/modules/customers/commands/interactions.ts +8 -5
- package/src/modules/customers/commands/shared.ts +26 -4
- package/src/modules/customers/commands/tags.ts +3 -3
- package/src/modules/customers/components/DealsKpiStrip.tsx +389 -0
- package/src/modules/customers/components/detail/ConfirmDealLostDialog.tsx +0 -1
- package/src/modules/customers/components/detail/DealForm.tsx +121 -19
- package/src/modules/customers/components/detail/PersonDetailTabs.tsx +12 -2
- package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +1 -2
- package/src/modules/customers/components/detail/assignableStaff.ts +32 -8
- package/src/modules/customers/components/kpi/PipelineStageBar.tsx +77 -0
- package/src/modules/customers/i18n/de.json +43 -0
- package/src/modules/customers/i18n/en.json +43 -0
- package/src/modules/customers/i18n/es.json +43 -0
- package/src/modules/customers/i18n/pl.json +43 -0
- package/src/modules/customers/lib/dealsMetrics.ts +159 -0
- package/src/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.ts +1 -1
- package/src/modules/data_sync/api/run.ts +1 -1
- package/src/modules/directory/api/organization-branding/route.ts +238 -0
- package/src/modules/directory/api/organizations/route.ts +7 -0
- package/src/modules/directory/backend/directory/branding/page.meta.ts +24 -0
- package/src/modules/directory/backend/directory/branding/page.tsx +248 -0
- package/src/modules/directory/commands/organizations.ts +9 -1
- package/src/modules/directory/data/entities.ts +3 -0
- package/src/modules/directory/data/validators.ts +12 -0
- package/src/modules/directory/i18n/de.json +21 -0
- package/src/modules/directory/i18n/en.json +21 -0
- package/src/modules/directory/i18n/es.json +21 -0
- package/src/modules/directory/i18n/pl.json +21 -0
- package/src/modules/directory/migrations/.snapshot-open-mercato.json +40 -0
- package/src/modules/directory/migrations/Migration20260607222259_directory.ts +13 -0
- package/src/modules/directory/subscribers/invalidateOrgScopeCache.ts +3 -1
- package/src/modules/directory/utils/organizationScope.ts +85 -30
- package/src/modules/entities/api/definitions.batch.ts +11 -7
- package/src/modules/entities/api/entities.ts +11 -0
- package/src/modules/entities/api/records.ts +46 -25
- package/src/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.tsx +15 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/create/page.tsx +15 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +23 -0
- package/src/modules/entities/components/useRecordsEntityGuard.ts +41 -0
- package/src/modules/entities/i18n/de.json +1 -0
- package/src/modules/entities/i18n/en.json +1 -0
- package/src/modules/entities/i18n/es.json +1 -0
- package/src/modules/entities/i18n/pl.json +1 -0
- package/src/modules/payment_gateways/api/transactions/route.ts +2 -5
- package/src/modules/progress/api/jobs/[id]/route.ts +6 -1
- package/src/modules/progress/api/jobs/route.ts +1 -1
- package/src/modules/progress/lib/progressServiceImpl.ts +7 -1
- package/src/modules/query_index/data/entities.ts +1 -0
- package/src/modules/query_index/lib/engine.ts +11 -5
- package/src/modules/query_index/migrations/.snapshot-open-mercato.json +11 -0
- package/src/modules/query_index/migrations/Migration20260611103000_query_index.ts +29 -0
- package/src/modules/resources/api/resources.ts +2 -3
- package/src/modules/sales/api/documents/factory.ts +2 -2
- package/src/modules/sales/commands/documents.ts +7 -5
- package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -1
- package/src/modules/sales/components/documents/salesDocumentsColumns.ts +6 -0
- package/src/modules/staff/AGENTS.md +1 -1
- package/src/modules/staff/api/team-members.ts +9 -2
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +31 -1
- package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +18 -8
- package/src/modules/staff/commands/team-members.ts +5 -2
- package/src/modules/staff/components/TeamMemberForm.tsx +4 -1
- package/src/modules/staff/i18n/de.json +1 -0
- package/src/modules/staff/i18n/en.json +1 -0
- package/src/modules/staff/i18n/es.json +1 -0
- package/src/modules/staff/i18n/pl.json +1 -0
- package/src/modules/staff/lib/scheduleSwitch.ts +46 -0
- package/src/modules/sync_excel/api/import/route.ts +1 -1
- package/src/modules/workflows/api/definitions/route.ts +3 -2
- package/src/modules/workflows/backend/definitions/create/page.tsx +1 -2
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +1 -2
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -2
- package/src/modules/workflows/components/NodeEditDialog.tsx +1 -4
- package/src/modules/workflows/components/NodeEditDialogCrudForm.tsx +4 -7
- package/src/modules/workflows/components/WorkflowGraphImpl.tsx +1 -2
- package/src/modules/workflows/components/fields/FormFieldArrayEditor.tsx +2 -3
|
@@ -6,6 +6,7 @@ import type { QueryEngine, QueryOptions, Where, Sort } from '@open-mercato/share
|
|
|
6
6
|
import { normalizeExportFormat, serializeExport, defaultExportFilename, ensureColumns } from '@open-mercato/shared/lib/crud/exporters'
|
|
7
7
|
import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
|
|
8
8
|
import { resolveOrganizationScope, getSelectedOrganizationFromRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'
|
|
9
|
+
import { SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, isOrmBackedSystemEntityId } from '@open-mercato/shared/lib/data/engine'
|
|
9
10
|
import { parseBooleanToken, parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'
|
|
10
11
|
import { setRecordCustomFields } from '../lib/helpers'
|
|
11
12
|
import { CustomFieldValue } from '../data/entities'
|
|
@@ -36,24 +37,34 @@ function isDeclaredCustomEntity(entityId: string): boolean {
|
|
|
36
37
|
|
|
37
38
|
const CUSTOM_ENTITY_RECORD_RESOURCE_KIND = 'entities.record'
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
type RecordsEntityKind = 'system' | 'custom' | 'unknown'
|
|
41
|
+
|
|
42
|
+
// This surface manages doc-storage records, which exist for CUSTOM entities only.
|
|
43
|
+
// Module-declared ids backed by a registered ORM table are system entities — their
|
|
44
|
+
// records live in their own module tables/APIs, and stray doc rows for them poisoned
|
|
45
|
+
// read-path classification platform-wide (#2939) — so they are rejected outright. The
|
|
46
|
+
// previous fallback that classified an entity by the mere presence of
|
|
47
|
+
// `custom_entities_storage` rows is gone: within the allowed set, declaration (ce.ts)
|
|
48
|
+
// or an active `custom_entities` registration is authoritative.
|
|
49
|
+
async function classifyRecordsEntity(em: any, entityId: string): Promise<RecordsEntityKind> {
|
|
50
|
+
if (isOrmBackedSystemEntityId(em, entityId)) return 'system'
|
|
51
|
+
if (isDeclaredCustomEntity(entityId)) return 'custom'
|
|
41
52
|
try {
|
|
42
53
|
const { CustomEntity } = await import('../data/entities')
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
// Any registration row — active or soft-deleted — proves the id is a custom
|
|
55
|
+
// entity. Records persist beyond the definition's soft delete (TC-ENTITIES-006)
|
|
56
|
+
// and must stay readable/deletable, e.g. for the restore flow and cleanup.
|
|
57
|
+
const found = await em.findOne(CustomEntity as any, { entityId })
|
|
58
|
+
if (found) return 'custom'
|
|
45
59
|
} catch {}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return !!row
|
|
55
|
-
} catch {}
|
|
56
|
-
return false
|
|
60
|
+
return 'unknown'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function systemEntityRecordsRejection(entityId: string) {
|
|
64
|
+
return NextResponse.json(
|
|
65
|
+
{ error: 'Records are available for custom entities only', code: SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, entityId },
|
|
66
|
+
{ status: 400 },
|
|
67
|
+
)
|
|
57
68
|
}
|
|
58
69
|
|
|
59
70
|
async function readCustomEntityRecordUpdatedAt(
|
|
@@ -148,13 +159,13 @@ export async function GET(req: Request) {
|
|
|
148
159
|
const rbac = resolve('rbacService') as RbacService
|
|
149
160
|
const scope = await resolveOrganizationScope({ em, rbac, auth, selectedId: getSelectedOrganizationFromRequest(req) })
|
|
150
161
|
let organizationIds: string[] | null = scope.filterIds
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const isCustomEntity =
|
|
162
|
+
// Module-declared custom entities (ce.ts) carry frozen system-style ids and are never
|
|
163
|
+
// registered in `custom_entities`, so classification checks the declared registry plus
|
|
164
|
+
// active registrations. System (table-backed) ids are rejected above; for the allowed
|
|
165
|
+
// set `isCustomEntity` drives mapRow's cf_ stripping so the edit form reads back values.
|
|
166
|
+
const entityKind = await classifyRecordsEntity(em, entityId)
|
|
167
|
+
if (entityKind === 'system') return systemEntityRecordsRejection(entityId)
|
|
168
|
+
const isCustomEntity = entityKind === 'custom'
|
|
158
169
|
await assertEntityAclForRequest({ auth, entityId, action: 'view', isCustomEntity, rbac })
|
|
159
170
|
if (organizationIds && organizationIds.length === 0) {
|
|
160
171
|
return NextResponse.json({ items: [], total: 0, page, pageSize, totalPages: 0 })
|
|
@@ -230,6 +241,10 @@ export async function GET(req: Request) {
|
|
|
230
241
|
if (organizationIds && organizationIds.length) {
|
|
231
242
|
qopts.organizationIds = organizationIds
|
|
232
243
|
}
|
|
244
|
+
// Allowed entities are doc-storage-backed by definition (system ids were rejected
|
|
245
|
+
// above) — direct the engine to doc storage explicitly so reads stay deterministic
|
|
246
|
+
// even before the first record exists.
|
|
247
|
+
if (isCustomEntity) qopts.forceCustomEntityStorage = true
|
|
233
248
|
for (const [k, v] of qpEntries) buildFilter(k, v, isCustomEntity)
|
|
234
249
|
const res = await qe.query(entityId as any, qopts)
|
|
235
250
|
const rawItems = res.items || []
|
|
@@ -363,7 +378,9 @@ export async function POST(req: Request) {
|
|
|
363
378
|
const scope = await resolveOrganizationScope({ em, rbac, auth, selectedId: getSelectedOrganizationFromRequest(req) })
|
|
364
379
|
const targetOrgId = scope.selectedId ?? auth.orgId
|
|
365
380
|
if (!targetOrgId) return NextResponse.json({ error: 'Organization context is required' }, { status: 400 })
|
|
366
|
-
const
|
|
381
|
+
const entityKind = await classifyRecordsEntity(em, entityId)
|
|
382
|
+
if (entityKind === 'system') return systemEntityRecordsRejection(entityId)
|
|
383
|
+
const isCustomEntity = entityKind === 'custom'
|
|
367
384
|
await assertEntityAclForRequest({ auth, entityId, action: 'manage', isCustomEntity, rbac })
|
|
368
385
|
const norm = normalizeValues(values)
|
|
369
386
|
|
|
@@ -427,7 +444,9 @@ export async function PUT(req: Request) {
|
|
|
427
444
|
const scope = await resolveOrganizationScope({ em, rbac, auth, selectedId: getSelectedOrganizationFromRequest(req) })
|
|
428
445
|
const targetOrgId = scope.selectedId ?? auth.orgId
|
|
429
446
|
if (!targetOrgId) return NextResponse.json({ error: 'Organization context is required' }, { status: 400 })
|
|
430
|
-
const
|
|
447
|
+
const entityKind = await classifyRecordsEntity(em, entityId)
|
|
448
|
+
if (entityKind === 'system') return systemEntityRecordsRejection(entityId)
|
|
449
|
+
const isCustomEntity = entityKind === 'custom'
|
|
431
450
|
await assertEntityAclForRequest({ auth, entityId, action: 'manage', isCustomEntity, rbac })
|
|
432
451
|
const norm = normalizeValues(values)
|
|
433
452
|
|
|
@@ -516,7 +535,9 @@ export async function DELETE(req: Request) {
|
|
|
516
535
|
const scope = await resolveOrganizationScope({ em, rbac, auth, selectedId: getSelectedOrganizationFromRequest(req) })
|
|
517
536
|
const targetOrgId = scope.selectedId ?? auth.orgId
|
|
518
537
|
if (!targetOrgId) return NextResponse.json({ error: 'Organization context is required' }, { status: 400 })
|
|
519
|
-
const
|
|
538
|
+
const entityKind = await classifyRecordsEntity(em, entityId)
|
|
539
|
+
if (entityKind === 'system') return systemEntityRecordsRejection(entityId)
|
|
540
|
+
const isCustomEntity = entityKind === 'custom'
|
|
520
541
|
await assertEntityAclForRequest({ auth, entityId, action: 'manage', isCustomEntity, rbac })
|
|
521
542
|
await de.deleteCustomEntityRecord({ entityId, recordId, organizationId: targetOrgId, tenantId: auth.tenantId!, soft: true })
|
|
522
543
|
return NextResponse.json({ ok: true })
|
|
@@ -6,6 +6,8 @@ import { z } from 'zod'
|
|
|
6
6
|
import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
7
7
|
import { updateCrud } from '@open-mercato/ui/backend/utils/crud'
|
|
8
8
|
import { createCrudFormError, raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
|
|
9
|
+
import { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'
|
|
10
|
+
import { useRecordsEntityGuard } from '@open-mercato/core/modules/entities/components/useRecordsEntityGuard'
|
|
9
11
|
|
|
10
12
|
type UpdateRecordRequest = (payload: { entityId: string; recordId: string; values: Record<string, unknown> }) => Promise<void>
|
|
11
13
|
|
|
@@ -38,6 +40,19 @@ export async function submitCustomEntityRecordUpdate(options: {
|
|
|
38
40
|
type RecordsResponse = { items: any[] }
|
|
39
41
|
|
|
40
42
|
export default function EditRecordPage({ params }: { params: { entityId?: string; recordId?: string } }) {
|
|
43
|
+
const t = useT()
|
|
44
|
+
const entityId = decodeURIComponent(params?.entityId || '')
|
|
45
|
+
const guard = useRecordsEntityGuard(entityId)
|
|
46
|
+
if (guard === 'blocked') {
|
|
47
|
+
return <ErrorMessage label={t('entities.userEntities.records.errors.systemEntity', 'This entity is system-managed. Records are available for custom entities only.')} />
|
|
48
|
+
}
|
|
49
|
+
if (guard === 'checking') {
|
|
50
|
+
return <LoadingMessage label={t('entities.userEntities.records.loading', 'Loading records...')} />
|
|
51
|
+
}
|
|
52
|
+
return <EditRecordPageInner params={params} />
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function EditRecordPageInner({ params }: { params: { entityId?: string; recordId?: string } }) {
|
|
41
56
|
const t = useT()
|
|
42
57
|
const entityId = decodeURIComponent(params?.entityId || '')
|
|
43
58
|
const recordId = decodeURIComponent(params?.recordId || '')
|
|
@@ -6,6 +6,8 @@ import { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'
|
|
|
6
6
|
import { z } from 'zod'
|
|
7
7
|
import { createCrud } from '@open-mercato/ui/backend/utils/crud'
|
|
8
8
|
import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
|
|
9
|
+
import { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'
|
|
10
|
+
import { useRecordsEntityGuard } from '@open-mercato/core/modules/entities/components/useRecordsEntityGuard'
|
|
9
11
|
|
|
10
12
|
type CreateRecordRequest = (payload: { entityId: string; values: Record<string, unknown> }) => Promise<void>
|
|
11
13
|
|
|
@@ -30,6 +32,19 @@ export async function submitCustomEntityRecord(options: {
|
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
export default function CreateRecordPage({ params }: { params: { entityId?: string } }) {
|
|
35
|
+
const t = useT()
|
|
36
|
+
const entityId = decodeURIComponent(params?.entityId || '')
|
|
37
|
+
const guard = useRecordsEntityGuard(entityId)
|
|
38
|
+
if (guard === 'blocked') {
|
|
39
|
+
return <ErrorMessage label={t('entities.userEntities.records.errors.systemEntity', 'This entity is system-managed. Records are available for custom entities only.')} />
|
|
40
|
+
}
|
|
41
|
+
if (guard === 'checking') {
|
|
42
|
+
return <LoadingMessage label={t('entities.userEntities.records.loading', 'Loading records...')} />
|
|
43
|
+
}
|
|
44
|
+
return <CreateRecordPageInner params={params} />
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function CreateRecordPageInner({ params }: { params: { entityId?: string } }) {
|
|
33
48
|
const t = useT()
|
|
34
49
|
const router = useRouter()
|
|
35
50
|
const entityId = decodeURIComponent(params?.entityId || '')
|
|
@@ -17,6 +17,9 @@ import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
|
17
17
|
import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
|
|
18
18
|
import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
19
19
|
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
20
|
+
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
21
|
+
import { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'
|
|
22
|
+
import { useRecordsEntityGuard } from '@open-mercato/core/modules/entities/components/useRecordsEntityGuard'
|
|
20
23
|
|
|
21
24
|
type RecordsResponse = {
|
|
22
25
|
items: any[]
|
|
@@ -44,6 +47,26 @@ function normalizeCell(v: any): string {
|
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
export default function RecordsPage({ params }: { params: { entityId?: string } }) {
|
|
50
|
+
const t = useT()
|
|
51
|
+
const entityId = decodeURIComponent(params?.entityId || '')
|
|
52
|
+
const guard = useRecordsEntityGuard(entityId)
|
|
53
|
+
if (guard !== 'allowed') {
|
|
54
|
+
return (
|
|
55
|
+
<Page>
|
|
56
|
+
<PageBody>
|
|
57
|
+
{guard === 'blocked' ? (
|
|
58
|
+
<ErrorMessage label={t('entities.userEntities.records.errors.systemEntity', 'This entity is system-managed. Records are available for custom entities only.')} />
|
|
59
|
+
) : (
|
|
60
|
+
<LoadingMessage label={t('entities.userEntities.records.loading', 'Loading records...')} />
|
|
61
|
+
)}
|
|
62
|
+
</PageBody>
|
|
63
|
+
</Page>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
return <RecordsPageInner params={params} />
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function RecordsPageInner({ params }: { params: { entityId?: string } }) {
|
|
47
70
|
const entityId = decodeURIComponent(params?.entityId || '')
|
|
48
71
|
const [sorting, setSorting] = React.useState<SortingState>([{ id: 'id', desc: false }])
|
|
49
72
|
const [page, setPage] = React.useState(1)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
|
|
4
|
+
|
|
5
|
+
export type RecordsEntityGuardState = 'checking' | 'blocked' | 'allowed'
|
|
6
|
+
|
|
7
|
+
// Mirrors SYSTEM_ENTITY_RECORDS_BLOCKED_CODE from @open-mercato/shared/lib/data/engine —
|
|
8
|
+
// kept as a literal so client bundles do not pull the server-side data engine in.
|
|
9
|
+
const SYSTEM_ENTITY_RECORDS_BLOCKED_CODE = 'system_entity_records_blocked'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The records surface serves custom entities only; the API rejects system
|
|
13
|
+
* (table-backed) entity ids with 400 + `system_entity_records_blocked` (#2939
|
|
14
|
+
* hardening). Records pages are URL-addressable for any entity id, so they probe
|
|
15
|
+
* once and render a dedicated error state instead of a broken table/form.
|
|
16
|
+
* Fails open on transport errors — the page's own data calls surface those.
|
|
17
|
+
*/
|
|
18
|
+
export function useRecordsEntityGuard(entityId: string): RecordsEntityGuardState {
|
|
19
|
+
const [state, setState] = React.useState<RecordsEntityGuardState>(entityId ? 'checking' : 'allowed')
|
|
20
|
+
React.useEffect(() => {
|
|
21
|
+
if (!entityId) {
|
|
22
|
+
setState('allowed')
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
let cancelled = false
|
|
26
|
+
setState('checking')
|
|
27
|
+
apiCall<{ code?: string }>(`/api/entities/records?entityId=${encodeURIComponent(entityId)}&page=1&pageSize=1`)
|
|
28
|
+
.then((res) => {
|
|
29
|
+
if (cancelled) return
|
|
30
|
+
const blocked = res.status === 400 && res.result?.code === SYSTEM_ENTITY_RECORDS_BLOCKED_CODE
|
|
31
|
+
setState(blocked ? 'blocked' : 'allowed')
|
|
32
|
+
})
|
|
33
|
+
.catch(() => {
|
|
34
|
+
if (!cancelled) setState('allowed')
|
|
35
|
+
})
|
|
36
|
+
return () => {
|
|
37
|
+
cancelled = true
|
|
38
|
+
}
|
|
39
|
+
}, [entityId])
|
|
40
|
+
return state
|
|
41
|
+
}
|
|
@@ -82,6 +82,7 @@
|
|
|
82
82
|
"entities.userEntities.records.errors.deleteFailed": "Datensatz konnte nicht gelöscht werden",
|
|
83
83
|
"entities.userEntities.records.errors.entityIdRequired": "Entitätskennung ist erforderlich",
|
|
84
84
|
"entities.userEntities.records.errors.recordIdRequired": "Datensatzkennung ist erforderlich",
|
|
85
|
+
"entities.userEntities.records.errors.systemEntity": "Diese Entität wird vom System verwaltet. Datensätze sind nur für benutzerdefinierte Entitäten verfügbar.",
|
|
85
86
|
"entities.userEntities.records.form.createTitle": "Datensatz erstellen",
|
|
86
87
|
"entities.userEntities.records.form.editTitle": "Datensatz bearbeiten",
|
|
87
88
|
"entities.userEntities.records.form.submitCreate": "Erstellen",
|
|
@@ -82,6 +82,7 @@
|
|
|
82
82
|
"entities.userEntities.records.errors.deleteFailed": "Failed to delete record",
|
|
83
83
|
"entities.userEntities.records.errors.entityIdRequired": "Entity identifier is required",
|
|
84
84
|
"entities.userEntities.records.errors.recordIdRequired": "Record identifier is required",
|
|
85
|
+
"entities.userEntities.records.errors.systemEntity": "This entity is system-managed. Records are available for custom entities only.",
|
|
85
86
|
"entities.userEntities.records.form.createTitle": "Create record",
|
|
86
87
|
"entities.userEntities.records.form.editTitle": "Edit record",
|
|
87
88
|
"entities.userEntities.records.form.submitCreate": "Create",
|
|
@@ -82,6 +82,7 @@
|
|
|
82
82
|
"entities.userEntities.records.errors.deleteFailed": "No se pudo eliminar el registro",
|
|
83
83
|
"entities.userEntities.records.errors.entityIdRequired": "Se requiere el identificador de la entidad",
|
|
84
84
|
"entities.userEntities.records.errors.recordIdRequired": "Se requiere el identificador del registro",
|
|
85
|
+
"entities.userEntities.records.errors.systemEntity": "Esta entidad está gestionada por el sistema. Los registros solo están disponibles para entidades personalizadas.",
|
|
85
86
|
"entities.userEntities.records.form.createTitle": "Crear registro",
|
|
86
87
|
"entities.userEntities.records.form.editTitle": "Editar registro",
|
|
87
88
|
"entities.userEntities.records.form.submitCreate": "Crear",
|
|
@@ -82,6 +82,7 @@
|
|
|
82
82
|
"entities.userEntities.records.errors.deleteFailed": "Nie udało się usunąć rekordu",
|
|
83
83
|
"entities.userEntities.records.errors.entityIdRequired": "Wymagany jest identyfikator encji",
|
|
84
84
|
"entities.userEntities.records.errors.recordIdRequired": "Wymagany jest identyfikator rekordu",
|
|
85
|
+
"entities.userEntities.records.errors.systemEntity": "Ta encja jest zarządzana systemowo. Rekordy są dostępne wyłącznie dla encji niestandardowych.",
|
|
85
86
|
"entities.userEntities.records.form.createTitle": "Utwórz rekord",
|
|
86
87
|
"entities.userEntities.records.form.editTitle": "Edytuj rekord",
|
|
87
88
|
"entities.userEntities.records.form.submitCreate": "Utwórz",
|
|
@@ -5,16 +5,13 @@ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
|
5
5
|
import { GatewayTransaction } from '../../data/entities'
|
|
6
6
|
import { listTransactionsQuerySchema } from '../../data/validators'
|
|
7
7
|
import { paymentGatewaysTag } from '../openapi'
|
|
8
|
+
import { buildIlikeTerm } from '@open-mercato/shared/lib/db/buildIlikeTerm'
|
|
8
9
|
|
|
9
10
|
export const metadata = {
|
|
10
11
|
path: '/payment_gateways/transactions',
|
|
11
12
|
GET: { requireAuth: true, requireFeatures: ['payment_gateways.view'] },
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
function escapeLikePattern(value: string): string {
|
|
15
|
-
return value.replace(/[\\%_]/g, '\\$&')
|
|
16
|
-
}
|
|
17
|
-
|
|
18
15
|
function formatDateValue(value: unknown): string | null {
|
|
19
16
|
if (!value) return null
|
|
20
17
|
if (value instanceof Date) return value.toISOString()
|
|
@@ -58,7 +55,7 @@ export async function GET(req: Request) {
|
|
|
58
55
|
qb.andWhere({ unifiedStatus: status })
|
|
59
56
|
}
|
|
60
57
|
if (search) {
|
|
61
|
-
const pattern =
|
|
58
|
+
const pattern = buildIlikeTerm(search)
|
|
62
59
|
qb.andWhere(`(
|
|
63
60
|
cast(gt.id as text) ilike ?
|
|
64
61
|
or cast(gt.payment_id as text) ilike ?
|
|
@@ -26,6 +26,7 @@ export async function GET(req: Request, { params }: { params: { id: string } })
|
|
|
26
26
|
const job = await em.findOne(ProgressJob, {
|
|
27
27
|
id: params.id,
|
|
28
28
|
tenantId: auth.tenantId,
|
|
29
|
+
...(auth.orgId ? { organizationId: auth.orgId } : {}),
|
|
29
30
|
})
|
|
30
31
|
|
|
31
32
|
if (!job) {
|
|
@@ -74,7 +75,11 @@ export async function PUT(req: Request, { params }: { params: { id: string } })
|
|
|
74
75
|
|
|
75
76
|
const container = await createRequestContainer()
|
|
76
77
|
const em = container.resolve('em') as EntityManager
|
|
77
|
-
const existing = await em.findOne(ProgressJob, {
|
|
78
|
+
const existing = await em.findOne(ProgressJob, {
|
|
79
|
+
id: params.id,
|
|
80
|
+
tenantId: auth.tenantId,
|
|
81
|
+
...(auth.orgId ? { organizationId: auth.orgId } : {}),
|
|
82
|
+
})
|
|
78
83
|
if (!existing) {
|
|
79
84
|
return NextResponse.json({ error: 'Not found' }, { status: 404 })
|
|
80
85
|
}
|
|
@@ -165,7 +165,7 @@ export async function POST(req: Request) {
|
|
|
165
165
|
const stack = error instanceof Error ? error.stack : undefined
|
|
166
166
|
console.error('[progress.jobs.create] unhandled error', { message, stack })
|
|
167
167
|
return NextResponse.json(
|
|
168
|
-
{ error: 'Failed to create progress job.'
|
|
168
|
+
{ error: 'Failed to create progress job.' },
|
|
169
169
|
{ status: 500 },
|
|
170
170
|
)
|
|
171
171
|
}
|
|
@@ -75,7 +75,11 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
75
75
|
},
|
|
76
76
|
|
|
77
77
|
async updateProgress(jobId, input, ctx) {
|
|
78
|
-
const job = await em.findOneOrFail(ProgressJob, {
|
|
78
|
+
const job = await em.findOneOrFail(ProgressJob, {
|
|
79
|
+
id: jobId,
|
|
80
|
+
tenantId: ctx.tenantId,
|
|
81
|
+
...(ctx.organizationId ? { organizationId: ctx.organizationId } : {}),
|
|
82
|
+
})
|
|
79
83
|
if (job.status === 'completed' || job.status === 'failed' || job.status === 'cancelled') {
|
|
80
84
|
return job
|
|
81
85
|
}
|
|
@@ -197,6 +201,7 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
197
201
|
const job = await em.findOneOrFail(ProgressJob, {
|
|
198
202
|
id: jobId,
|
|
199
203
|
tenantId: ctx.tenantId,
|
|
204
|
+
...(ctx.organizationId ? { organizationId: ctx.organizationId } : {}),
|
|
200
205
|
cancellable: true,
|
|
201
206
|
status: { $in: ['pending', 'running'] },
|
|
202
207
|
})
|
|
@@ -279,6 +284,7 @@ export function createProgressService(em: EntityManager, eventBus: { emit: (even
|
|
|
279
284
|
return em.findOne(ProgressJob, {
|
|
280
285
|
id: jobId,
|
|
281
286
|
tenantId: ctx.tenantId,
|
|
287
|
+
...(ctx.organizationId ? { organizationId: ctx.organizationId } : {}),
|
|
282
288
|
})
|
|
283
289
|
},
|
|
284
290
|
|
|
@@ -254,6 +254,7 @@ export class IndexerStatusLog {
|
|
|
254
254
|
@Entity({ tableName: 'search_tokens' })
|
|
255
255
|
@Index({ name: 'search_tokens_lookup_idx', properties: ['entityType', 'field', 'tokenHash', 'tenantId', 'organizationId'] })
|
|
256
256
|
@Index({ name: 'search_tokens_entity_idx', properties: ['entityType', 'entityId'] })
|
|
257
|
+
@Index({ name: 'search_tokens_tenant_token_hash_idx', properties: ['tenantId', 'tokenHash'] })
|
|
257
258
|
export class SearchToken {
|
|
258
259
|
@PrimaryKey({ type: 'uuid', defaultRaw: 'gen_random_uuid()' })
|
|
259
260
|
id!: string
|
|
@@ -2,7 +2,7 @@ import type { QueryEngine, QueryOptions, QueryResult, FilterOp, Filter, QueryCus
|
|
|
2
2
|
import { SortDir } from '@open-mercato/shared/lib/query/types'
|
|
3
3
|
import type { EntityId } from '@open-mercato/shared/modules/entities'
|
|
4
4
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
5
|
-
import { BasicQueryEngine, resolveEntityTableName } from '@open-mercato/shared/lib/query/engine'
|
|
5
|
+
import { BasicQueryEngine, resolveEntityTableName, resolveRegisteredEntityTableName } from '@open-mercato/shared/lib/query/engine'
|
|
6
6
|
import { type Kysely, sql, type RawBuilder } from 'kysely'
|
|
7
7
|
import type { EventBus } from '@open-mercato/events'
|
|
8
8
|
import { readCoverageSnapshot, refreshCoverageSnapshot } from './coverage'
|
|
@@ -219,7 +219,7 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
219
219
|
const debugEnabled = this.isDebugVerbosity()
|
|
220
220
|
if (debugEnabled) this.debug('query:start', { entity })
|
|
221
221
|
|
|
222
|
-
const isCustom = await this.isCustomEntity(entity)
|
|
222
|
+
const isCustom = opts.forceCustomEntityStorage === true || await this.isCustomEntity(entity)
|
|
223
223
|
if (isCustom) {
|
|
224
224
|
if (debugEnabled) this.debug('query:custom-entity', { entity })
|
|
225
225
|
const section = profiler.section('custom_entity')
|
|
@@ -1005,6 +1005,14 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
1005
1005
|
.executeTakeFirst()
|
|
1006
1006
|
if (row) {
|
|
1007
1007
|
result = true
|
|
1008
|
+
} else if (resolveRegisteredEntityTableName(this.em, entity) !== null) {
|
|
1009
|
+
// An id backed by a registered ORM table is never doc-storage-backed by
|
|
1010
|
+
// inference: stray `custom_entities_storage` rows for such an id (e.g. written
|
|
1011
|
+
// through the generic entities data engine) must not hijack every list/detail
|
|
1012
|
+
// read for the whole entity type away from its base table (#2939). Surfaces
|
|
1013
|
+
// that intentionally read doc records for a dual-declared id pass
|
|
1014
|
+
// `forceCustomEntityStorage` in QueryOptions instead.
|
|
1015
|
+
result = false
|
|
1008
1016
|
} else {
|
|
1009
1017
|
// Read/write symmetry. Records written through the entities data engine
|
|
1010
1018
|
// (`de.createCustomEntityRecord`) always land in `custom_entities_storage`,
|
|
@@ -1012,9 +1020,7 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
1012
1020
|
// id — those are NEVER registered in `custom_entities` (install treats a
|
|
1013
1021
|
// system id as non-registrable). Without this fallback the query routes to
|
|
1014
1022
|
// the empty ORM/index path and those records are write-only (created with
|
|
1015
|
-
// 200 but unreadable on the edit form).
|
|
1016
|
-
// to `custom_entities_storage`, so this can only ever re-classify genuine
|
|
1017
|
-
// doc-storage entities — it cannot misroute table-backed entities.
|
|
1023
|
+
// 200 but unreadable on the edit form).
|
|
1018
1024
|
result = await this.hasCustomEntityStorageRows(entity)
|
|
1019
1025
|
}
|
|
1020
1026
|
} catch {
|
|
@@ -1354,6 +1354,17 @@
|
|
|
1354
1354
|
"primary": false,
|
|
1355
1355
|
"unique": false
|
|
1356
1356
|
},
|
|
1357
|
+
{
|
|
1358
|
+
"columnNames": [
|
|
1359
|
+
"tenant_id",
|
|
1360
|
+
"token_hash"
|
|
1361
|
+
],
|
|
1362
|
+
"composite": true,
|
|
1363
|
+
"constraint": false,
|
|
1364
|
+
"keyName": "search_tokens_tenant_token_hash_idx",
|
|
1365
|
+
"primary": false,
|
|
1366
|
+
"unique": false
|
|
1367
|
+
},
|
|
1357
1368
|
{
|
|
1358
1369
|
"columnNames": [
|
|
1359
1370
|
"id"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
|
|
3
|
+
// #2966: TokenSearchStrategy — the always-available global-search fallback —
|
|
4
|
+
// filters search_tokens by token_hash IN (...) AND tenant_id on every
|
|
5
|
+
// keystroke, but both existing indexes lead with entity_type (and the lookup
|
|
6
|
+
// index interposes field, which the query never filters), so the per-keystroke
|
|
7
|
+
// lookup degrades to a sequential scan as the table grows. Add a
|
|
8
|
+
// (tenant_id, token_hash)-leading index so it becomes an index scan.
|
|
9
|
+
//
|
|
10
|
+
// search_tokens is high-churn (rows scale with records × tokens), so the
|
|
11
|
+
// index is built CONCURRENTLY to avoid blocking writes during the build.
|
|
12
|
+
// CREATE INDEX CONCURRENTLY cannot run inside a transaction, hence
|
|
13
|
+
// isTransactional() => false; the migration runner applies migrations
|
|
14
|
+
// one-by-one, so this opt-out is safe.
|
|
15
|
+
export class Migration20260611103000_query_index extends Migration {
|
|
16
|
+
|
|
17
|
+
override isTransactional(): boolean {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
override up(): void | Promise<void> {
|
|
22
|
+
this.addSql(`create index concurrently if not exists "search_tokens_tenant_token_hash_idx" on "search_tokens" ("tenant_id", "token_hash");`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
override down(): void | Promise<void> {
|
|
26
|
+
this.addSql(`drop index if exists "search_tokens_tenant_token_hash_idx";`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
}
|
|
@@ -2,7 +2,7 @@ import { z } from 'zod'
|
|
|
2
2
|
import { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'
|
|
3
3
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
4
4
|
import { resolveCrudRecordId, parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'
|
|
5
|
-
import {
|
|
5
|
+
import { buildIlikeTerm } from '@open-mercato/shared/lib/db/buildIlikeTerm'
|
|
6
6
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
7
7
|
import { ResourcesResource, ResourcesResourceTagAssignment, ResourcesResourceTag } from '../data/entities'
|
|
8
8
|
import { resourcesResourceCreateSchema, resourcesResourceUpdateSchema } from '../data/validators'
|
|
@@ -107,8 +107,7 @@ const crud = makeCrudRoute({
|
|
|
107
107
|
}
|
|
108
108
|
const term = sanitizeSearchTerm(query.search)
|
|
109
109
|
if (term) {
|
|
110
|
-
|
|
111
|
-
filters[F.name] = { $ilike: like }
|
|
110
|
+
filters[F.name] = { $ilike: buildIlikeTerm(term) }
|
|
112
111
|
}
|
|
113
112
|
if (query.resourceTypeId) {
|
|
114
113
|
filters[F.resource_type_id] = query.resourceTypeId
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from '../openapi'
|
|
18
18
|
import { parseScopedCommandInput, resolveCrudRecordId } from '../utils'
|
|
19
19
|
import { documentUpdateSchema } from '../../commands/documents'
|
|
20
|
-
import {
|
|
20
|
+
import { buildIlikeTerm } from '@open-mercato/shared/lib/db/buildIlikeTerm'
|
|
21
21
|
import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'
|
|
22
22
|
import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
|
|
23
23
|
import { recalculateOrderTotalsForDisplay } from '../../commands/returns'
|
|
@@ -101,7 +101,7 @@ function buildFilters(query: ListQuery, numberColumn: string, kind: DocumentKind
|
|
|
101
101
|
const filters: Record<string, unknown> = {}
|
|
102
102
|
if (query.id) filters.id = { $eq: query.id }
|
|
103
103
|
if (query.search && query.search.trim().length > 0) {
|
|
104
|
-
const term =
|
|
104
|
+
const term = buildIlikeTerm(query.search.trim())
|
|
105
105
|
filters[numberColumn] = { $ilike: term }
|
|
106
106
|
}
|
|
107
107
|
if (query.customerId) {
|
|
@@ -719,11 +719,13 @@ async function resolveAddressSnapshot(
|
|
|
719
719
|
addressId?: string | null,
|
|
720
720
|
): Promise<Record<string, unknown> | null> {
|
|
721
721
|
if (!addressId) return null;
|
|
722
|
-
const address = await
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
tenantId,
|
|
726
|
-
|
|
722
|
+
const address = await findOneWithDecryption(
|
|
723
|
+
em,
|
|
724
|
+
CustomerAddress,
|
|
725
|
+
{ id: addressId, organizationId, tenantId },
|
|
726
|
+
undefined,
|
|
727
|
+
{ tenantId, organizationId },
|
|
728
|
+
);
|
|
727
729
|
if (!address) return null;
|
|
728
730
|
|
|
729
731
|
return {
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
createDictionaryMap,
|
|
24
24
|
normalizeDictionaryEntries,
|
|
25
25
|
} from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'
|
|
26
|
+
import { SALES_DOCUMENT_NUMBER_COLUMN_META } from './salesDocumentsColumns'
|
|
26
27
|
|
|
27
28
|
type SalesDocumentKind = 'order' | 'quote'
|
|
28
29
|
|
|
@@ -594,7 +595,7 @@ export function SalesDocumentsTable({ kind }: { kind: SalesDocumentKind }) {
|
|
|
594
595
|
) : null}
|
|
595
596
|
</div>
|
|
596
597
|
),
|
|
597
|
-
meta:
|
|
598
|
+
meta: SALES_DOCUMENT_NUMBER_COLUMN_META,
|
|
598
599
|
},
|
|
599
600
|
{
|
|
600
601
|
accessorKey: 'customerName',
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The `staff` module is **optional** and slated for extraction into a standalone `@open-mercato/staff` package published from the [official-modules](https://github.com/open-mercato/official-modules) repository. Core modules MUST NOT take direct dependencies on staff entities, helpers, or services — cross-module contact happens through the public surfaces listed below.
|
|
4
4
|
|
|
5
|
-
See [`.ai/specs/2026-05-08-staff-decouple-from-core.md`](../../../../../.ai/specs/2026-05-08-staff-decouple-from-core.md) for the decoupling plan, and [`BACKWARD_COMPATIBILITY.md`](../../../../../BACKWARD_COMPATIBILITY.md) for the contract-surface taxonomy referenced below.
|
|
5
|
+
See [`.ai/specs/implemented/2026-05-08-staff-decouple-from-core.md`](../../../../../.ai/specs/implemented/2026-05-08-staff-decouple-from-core.md) for the decoupling plan, and [`BACKWARD_COMPATIBILITY.md`](../../../../../BACKWARD_COMPATIBILITY.md) for the contract-surface taxonomy referenced below.
|
|
6
6
|
|
|
7
7
|
## MUST Rules
|
|
8
8
|
|
|
@@ -225,7 +225,12 @@ const crud = makeCrudRoute({
|
|
|
225
225
|
const { translate } = await resolveTranslations()
|
|
226
226
|
return parseScopedCommandInput(staffTeamMemberUpdateSchema, raw ?? {}, ctx, translate)
|
|
227
227
|
},
|
|
228
|
-
|
|
228
|
+
// Surface the freshly-bumped updatedAt so inline (non-CrudForm) callers can
|
|
229
|
+
// refresh their optimistic-lock token between sequential edits (#2848).
|
|
230
|
+
response: (arg: { result?: { updatedAt?: string | null } | null }) => ({
|
|
231
|
+
ok: true,
|
|
232
|
+
updatedAt: arg?.result?.updatedAt ?? null,
|
|
233
|
+
}),
|
|
229
234
|
},
|
|
230
235
|
delete: {
|
|
231
236
|
commandId: 'staff.team-members.delete',
|
|
@@ -287,7 +292,9 @@ export const openApi = createStaffCrudOpenApi({
|
|
|
287
292
|
},
|
|
288
293
|
update: {
|
|
289
294
|
schema: staffTeamMemberUpdateSchema,
|
|
290
|
-
responseSchema: defaultOkResponseSchema
|
|
295
|
+
responseSchema: defaultOkResponseSchema.extend({
|
|
296
|
+
updatedAt: z.string().nullable().optional(),
|
|
297
|
+
}),
|
|
291
298
|
description: 'Updates a team member by id.',
|
|
292
299
|
},
|
|
293
300
|
del: {
|