@open-mercato/core 0.8.1-develop.7297.1.67605e74f9 → 0.9.0
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 +2 -2
- package/AGENTS.md +4 -5
- package/dist/generated/entities/availability_policy/index.js +41 -0
- package/dist/generated/entities/availability_policy/index.js.map +7 -0
- package/dist/generated/entities/communication_channel/index.js +3 -1
- package/dist/generated/entities/communication_channel/index.js.map +2 -2
- package/dist/generated/entities/customer_email_conversation_share/index.js +21 -0
- package/dist/generated/entities/customer_email_conversation_share/index.js.map +7 -0
- package/dist/generated/entities/customer_group/index.js +33 -0
- package/dist/generated/entities/customer_group/index.js.map +7 -0
- package/dist/generated/entities/customer_group_membership/index.js +29 -0
- package/dist/generated/entities/customer_group_membership/index.js.map +7 -0
- package/dist/generated/entities/customer_group_terms/index.js +33 -0
- package/dist/generated/entities/customer_group_terms/index.js.map +7 -0
- package/dist/generated/entities/customer_interaction/index.js +2 -0
- package/dist/generated/entities/customer_interaction/index.js.map +2 -2
- package/dist/generated/entities/form/index.js +35 -0
- package/dist/generated/entities/form/index.js.map +7 -0
- package/dist/generated/entities/form_access_audit/index.js +21 -0
- package/dist/generated/entities/form_access_audit/index.js.map +7 -0
- package/dist/generated/entities/form_attachment/index.js +29 -0
- package/dist/generated/entities/form_attachment/index.js.map +7 -0
- package/dist/generated/entities/form_consent_record/index.js +37 -0
- package/dist/generated/entities/form_consent_record/index.js.map +7 -0
- package/dist/generated/entities/form_distribution/index.js +47 -0
- package/dist/generated/entities/form_distribution/index.js.map +7 -0
- package/dist/generated/entities/form_invitation/index.js +49 -0
- package/dist/generated/entities/form_invitation/index.js.map +7 -0
- package/dist/generated/entities/form_submission/index.js +39 -0
- package/dist/generated/entities/form_submission/index.js.map +7 -0
- package/dist/generated/entities/form_submission_actor/index.js +23 -0
- package/dist/generated/entities/form_submission_actor/index.js.map +7 -0
- package/dist/generated/entities/form_submission_revision/index.js +29 -0
- package/dist/generated/entities/form_submission_revision/index.js.map +7 -0
- package/dist/generated/entities/form_version/index.js +37 -0
- package/dist/generated/entities/form_version/index.js.map +7 -0
- package/dist/generated/entities/forms_encryption_key/index.js +15 -0
- package/dist/generated/entities/forms_encryption_key/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +27 -2
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +269 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/communicationChannelsFixtures.js +16 -0
- package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
- package/dist/helpers/integration/customerGroupsFixtures.js +88 -0
- package/dist/helpers/integration/customerGroupsFixtures.js.map +7 -0
- package/dist/helpers/integration/formsFixtures.js +448 -0
- package/dist/helpers/integration/formsFixtures.js.map +7 -0
- package/dist/modules/api_docs/api/get/version.js +19 -7
- package/dist/modules/api_docs/api/get/version.js.map +2 -2
- package/dist/modules/api_keys/api/keys/route.js +74 -169
- package/dist/modules/api_keys/api/keys/route.js.map +2 -2
- package/dist/modules/api_keys/backend/api-keys/create/page.js +1 -1
- package/dist/modules/api_keys/backend/api-keys/create/page.js.map +2 -2
- package/dist/modules/api_keys/commands/keys.js +140 -0
- package/dist/modules/api_keys/commands/keys.js.map +7 -0
- package/dist/modules/api_keys/services/apiKeyService.js +72 -43
- package/dist/modules/api_keys/services/apiKeyService.js.map +2 -2
- package/dist/modules/attachments/api/file/[id]/route.js +9 -2
- package/dist/modules/attachments/api/file/[id]/route.js.map +2 -2
- package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +6 -2
- package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
- package/dist/modules/attachments/api/route.js +10 -4
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentLibrary.js +16 -6
- package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
- package/dist/modules/attachments/lib/access.js +1 -3
- package/dist/modules/attachments/lib/access.js.map +2 -2
- package/dist/modules/attachments/lib/requestScope.js +13 -4
- package/dist/modules/attachments/lib/requestScope.js.map +2 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/export/route.js +6 -1
- package/dist/modules/audit_logs/api/audit-logs/actions/export/route.js.map +2 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +36 -6
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +3 -3
- package/dist/modules/audit_logs/api/audit-logs/actions/route.js +6 -1
- package/dist/modules/audit_logs/api/audit-logs/actions/route.js.map +2 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/undo/route.js +34 -4
- package/dist/modules/audit_logs/api/audit-logs/actions/undo/route.js.map +2 -2
- package/dist/modules/audit_logs/data/validators.js +1 -0
- package/dist/modules/audit_logs/data/validators.js.map +2 -2
- package/dist/modules/audit_logs/lib/actorSubject.js +65 -0
- package/dist/modules/audit_logs/lib/actorSubject.js.map +7 -0
- package/dist/modules/audit_logs/lib/replayAuthorization.js +41 -0
- package/dist/modules/audit_logs/lib/replayAuthorization.js.map +7 -0
- package/dist/modules/audit_logs/services/actionLogService.js +162 -39
- package/dist/modules/audit_logs/services/actionLogService.js.map +3 -3
- package/dist/modules/auth/api/features.js +1 -1
- package/dist/modules/auth/api/features.js.map +2 -2
- package/dist/modules/auth/cli.js +29 -17
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/auth/commands/acl.js +25 -36
- package/dist/modules/auth/commands/acl.js.map +2 -2
- package/dist/modules/auth/commands/interceptors.js +22 -0
- package/dist/modules/auth/commands/interceptors.js.map +7 -0
- package/dist/modules/auth/commands/roles.js +421 -147
- package/dist/modules/auth/commands/roles.js.map +2 -2
- package/dist/modules/auth/commands/users.js +543 -69
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/lib/authorizationStateLocks.js +249 -0
- package/dist/modules/auth/lib/authorizationStateLocks.js.map +7 -0
- package/dist/modules/auth/lib/commandReplay.js +144 -0
- package/dist/modules/auth/lib/commandReplay.js.map +7 -0
- package/dist/modules/auth/lib/executionPrincipal.js +32 -25
- package/dist/modules/auth/lib/executionPrincipal.js.map +2 -2
- package/dist/modules/auth/lib/grantChecks.js +7 -2
- package/dist/modules/auth/lib/grantChecks.js.map +2 -2
- package/dist/modules/auth/lib/setup-app.js +85 -49
- package/dist/modules/auth/lib/setup-app.js.map +2 -2
- package/dist/modules/auth/lib/tokenHash.js +13 -1
- package/dist/modules/auth/lib/tokenHash.js.map +2 -2
- package/dist/modules/auth/services/rbacService.js +386 -5
- package/dist/modules/auth/services/rbacService.js.map +3 -3
- package/dist/modules/availability/acl.js +16 -0
- package/dist/modules/availability/acl.js.map +7 -0
- package/dist/modules/availability/api/check/route.js +115 -0
- package/dist/modules/availability/api/check/route.js.map +7 -0
- package/dist/modules/availability/api/openapi.js +27 -0
- package/dist/modules/availability/api/openapi.js.map +7 -0
- package/dist/modules/availability/api/policies/resolve-preview/route.js +72 -0
- package/dist/modules/availability/api/policies/resolve-preview/route.js.map +7 -0
- package/dist/modules/availability/api/policies/route.js +203 -0
- package/dist/modules/availability/api/policies/route.js.map +7 -0
- package/dist/modules/availability/backend/availability/check/page.js +186 -0
- package/dist/modules/availability/backend/availability/check/page.js.map +7 -0
- package/dist/modules/availability/backend/availability/check/page.meta.js +16 -0
- package/dist/modules/availability/backend/availability/check/page.meta.js.map +7 -0
- package/dist/modules/availability/backend/availability/policies/ResolutionPreviewPanel.js +83 -0
- package/dist/modules/availability/backend/availability/policies/ResolutionPreviewPanel.js.map +7 -0
- package/dist/modules/availability/backend/availability/policies/ScopeFields.js +53 -0
- package/dist/modules/availability/backend/availability/policies/ScopeFields.js.map +7 -0
- package/dist/modules/availability/backend/availability/policies/StockManagedField.js +32 -0
- package/dist/modules/availability/backend/availability/policies/StockManagedField.js.map +7 -0
- package/dist/modules/availability/backend/availability/policies/[id]/page.js +168 -0
- package/dist/modules/availability/backend/availability/policies/[id]/page.js.map +7 -0
- package/dist/modules/availability/backend/availability/policies/[id]/page.meta.js +12 -0
- package/dist/modules/availability/backend/availability/policies/[id]/page.meta.js.map +7 -0
- package/dist/modules/availability/backend/availability/policies/create/page.js +122 -0
- package/dist/modules/availability/backend/availability/policies/create/page.js.map +7 -0
- package/dist/modules/availability/backend/availability/policies/create/page.meta.js +12 -0
- package/dist/modules/availability/backend/availability/policies/create/page.meta.js.map +7 -0
- package/dist/modules/availability/backend/availability/policies/formGroups.js +77 -0
- package/dist/modules/availability/backend/availability/policies/formGroups.js.map +7 -0
- package/dist/modules/availability/backend/availability/policies/page.js +207 -0
- package/dist/modules/availability/backend/availability/policies/page.js.map +7 -0
- package/dist/modules/availability/backend/availability/policies/page.meta.js +16 -0
- package/dist/modules/availability/backend/availability/policies/page.meta.js.map +7 -0
- package/dist/modules/availability/backend/availability/policies/policyPayload.js +28 -0
- package/dist/modules/availability/backend/availability/policies/policyPayload.js.map +7 -0
- package/dist/modules/availability/commands/policies.js +364 -0
- package/dist/modules/availability/commands/policies.js.map +7 -0
- package/dist/modules/availability/commands/scope.js +19 -0
- package/dist/modules/availability/commands/scope.js.map +7 -0
- package/dist/modules/availability/data/entities.js +94 -0
- package/dist/modules/availability/data/entities.js.map +7 -0
- package/dist/modules/availability/data/validators.js +59 -0
- package/dist/modules/availability/data/validators.js.map +7 -0
- package/dist/modules/availability/di.js +50 -0
- package/dist/modules/availability/di.js.map +7 -0
- package/dist/modules/availability/events.js +18 -0
- package/dist/modules/availability/events.js.map +7 -0
- package/dist/modules/availability/index.js +15 -0
- package/dist/modules/availability/index.js.map +7 -0
- package/dist/modules/availability/lib/catalogTarget.js +42 -0
- package/dist/modules/availability/lib/catalogTarget.js.map +7 -0
- package/dist/modules/availability/lib/organizationScope.js +33 -0
- package/dist/modules/availability/lib/organizationScope.js.map +7 -0
- package/dist/modules/availability/lib/policyResolution.js +150 -0
- package/dist/modules/availability/lib/policyResolution.js.map +7 -0
- package/dist/modules/availability/lib/tryResolve.js +11 -0
- package/dist/modules/availability/lib/tryResolve.js.map +7 -0
- package/dist/modules/availability/migrations/Migration20260922095844_availability.js +16 -0
- package/dist/modules/availability/migrations/Migration20260922095844_availability.js.map +7 -0
- package/dist/modules/availability/migrations/Migration20260930120000_availability.js +20 -0
- package/dist/modules/availability/migrations/Migration20260930120000_availability.js.map +7 -0
- package/dist/modules/availability/setup.js +12 -0
- package/dist/modules/availability/setup.js.map +7 -0
- package/dist/modules/catalog/api/categories/bulk-create/route.js +124 -0
- package/dist/modules/catalog/api/categories/bulk-create/route.js.map +7 -0
- package/dist/modules/catalog/api/categories/route.js +56 -8
- package/dist/modules/catalog/api/categories/route.js.map +2 -2
- package/dist/modules/catalog/api/products/bulk-create/route.js +124 -0
- package/dist/modules/catalog/api/products/bulk-create/route.js.map +7 -0
- package/dist/modules/catalog/api/products/route.js +55 -31
- package/dist/modules/catalog/api/products/route.js.map +3 -3
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +3 -6
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/create/page.js +12 -4
- package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
- package/dist/modules/catalog/commands/categories.js +1 -0
- package/dist/modules/catalog/commands/categories.js.map +2 -2
- package/dist/modules/catalog/commands/products.js +5 -2
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/catalog/components/PriceKindSettings.js +5 -19
- package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
- package/dist/modules/catalog/components/prices/PriceScopeSelectors.js +2 -2
- package/dist/modules/catalog/components/prices/PriceScopeSelectors.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductUomSection.js +26 -29
- package/dist/modules/catalog/components/products/ProductUomSection.js.map +2 -2
- package/dist/modules/catalog/components/products/productForm.js +3 -2
- package/dist/modules/catalog/components/products/productForm.js.map +2 -2
- package/dist/modules/catalog/data/validators.js +15 -0
- package/dist/modules/catalog/data/validators.js.map +2 -2
- package/dist/modules/catalog/di.js +3 -2
- package/dist/modules/catalog/di.js.map +2 -2
- package/dist/modules/catalog/events.js +9 -3
- package/dist/modules/catalog/events.js.map +2 -2
- package/dist/modules/catalog/lib/bulkCreateCategories.js +237 -0
- package/dist/modules/catalog/lib/bulkCreateCategories.js.map +7 -0
- package/dist/modules/catalog/lib/bulkCreateCheckpoint.js +82 -0
- package/dist/modules/catalog/lib/bulkCreateCheckpoint.js.map +7 -0
- package/dist/modules/catalog/lib/bulkCreateMutationGuards.js +39 -0
- package/dist/modules/catalog/lib/bulkCreateMutationGuards.js.map +7 -0
- package/dist/modules/catalog/lib/bulkCreateProducts.js +241 -0
- package/dist/modules/catalog/lib/bulkCreateProducts.js.map +7 -0
- package/dist/modules/catalog/lib/bulkDelete.js +10 -11
- package/dist/modules/catalog/lib/bulkDelete.js.map +2 -2
- package/dist/modules/catalog/lib/catalogQueue.js +14 -0
- package/dist/modules/catalog/lib/catalogQueue.js.map +7 -0
- package/dist/modules/catalog/lib/currencyOptions.js +25 -0
- package/dist/modules/catalog/lib/currencyOptions.js.map +7 -0
- package/dist/modules/catalog/lib/productSearch.js +8 -0
- package/dist/modules/catalog/lib/productSearch.js.map +7 -0
- package/dist/modules/catalog/lib/seeds.js +2 -42
- package/dist/modules/catalog/lib/seeds.js.map +2 -2
- package/dist/modules/catalog/lib/unitLabels.js +53 -0
- package/dist/modules/catalog/lib/unitLabels.js.map +7 -0
- package/dist/modules/catalog/migrations/Migration20260914120000.js +29 -0
- package/dist/modules/catalog/migrations/Migration20260914120000.js.map +7 -0
- package/dist/modules/catalog/migrations/Migration20260914120100.js +28 -0
- package/dist/modules/catalog/migrations/Migration20260914120100.js.map +7 -0
- package/dist/modules/catalog/workers/catalog-category-bulk-create.js +40 -0
- package/dist/modules/catalog/workers/catalog-category-bulk-create.js.map +7 -0
- package/dist/modules/catalog/workers/catalog-product-bulk-create.js +40 -0
- package/dist/modules/catalog/workers/catalog-product-bulk-create.js.map +7 -0
- package/dist/modules/communication_channels/acl.js +8 -0
- package/dist/modules/communication_channels/acl.js.map +2 -2
- package/dist/modules/communication_channels/api/get/me/channels/route.js +4 -0
- package/dist/modules/communication_channels/api/get/me/channels/route.js.map +2 -2
- package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js +22 -0
- package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js +4 -4
- package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +2 -1
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js +90 -20
- package/dist/modules/communication_channels/api/post/test-seed/route.js.map +3 -3
- package/dist/modules/communication_channels/api/put/channels/[id]/visibility/route.js +137 -0
- package/dist/modules/communication_channels/api/put/channels/[id]/visibility/route.js.map +7 -0
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +175 -107
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +2 -2
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js +2 -1
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +2 -2
- package/dist/modules/communication_channels/commands/set-channel-visibility.js +88 -0
- package/dist/modules/communication_channels/commands/set-channel-visibility.js.map +7 -0
- package/dist/modules/communication_channels/data/enrichers.js +18 -9
- package/dist/modules/communication_channels/data/enrichers.js.map +2 -2
- package/dist/modules/communication_channels/data/entities.js +4 -0
- package/dist/modules/communication_channels/data/entities.js.map +2 -2
- package/dist/modules/communication_channels/di.js +9 -1
- package/dist/modules/communication_channels/di.js.map +2 -2
- package/dist/modules/communication_channels/events.js +7 -0
- package/dist/modules/communication_channels/events.js.map +2 -2
- package/dist/modules/communication_channels/index.js +1 -1
- package/dist/modules/communication_channels/index.js.map +1 -1
- package/dist/modules/communication_channels/lib/access-control.js +2 -1
- package/dist/modules/communication_channels/lib/access-control.js.map +2 -2
- package/dist/modules/communication_channels/lib/channel-thread-access.js +60 -0
- package/dist/modules/communication_channels/lib/channel-thread-access.js.map +7 -0
- package/dist/modules/communication_channels/lib/credential-refresh.js +11 -2
- package/dist/modules/communication_channels/lib/credential-refresh.js.map +2 -2
- package/dist/modules/communication_channels/lib/inbound-message-origin.js +18 -0
- package/dist/modules/communication_channels/lib/inbound-message-origin.js.map +7 -0
- package/dist/modules/communication_channels/lib/oauth-state.js +37 -0
- package/dist/modules/communication_channels/lib/oauth-state.js.map +2 -2
- package/dist/modules/communication_channels/lib/outbound-delivery-intent.js +12 -0
- package/dist/modules/communication_channels/lib/outbound-delivery-intent.js.map +7 -0
- package/dist/modules/communication_channels/lib/send-as-user.js +1 -1
- package/dist/modules/communication_channels/lib/send-as-user.js.map +2 -2
- package/dist/modules/communication_channels/migrations/Migration20260825141000_communication_channels.js +14 -0
- package/dist/modules/communication_channels/migrations/Migration20260825141000_communication_channels.js.map +7 -0
- package/dist/modules/communication_channels/setup.js +6 -2
- package/dist/modules/communication_channels/setup.js.map +2 -2
- package/dist/modules/communication_channels/subscribers/outbound-bridge.js +8 -2
- package/dist/modules/communication_channels/subscribers/outbound-bridge.js.map +2 -2
- package/dist/modules/communication_channels/workers/gmail-history-sync.js +1 -1
- package/dist/modules/communication_channels/workers/gmail-history-sync.js.map +2 -2
- package/dist/modules/communication_channels/workers/poll-channel.js +1 -1
- package/dist/modules/communication_channels/workers/poll-channel.js.map +2 -2
- package/dist/modules/communication_channels/workers/reaction-processor.js +2 -2
- package/dist/modules/communication_channels/workers/reaction-processor.js.map +2 -2
- package/dist/modules/configs/api/upgrade-actions/route.js +2 -1
- package/dist/modules/configs/api/upgrade-actions/route.js.map +2 -2
- package/dist/modules/configs/cli.js +2 -2
- package/dist/modules/configs/cli.js.map +2 -2
- package/dist/modules/configs/lib/upgrade-actions.js +28 -6
- package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
- package/dist/modules/configs/services/upgradeActionsService.js +44 -30
- package/dist/modules/configs/services/upgradeActionsService.js.map +2 -2
- package/dist/modules/currencies/api/currencies/route.js +11 -5
- package/dist/modules/currencies/api/currencies/route.js.map +2 -2
- package/dist/modules/currencies/api/exchange-rates/route.js +11 -5
- package/dist/modules/currencies/api/exchange-rates/route.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/users/[id]/sessions/[sessionId].js +78 -0
- package/dist/modules/customer_accounts/api/admin/users/[id]/sessions/[sessionId].js.map +7 -0
- package/dist/modules/customer_groups/acl.js +14 -0
- package/dist/modules/customer_groups/acl.js.map +7 -0
- package/dist/modules/customer_groups/api/customer-groups/[id]/terms/route.js +266 -0
- package/dist/modules/customer_groups/api/customer-groups/[id]/terms/route.js.map +7 -0
- package/dist/modules/customer_groups/api/customer-groups/crud.js +473 -0
- package/dist/modules/customer_groups/api/customer-groups/crud.js.map +7 -0
- package/dist/modules/customer_groups/api/customer-groups/explain-terms/route.js +143 -0
- package/dist/modules/customer_groups/api/customer-groups/explain-terms/route.js.map +7 -0
- package/dist/modules/customer_groups/api/customer-groups/memberships/crud.js +383 -0
- package/dist/modules/customer_groups/api/customer-groups/memberships/crud.js.map +7 -0
- package/dist/modules/customer_groups/api/customer-groups/memberships/route.js +66 -0
- package/dist/modules/customer_groups/api/customer-groups/memberships/route.js.map +7 -0
- package/dist/modules/customer_groups/api/customer-groups/price-kinds/route.js +90 -0
- package/dist/modules/customer_groups/api/customer-groups/price-kinds/route.js.map +7 -0
- package/dist/modules/customer_groups/api/customer-groups/reconcile/adopt/route.js +133 -0
- package/dist/modules/customer_groups/api/customer-groups/reconcile/adopt/route.js.map +7 -0
- package/dist/modules/customer_groups/api/customer-groups/reconcile/route.js +80 -0
- package/dist/modules/customer_groups/api/customer-groups/reconcile/route.js.map +7 -0
- package/dist/modules/customer_groups/api/customer-groups/reorder/route.js +127 -0
- package/dist/modules/customer_groups/api/customer-groups/reorder/route.js.map +7 -0
- package/dist/modules/customer_groups/api/customer-groups/route.js +62 -0
- package/dist/modules/customer_groups/api/customer-groups/route.js.map +7 -0
- package/dist/modules/customer_groups/api/openapi.js +27 -0
- package/dist/modules/customer_groups/api/openapi.js.map +7 -0
- package/dist/modules/customer_groups/backend/customer-groups/[id]/edit/page.js +328 -0
- package/dist/modules/customer_groups/backend/customer-groups/[id]/edit/page.js.map +7 -0
- package/dist/modules/customer_groups/backend/customer-groups/[id]/edit/page.meta.js +16 -0
- package/dist/modules/customer_groups/backend/customer-groups/[id]/edit/page.meta.js.map +7 -0
- package/dist/modules/customer_groups/backend/customer-groups/create/page.js +249 -0
- package/dist/modules/customer_groups/backend/customer-groups/create/page.js.map +7 -0
- package/dist/modules/customer_groups/backend/customer-groups/create/page.meta.js +27 -0
- package/dist/modules/customer_groups/backend/customer-groups/create/page.meta.js.map +7 -0
- package/dist/modules/customer_groups/backend/customer-groups/orphans/page.js +139 -0
- package/dist/modules/customer_groups/backend/customer-groups/orphans/page.js.map +7 -0
- package/dist/modules/customer_groups/backend/customer-groups/orphans/page.meta.js +16 -0
- package/dist/modules/customer_groups/backend/customer-groups/orphans/page.meta.js.map +7 -0
- package/dist/modules/customer_groups/backend/customer-groups/page.js +368 -0
- package/dist/modules/customer_groups/backend/customer-groups/page.js.map +7 -0
- package/dist/modules/customer_groups/backend/customer-groups/page.meta.js +15 -0
- package/dist/modules/customer_groups/backend/customer-groups/page.meta.js.map +7 -0
- package/dist/modules/customer_groups/cli.js +72 -0
- package/dist/modules/customer_groups/cli.js.map +7 -0
- package/dist/modules/customer_groups/commands/reorderGroups.js +96 -0
- package/dist/modules/customer_groups/commands/reorderGroups.js.map +7 -0
- package/dist/modules/customer_groups/components/CustomerGroupDefaultField.js +41 -0
- package/dist/modules/customer_groups/components/CustomerGroupDefaultField.js.map +7 -0
- package/dist/modules/customer_groups/components/CustomerGroupParentField.js +156 -0
- package/dist/modules/customer_groups/components/CustomerGroupParentField.js.map +7 -0
- package/dist/modules/customer_groups/components/CustomerGroupTermsPriceKindField.js +94 -0
- package/dist/modules/customer_groups/components/CustomerGroupTermsPriceKindField.js.map +7 -0
- package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js +199 -0
- package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js.map +7 -0
- package/dist/modules/customer_groups/components/GroupPickerField.js +116 -0
- package/dist/modules/customer_groups/components/GroupPickerField.js.map +7 -0
- package/dist/modules/customer_groups/components/OrphanBanner.js +75 -0
- package/dist/modules/customer_groups/components/OrphanBanner.js.map +7 -0
- package/dist/modules/customer_groups/components/customerGroupTree.js +57 -0
- package/dist/modules/customer_groups/components/customerGroupTree.js.map +7 -0
- package/dist/modules/customer_groups/data/entities.js +226 -0
- package/dist/modules/customer_groups/data/entities.js.map +7 -0
- package/dist/modules/customer_groups/data/validators.js +153 -0
- package/dist/modules/customer_groups/data/validators.js.map +7 -0
- package/dist/modules/customer_groups/di.js +20 -0
- package/dist/modules/customer_groups/di.js.map +7 -0
- package/dist/modules/customer_groups/events.js +22 -0
- package/dist/modules/customer_groups/events.js.map +7 -0
- package/dist/modules/customer_groups/index.js +15 -0
- package/dist/modules/customer_groups/index.js.map +7 -0
- package/dist/modules/customer_groups/lib/createConflictRecheck.js +29 -0
- package/dist/modules/customer_groups/lib/createConflictRecheck.js.map +7 -0
- package/dist/modules/customer_groups/lib/customerScope.js +81 -0
- package/dist/modules/customer_groups/lib/customerScope.js.map +7 -0
- package/dist/modules/customer_groups/lib/groupEvents.js +31 -0
- package/dist/modules/customer_groups/lib/groupEvents.js.map +7 -0
- package/dist/modules/customer_groups/lib/priceKindScope.js +50 -0
- package/dist/modules/customer_groups/lib/priceKindScope.js.map +7 -0
- package/dist/modules/customer_groups/lib/reconcile.js +161 -0
- package/dist/modules/customer_groups/lib/reconcile.js.map +7 -0
- package/dist/modules/customer_groups/migrations/Migration20260922095316_customer_groups.js +27 -0
- package/dist/modules/customer_groups/migrations/Migration20260922095316_customer_groups.js.map +7 -0
- package/dist/modules/customer_groups/migrations/Migration20260922103033_customer_groups.js +19 -0
- package/dist/modules/customer_groups/migrations/Migration20260922103033_customer_groups.js.map +7 -0
- package/dist/modules/customer_groups/migrations/Migration20260922105444_customer_groups.js +19 -0
- package/dist/modules/customer_groups/migrations/Migration20260922105444_customer_groups.js.map +7 -0
- package/dist/modules/customer_groups/migrations/Migration20260923193330_customer_groups.js +19 -0
- package/dist/modules/customer_groups/migrations/Migration20260923193330_customer_groups.js.map +7 -0
- package/dist/modules/customer_groups/migrations/Migration20260929083000_customer_groups.js +20 -0
- package/dist/modules/customer_groups/migrations/Migration20260929083000_customer_groups.js.map +7 -0
- package/dist/modules/customer_groups/services/customerGroupsService.js +190 -0
- package/dist/modules/customer_groups/services/customerGroupsService.js.map +7 -0
- package/dist/modules/customer_groups/setup.js +13 -0
- package/dist/modules/customer_groups/setup.js.map +7 -0
- package/dist/modules/customer_groups/widgets/injection/group-picker-field/widget.js +31 -0
- package/dist/modules/customer_groups/widgets/injection/group-picker-field/widget.js.map +7 -0
- package/dist/modules/customer_groups/widgets/injection/person-groups-explain-terms.js +166 -0
- package/dist/modules/customer_groups/widgets/injection/person-groups-explain-terms.js.map +7 -0
- package/dist/modules/customer_groups/widgets/injection/person-groups-tab/widget.js +17 -0
- package/dist/modules/customer_groups/widgets/injection/person-groups-tab/widget.js.map +7 -0
- package/dist/modules/customer_groups/widgets/injection/person-groups-tab.js +457 -0
- package/dist/modules/customer_groups/widgets/injection/person-groups-tab.js.map +7 -0
- package/dist/modules/customer_groups/widgets/injection-table.js +33 -0
- package/dist/modules/customer_groups/widgets/injection-table.js.map +7 -0
- package/dist/modules/customers/acl.js +13 -0
- package/dist/modules/customers/acl.js.map +2 -2
- package/dist/modules/customers/api/activities/route.js +9 -1
- package/dist/modules/customers/api/activities/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/people/route.js +2 -1
- package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/route.js +19 -7
- package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/companies/route.js +2 -1
- package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/people/route.js +2 -1
- package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/deals/map/route.js +7 -2
- package/dist/modules/customers/api/deals/map/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/context.js +2 -1
- package/dist/modules/customers/api/dictionaries/context.js.map +2 -2
- package/dist/modules/customers/api/entity-roles-factory.js +3 -2
- package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
- package/dist/modules/customers/api/interactions/[id]/visibility/route.js +2 -1
- package/dist/modules/customers/api/interactions/[id]/visibility/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/cancel/route.js +2 -1
- package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/complete/route.js +2 -1
- package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/conflicts/route.js +10 -6
- package/dist/modules/customers/api/interactions/conflicts/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/counts/route.js +24 -4
- package/dist/modules/customers/api/interactions/counts/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/route.js +31 -8
- package/dist/modules/customers/api/interactions/route.js.map +2 -2
- package/dist/modules/customers/api/labels/assign/route.js +2 -1
- package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
- package/dist/modules/customers/api/labels/route.js +3 -2
- package/dist/modules/customers/api/labels/route.js.map +2 -2
- package/dist/modules/customers/api/labels/unassign/route.js +2 -1
- package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/context.js +7 -2
- package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/email-share/route.js +218 -0
- package/dist/modules/customers/api/people/[id]/email-share/route.js.map +7 -0
- package/dist/modules/customers/api/people/[id]/email-threads/route.js +11 -2
- package/dist/modules/customers/api/people/[id]/email-threads/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/emails/route.js +2 -1
- package/dist/modules/customers/api/people/[id]/emails/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/route.js +20 -19
- package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/people/check-phone/route.js +2 -1
- package/dist/modules/customers/api/people/check-phone/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js +3 -2
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/route.js +3 -3
- package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
- package/dist/modules/customers/api/pipelines/route.js +3 -3
- package/dist/modules/customers/api/pipelines/route.js.map +2 -2
- package/dist/modules/customers/api/settings/address-format/route.js +2 -1
- package/dist/modules/customers/api/settings/address-format/route.js.map +2 -2
- package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js +2 -1
- package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js.map +2 -2
- package/dist/modules/customers/api/settings/stuck-threshold/route.js +2 -1
- package/dist/modules/customers/api/settings/stuck-threshold/route.js.map +2 -2
- package/dist/modules/customers/api/tags/assign/route.js +2 -1
- package/dist/modules/customers/api/tags/assign/route.js.map +2 -2
- package/dist/modules/customers/api/tags/unassign/route.js +2 -1
- package/dist/modules/customers/api/tags/unassign/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/[id]/page.js +4 -2
- package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js +6 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +6 -1
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/components/ReassignOwnerDialog.js +93 -0
- package/dist/modules/customers/backend/customers/deals/components/ReassignOwnerDialog.js.map +7 -0
- package/dist/modules/customers/backend/customers/deals/page.js +111 -1
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/[id]/page.js +4 -2
- package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
- package/dist/modules/customers/commands/addresses.js +83 -15
- package/dist/modules/customers/commands/addresses.js.map +2 -2
- package/dist/modules/customers/commands/companies.js +24 -5
- package/dist/modules/customers/commands/companies.js.map +2 -2
- package/dist/modules/customers/commands/emailConversationShares.js +177 -0
- package/dist/modules/customers/commands/emailConversationShares.js.map +7 -0
- package/dist/modules/customers/commands/index.js +1 -0
- package/dist/modules/customers/commands/index.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +48 -23
- package/dist/modules/customers/commands/interactions.js.map +3 -3
- package/dist/modules/customers/commands/people.js +3 -2
- package/dist/modules/customers/commands/people.js.map +2 -2
- package/dist/modules/customers/commands/shared.js +4 -0
- package/dist/modules/customers/commands/shared.js.map +2 -2
- package/dist/modules/customers/components/calendar/types.js.map +1 -1
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js +3 -11
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +66 -2
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/DealOwnerSelect.js +96 -0
- package/dist/modules/customers/components/detail/DealOwnerSelect.js.map +7 -0
- package/dist/modules/customers/components/detail/DealsSection.js +8 -14
- package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonEmailShareControl.js +190 -0
- package/dist/modules/customers/components/detail/PersonEmailShareControl.js.map +7 -0
- package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js +17 -0
- package/dist/modules/customers/components/detail/PersonEmailThreadsTab.js.map +2 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +5 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/create/CreateDealForm.js +19 -0
- package/dist/modules/customers/components/detail/create/CreateDealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/create/DealDetailsFields.js +11 -0
- package/dist/modules/customers/components/detail/create/DealDetailsFields.js.map +2 -2
- package/dist/modules/customers/components/detail/create/DealSectionCard.js +2 -2
- package/dist/modules/customers/components/detail/create/DealSectionCard.js.map +2 -2
- package/dist/modules/customers/components/detail/create/dealFormTypes.js +1 -0
- package/dist/modules/customers/components/detail/create/dealFormTypes.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +1 -1
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
- package/dist/modules/customers/components/formConfig.js +2 -0
- package/dist/modules/customers/components/formConfig.js.map +2 -2
- package/dist/modules/customers/components/linking/LinkEntityDialog.js +8 -0
- package/dist/modules/customers/components/linking/LinkEntityDialog.js.map +2 -2
- package/dist/modules/customers/data/enrichers.js +40 -2
- package/dist/modules/customers/data/enrichers.js.map +2 -2
- package/dist/modules/customers/data/entities.js +56 -0
- package/dist/modules/customers/data/entities.js.map +2 -2
- package/dist/modules/customers/data/validators.js +11 -0
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/events.js +6 -1
- package/dist/modules/customers/events.js.map +2 -2
- package/dist/modules/customers/lib/assignableStaff.js +14 -0
- package/dist/modules/customers/lib/assignableStaff.js.map +2 -2
- package/dist/modules/customers/lib/calendar/editorPayload.js +17 -13
- package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
- package/dist/modules/customers/lib/calendar/mapItem.js +1 -0
- package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
- package/dist/modules/customers/lib/conversationShares.js +126 -0
- package/dist/modules/customers/lib/conversationShares.js.map +7 -0
- package/dist/modules/customers/lib/dealsOrganizationScope.js +5 -3
- package/dist/modules/customers/lib/dealsOrganizationScope.js.map +2 -2
- package/dist/modules/customers/lib/interactionRequestContext.js +2 -1
- package/dist/modules/customers/lib/interactionRequestContext.js.map +2 -2
- package/dist/modules/customers/lib/link-channel-message-handler.js +19 -9
- package/dist/modules/customers/lib/link-channel-message-handler.js.map +2 -2
- package/dist/modules/customers/lib/personDetailCacheTags.js +32 -0
- package/dist/modules/customers/lib/personDetailCacheTags.js.map +7 -0
- package/dist/modules/customers/lib/personEmailThreads.js +26 -5
- package/dist/modules/customers/lib/personEmailThreads.js.map +2 -2
- package/dist/modules/customers/lib/reassignDealOwner.js +25 -0
- package/dist/modules/customers/lib/reassignDealOwner.js.map +7 -0
- package/dist/modules/customers/lib/userNames.js +26 -0
- package/dist/modules/customers/lib/userNames.js.map +7 -0
- package/dist/modules/customers/lib/visibilityFilter.js +70 -2
- package/dist/modules/customers/lib/visibilityFilter.js.map +2 -2
- package/dist/modules/customers/migrations/Migration20260825120000_customers.js +18 -0
- package/dist/modules/customers/migrations/Migration20260825120000_customers.js.map +7 -0
- package/dist/modules/customers/migrations/Migration20260825140000_customers.js +27 -0
- package/dist/modules/customers/migrations/Migration20260825140000_customers.js.map +7 -0
- package/dist/modules/customers/setup.js +1 -0
- package/dist/modules/customers/setup.js.map +2 -2
- package/dist/modules/customers/subscribers/channel-visibility-changed.js +16 -0
- package/dist/modules/customers/subscribers/channel-visibility-changed.js.map +7 -0
- package/dist/modules/dashboards/api/widgets/data/batch/route.js +3 -8
- package/dist/modules/dashboards/api/widgets/data/batch/route.js.map +2 -2
- package/dist/modules/dashboards/api/widgets/data/route.js +10 -15
- package/dist/modules/dashboards/api/widgets/data/route.js.map +2 -2
- package/dist/modules/dashboards/lib/aggregations.js +1 -1
- package/dist/modules/dashboards/lib/aggregations.js.map +2 -2
- package/dist/modules/dashboards/lib/widgetScope.js +2 -7
- package/dist/modules/dashboards/lib/widgetScope.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +4 -1
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +497 -329
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +248 -113
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +3 -3
- package/dist/modules/data_sync/components/useDataSyncRunAccess.js +17 -0
- package/dist/modules/data_sync/components/useDataSyncRunAccess.js.map +7 -0
- package/dist/modules/data_sync/lib/resume-point.js +21 -0
- package/dist/modules/data_sync/lib/resume-point.js.map +7 -0
- package/dist/modules/data_sync/lib/start-run.js +52 -2
- package/dist/modules/data_sync/lib/start-run.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-run-service.js +22 -33
- package/dist/modules/data_sync/lib/sync-run-service.js.map +3 -3
- package/dist/modules/devices/api/[id]/route.js +3 -2
- package/dist/modules/devices/api/[id]/route.js.map +2 -2
- package/dist/modules/devices/api/admin/devices/route.js +2 -1
- package/dist/modules/devices/api/admin/devices/route.js.map +2 -2
- package/dist/modules/devices/api/route.js +2 -1
- package/dist/modules/devices/api/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/api/context.js +2 -1
- package/dist/modules/dictionaries/api/context.js.map +2 -2
- package/dist/modules/dictionaries/api/route.js +4 -0
- package/dist/modules/dictionaries/api/route.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionariesManager.js +101 -89
- package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
- package/dist/modules/dictionaries/data/validators.js +3 -1
- package/dist/modules/dictionaries/data/validators.js.map +2 -2
- package/dist/modules/directory/api/organization-branding/route.js +10 -1
- package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
- package/dist/modules/directory/api/organizations/route.js +1 -1
- package/dist/modules/directory/api/organizations/route.js.map +1 -1
- package/dist/modules/directory/backend/directory/branding/page.js +17 -4
- package/dist/modules/directory/backend/directory/branding/page.js.map +3 -3
- package/dist/modules/directory/commands/organizations.js +136 -74
- package/dist/modules/directory/commands/organizations.js.map +2 -2
- package/dist/modules/directory/lib/hierarchy.js +19 -1
- package/dist/modules/directory/lib/hierarchy.js.map +2 -2
- package/dist/modules/directory/utils/organizationScope.js +66 -12
- package/dist/modules/directory/utils/organizationScope.js.map +2 -2
- package/dist/modules/directory/utils/organizationScopeFilter.js +7 -4
- package/dist/modules/directory/utils/organizationScopeFilter.js.map +2 -2
- package/dist/modules/directory/utils/organizationScopeGuard.js +6 -2
- package/dist/modules/directory/utils/organizationScopeGuard.js.map +2 -2
- package/dist/modules/entities/api/encryption.js +68 -25
- package/dist/modules/entities/api/encryption.js.map +2 -2
- package/dist/modules/entities/api/records.js +9 -8
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/cli.js +33 -22
- package/dist/modules/entities/cli.js.map +2 -2
- package/dist/modules/entities/data/entities.js +8 -0
- package/dist/modules/entities/data/entities.js.map +2 -2
- package/dist/modules/entities/lib/definition-scope.js +3 -1
- package/dist/modules/entities/lib/definition-scope.js.map +2 -2
- package/dist/modules/entities/lib/encryption-maps.js +117 -0
- package/dist/modules/entities/lib/encryption-maps.js.map +7 -0
- package/dist/modules/entities/lib/helpers.js +30 -6
- package/dist/modules/entities/lib/helpers.js.map +2 -2
- package/dist/modules/entities/migrations/Migration20260910120000.js +17 -0
- package/dist/modules/entities/migrations/Migration20260910120000.js.map +7 -0
- package/dist/modules/entities/migrations/Migration20261004120000_encryption_map_scope_uniqueness.js +130 -0
- package/dist/modules/entities/migrations/Migration20261004120000_encryption_map_scope_uniqueness.js.map +7 -0
- package/dist/modules/eudr/api/dashboard/widgets/compliance-overview/route.js +2 -1
- package/dist/modules/eudr/api/dashboard/widgets/compliance-overview/route.js.map +2 -2
- package/dist/modules/eudr/api/plots/import/route.js +2 -1
- package/dist/modules/eudr/api/plots/import/route.js.map +2 -2
- package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js +2 -1
- package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js.map +2 -2
- package/dist/modules/eudr/api/product-mappings/suggestions/route.js +2 -1
- package/dist/modules/eudr/api/product-mappings/suggestions/route.js.map +2 -2
- package/dist/modules/eudr/api/reports/annual/route.js +2 -1
- package/dist/modules/eudr/api/reports/annual/route.js.map +2 -2
- package/dist/modules/eudr/api/statements/[id]/export/route.js +2 -1
- package/dist/modules/eudr/api/statements/[id]/export/route.js.map +2 -2
- package/dist/modules/eudr/api/statements/[id]/readiness/route.js +2 -1
- package/dist/modules/eudr/api/statements/[id]/readiness/route.js.map +2 -2
- package/dist/modules/eudr/api/suppliers/compliance/route.js +2 -1
- package/dist/modules/eudr/api/suppliers/compliance/route.js.map +2 -2
- package/dist/modules/feature_toggles/commands/global.js +13 -13
- package/dist/modules/feature_toggles/commands/global.js.map +2 -2
- package/dist/modules/feature_toggles/lib/feature-flag-check.js +8 -2
- package/dist/modules/feature_toggles/lib/feature-flag-check.js.map +2 -2
- package/dist/modules/forms/acl.js +14 -0
- package/dist/modules/forms/acl.js.map +7 -0
- package/dist/modules/forms/api/[id]/analytics/route.js +113 -0
- package/dist/modules/forms/api/[id]/analytics/route.js.map +7 -0
- package/dist/modules/forms/api/[id]/distributions/route.js +176 -0
- package/dist/modules/forms/api/[id]/distributions/route.js.map +7 -0
- package/dist/modules/forms/api/[id]/route.js +225 -0
- package/dist/modules/forms/api/[id]/route.js.map +7 -0
- package/dist/modules/forms/api/[id]/run/context/route.js +115 -0
- package/dist/modules/forms/api/[id]/run/context/route.js.map +7 -0
- package/dist/modules/forms/api/[id]/run/submissions/route.js +109 -0
- package/dist/modules/forms/api/[id]/run/submissions/route.js.map +7 -0
- package/dist/modules/forms/api/[id]/submissions/route.js +83 -0
- package/dist/modules/forms/api/[id]/submissions/route.js.map +7 -0
- package/dist/modules/forms/api/[id]/theme-logo/route.js +117 -0
- package/dist/modules/forms/api/[id]/theme-logo/route.js.map +7 -0
- package/dist/modules/forms/api/[id]/versions/[versionId]/diff/route.js +125 -0
- package/dist/modules/forms/api/[id]/versions/[versionId]/diff/route.js.map +7 -0
- package/dist/modules/forms/api/[id]/versions/[versionId]/publish/route.js +99 -0
- package/dist/modules/forms/api/[id]/versions/[versionId]/publish/route.js.map +7 -0
- package/dist/modules/forms/api/[id]/versions/[versionId]/route.js +176 -0
- package/dist/modules/forms/api/[id]/versions/[versionId]/route.js.map +7 -0
- package/dist/modules/forms/api/[id]/versions/fork/route.js +92 -0
- package/dist/modules/forms/api/[id]/versions/fork/route.js.map +7 -0
- package/dist/modules/forms/api/attachment-helpers.js +111 -0
- package/dist/modules/forms/api/attachment-helpers.js.map +7 -0
- package/dist/modules/forms/api/by-key/[key]/active/route.js +140 -0
- package/dist/modules/forms/api/by-key/[key]/active/route.js.map +7 -0
- package/dist/modules/forms/api/distributions/[distributionId]/invitations/[invitationId]/route.js +84 -0
- package/dist/modules/forms/api/distributions/[distributionId]/invitations/[invitationId]/route.js.map +7 -0
- package/dist/modules/forms/api/distributions/[distributionId]/invitations/[invitationId]/send/route.js +88 -0
- package/dist/modules/forms/api/distributions/[distributionId]/invitations/[invitationId]/send/route.js.map +7 -0
- package/dist/modules/forms/api/distributions/[distributionId]/invitations/route.js +183 -0
- package/dist/modules/forms/api/distributions/[distributionId]/invitations/route.js.map +7 -0
- package/dist/modules/forms/api/distributions/[distributionId]/route.js +191 -0
- package/dist/modules/forms/api/distributions/[distributionId]/route.js.map +7 -0
- package/dist/modules/forms/api/form-submissions/[id]/attachments/route.js +91 -0
- package/dist/modules/forms/api/form-submissions/[id]/attachments/route.js.map +7 -0
- package/dist/modules/forms/api/form-submissions/[id]/resume-token/route.js +110 -0
- package/dist/modules/forms/api/form-submissions/[id]/resume-token/route.js.map +7 -0
- package/dist/modules/forms/api/form-submissions/[id]/route.js +146 -0
- package/dist/modules/forms/api/form-submissions/[id]/route.js.map +7 -0
- package/dist/modules/forms/api/form-submissions/[id]/submit/route.js +74 -0
- package/dist/modules/forms/api/form-submissions/[id]/submit/route.js.map +7 -0
- package/dist/modules/forms/api/form-submissions/by-subject/[subject_type]/[subject_id]/route.js +76 -0
- package/dist/modules/forms/api/form-submissions/by-subject/[subject_type]/[subject_id]/route.js.map +7 -0
- package/dist/modules/forms/api/form-submissions/route.js +113 -0
- package/dist/modules/forms/api/form-submissions/route.js.map +7 -0
- package/dist/modules/forms/api/helpers.js +124 -0
- package/dist/modules/forms/api/helpers.js.map +7 -0
- package/dist/modules/forms/api/public/distributions/[slug]/embed-policy/route.js +47 -0
- package/dist/modules/forms/api/public/distributions/[slug]/embed-policy/route.js.map +7 -0
- package/dist/modules/forms/api/public/distributions/[slug]/route.js +81 -0
- package/dist/modules/forms/api/public/distributions/[slug]/route.js.map +7 -0
- package/dist/modules/forms/api/public/embed-loader/route.js +116 -0
- package/dist/modules/forms/api/public/embed-loader/route.js.map +7 -0
- package/dist/modules/forms/api/public/invitations/[token]/route.js +87 -0
- package/dist/modules/forms/api/public/invitations/[token]/route.js.map +7 -0
- package/dist/modules/forms/api/public/rate-limit.js +54 -0
- package/dist/modules/forms/api/public/rate-limit.js.map +7 -0
- package/dist/modules/forms/api/public/start/route.js +142 -0
- package/dist/modules/forms/api/public/start/route.js.map +7 -0
- package/dist/modules/forms/api/public/submissions/[id]/attachments/[attachmentId]/route.js +67 -0
- package/dist/modules/forms/api/public/submissions/[id]/attachments/[attachmentId]/route.js.map +7 -0
- package/dist/modules/forms/api/public/submissions/[id]/attachments/route.js +85 -0
- package/dist/modules/forms/api/public/submissions/[id]/attachments/route.js.map +7 -0
- package/dist/modules/forms/api/public/submissions/[id]/pdf/route.js +72 -0
- package/dist/modules/forms/api/public/submissions/[id]/pdf/route.js.map +7 -0
- package/dist/modules/forms/api/public/submissions/[id]/route.js +154 -0
- package/dist/modules/forms/api/public/submissions/[id]/route.js.map +7 -0
- package/dist/modules/forms/api/public/submissions/[id]/submit/route.js +127 -0
- package/dist/modules/forms/api/public/submissions/[id]/submit/route.js.map +7 -0
- package/dist/modules/forms/api/route.js +216 -0
- package/dist/modules/forms/api/route.js.map +7 -0
- package/dist/modules/forms/api/runtime-helpers.js +149 -0
- package/dist/modules/forms/api/runtime-helpers.js.map +7 -0
- package/dist/modules/forms/api/subjects/[subjectType]/[subjectId]/consents/route.js +137 -0
- package/dist/modules/forms/api/subjects/[subjectType]/[subjectId]/consents/route.js.map +7 -0
- package/dist/modules/forms/api/subjects/[subjectType]/[subjectId]/export/route.js +101 -0
- package/dist/modules/forms/api/subjects/[subjectType]/[subjectId]/export/route.js.map +7 -0
- package/dist/modules/forms/api/submissions/[submissionId]/access-audit/route.js +76 -0
- package/dist/modules/forms/api/submissions/[submissionId]/access-audit/route.js.map +7 -0
- package/dist/modules/forms/api/submissions/[submissionId]/actors/[actorId]/route.js +56 -0
- package/dist/modules/forms/api/submissions/[submissionId]/actors/[actorId]/route.js.map +7 -0
- package/dist/modules/forms/api/submissions/[submissionId]/actors/route.js +76 -0
- package/dist/modules/forms/api/submissions/[submissionId]/actors/route.js.map +7 -0
- package/dist/modules/forms/api/submissions/[submissionId]/anonymize/route.js +107 -0
- package/dist/modules/forms/api/submissions/[submissionId]/anonymize/route.js.map +7 -0
- package/dist/modules/forms/api/submissions/[submissionId]/attachments/[attachmentId]/route.js +73 -0
- package/dist/modules/forms/api/submissions/[submissionId]/attachments/[attachmentId]/route.js.map +7 -0
- package/dist/modules/forms/api/submissions/[submissionId]/export/route.js +90 -0
- package/dist/modules/forms/api/submissions/[submissionId]/export/route.js.map +7 -0
- package/dist/modules/forms/api/submissions/[submissionId]/pdf/route.js +91 -0
- package/dist/modules/forms/api/submissions/[submissionId]/pdf/route.js.map +7 -0
- package/dist/modules/forms/api/submissions/[submissionId]/reopen/route.js +55 -0
- package/dist/modules/forms/api/submissions/[submissionId]/reopen/route.js.map +7 -0
- package/dist/modules/forms/api/submissions/[submissionId]/revisions/route.js +61 -0
- package/dist/modules/forms/api/submissions/[submissionId]/revisions/route.js.map +7 -0
- package/dist/modules/forms/api/submissions/[submissionId]/route.js +75 -0
- package/dist/modules/forms/api/submissions/[submissionId]/route.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/FormStudio.js +2811 -0
- package/dist/modules/forms/backend/forms/[id]/FormStudio.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/analytics/page.js +214 -0
- package/dist/modules/forms/backend/forms/[id]/analytics/page.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/analytics/page.meta.js +19 -0
- package/dist/modules/forms/backend/forms/[id]/analytics/page.meta.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/distributions/page.js +12 -0
- package/dist/modules/forms/backend/forms/[id]/distributions/page.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/distributions/page.meta.js +19 -0
- package/dist/modules/forms/backend/forms/[id]/distributions/page.meta.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/history/page.js +152 -0
- package/dist/modules/forms/backend/forms/[id]/history/page.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/history/page.meta.js +15 -0
- package/dist/modules/forms/backend/forms/[id]/history/page.meta.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/page.js +13 -0
- package/dist/modules/forms/backend/forms/[id]/page.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/page.meta.js +15 -0
- package/dist/modules/forms/backend/forms/[id]/page.meta.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/YesNoRow.js +51 -0
- package/dist/modules/forms/backend/forms/[id]/studio/YesNoRow.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/autosave-guard.js +21 -0
- package/dist/modules/forms/backend/forms/[id]/studio/autosave-guard.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/DragOverlayCard.js +18 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/DragOverlayCard.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/DropIndicator.js +41 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/DropIndicator.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/FieldRow.js +277 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/FieldRow.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/FormCanvas.js +196 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/FormCanvas.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/GridSlot.js +151 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/GridSlot.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/SectionContainer.js +340 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/SectionContainer.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/grid-collision.js +24 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/grid-collision.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/keyboard-coordinates.js +91 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/keyboard-coordinates.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/overlay-size.js +11 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/overlay-size.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/resize-math.js +27 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/resize-math.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/row-layout.js +80 -0
- package/dist/modules/forms/backend/forms/[id]/studio/canvas/row-layout.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/logic/ConditionBuilder.js +210 -0
- package/dist/modules/forms/backend/forms/[id]/studio/logic/ConditionBuilder.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/logic/HiddenFieldsPanel.js +91 -0
- package/dist/modules/forms/backend/forms/[id]/studio/logic/HiddenFieldsPanel.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/logic/JumpsEditor.js +201 -0
- package/dist/modules/forms/backend/forms/[id]/studio/logic/JumpsEditor.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/logic/RecallTokenPicker.js +66 -0
- package/dist/modules/forms/backend/forms/[id]/studio/logic/RecallTokenPicker.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/logic/VariablesPanel.js +223 -0
- package/dist/modules/forms/backend/forms/[id]/studio/logic/VariablesPanel.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/logic/condition-model.js +151 -0
- package/dist/modules/forms/backend/forms/[id]/studio/logic/condition-model.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/lucide-icons.js +88 -0
- package/dist/modules/forms/backend/forms/[id]/studio/lucide-icons.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/palette/FormAppearancePanel.js +493 -0
- package/dist/modules/forms/backend/forms/[id]/studio/palette/FormAppearancePanel.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/palette/FormPalettePanel.js +44 -0
- package/dist/modules/forms/backend/forms/[id]/studio/palette/FormPalettePanel.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/palette/InputParametersTab.js +350 -0
- package/dist/modules/forms/backend/forms/[id]/studio/palette/InputParametersTab.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/palette/PaletteCard.js +40 -0
- package/dist/modules/forms/backend/forms/[id]/studio/palette/PaletteCard.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/palette/RolesEditor.js +202 -0
- package/dist/modules/forms/backend/forms/[id]/studio/palette/RolesEditor.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/palette/entries.js +66 -0
- package/dist/modules/forms/backend/forms/[id]/studio/palette/entries.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/preview/PreviewSurface.js +1200 -0
- package/dist/modules/forms/backend/forms/[id]/studio/preview/PreviewSurface.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/preview/ViewportFrame.js +34 -0
- package/dist/modules/forms/backend/forms/[id]/studio/preview/ViewportFrame.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/recall.js +76 -0
- package/dist/modules/forms/backend/forms/[id]/studio/recall.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/schema-helpers.js +1693 -0
- package/dist/modules/forms/backend/forms/[id]/studio/schema-helpers.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/style/BackgroundControl.js +127 -0
- package/dist/modules/forms/backend/forms/[id]/studio/style/BackgroundControl.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/style/ColorControl.js +181 -0
- package/dist/modules/forms/backend/forms/[id]/studio/style/ColorControl.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/style/contrast.js +75 -0
- package/dist/modules/forms/backend/forms/[id]/studio/style/contrast.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/style/presets.js +91 -0
- package/dist/modules/forms/backend/forms/[id]/studio/style/presets.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/type-label.js +11 -0
- package/dist/modules/forms/backend/forms/[id]/studio/type-label.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/types.js +1 -0
- package/dist/modules/forms/backend/forms/[id]/studio/types.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/undo-controller.js +43 -0
- package/dist/modules/forms/backend/forms/[id]/studio/undo-controller.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/FileConstraintsEditor.js +74 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/FileConstraintsEditor.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/GroupConfigEditor.js +134 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/GroupConfigEditor.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/LengthRangeEditor.js +61 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/LengthRangeEditor.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/MatrixColumnsEditor.js +149 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/MatrixColumnsEditor.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/MatrixRowsEditor.js +168 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/MatrixRowsEditor.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/MessageOverridesEditor.js +64 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/MessageOverridesEditor.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/NpsAnchorsEditor.js +52 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/NpsAnchorsEditor.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/NumberRangeEditor.js +59 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/NumberRangeEditor.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/OpinionIconSelect.js +31 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/OpinionIconSelect.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/PatternEditor.js +105 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/PatternEditor.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/RankingExhaustiveSwitch.js +15 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/RankingExhaustiveSwitch.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/SignatureConfigEditor.js +75 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/SignatureConfigEditor.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/ValidationPanel.js +293 -0
- package/dist/modules/forms/backend/forms/[id]/studio/validation/ValidationPanel.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/submissions/components/RowBadges.js +42 -0
- package/dist/modules/forms/backend/forms/[id]/submissions/components/RowBadges.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/submissions/components/SubmissionDrawer.js +473 -0
- package/dist/modules/forms/backend/forms/[id]/submissions/components/SubmissionDrawer.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/submissions/components/formSummary.js +9 -0
- package/dist/modules/forms/backend/forms/[id]/submissions/components/formSummary.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/submissions/page.js +205 -0
- package/dist/modules/forms/backend/forms/[id]/submissions/page.js.map +7 -0
- package/dist/modules/forms/backend/forms/[id]/submissions/page.meta.js +12 -0
- package/dist/modules/forms/backend/forms/[id]/submissions/page.meta.js.map +7 -0
- package/dist/modules/forms/backend/forms/create/page.js +101 -0
- package/dist/modules/forms/backend/forms/create/page.js.map +7 -0
- package/dist/modules/forms/backend/forms/create/page.meta.js +15 -0
- package/dist/modules/forms/backend/forms/create/page.meta.js.map +7 -0
- package/dist/modules/forms/backend/forms/page.js +227 -0
- package/dist/modules/forms/backend/forms/page.js.map +7 -0
- package/dist/modules/forms/backend/forms/page.meta.js +33 -0
- package/dist/modules/forms/backend/forms/page.meta.js.map +7 -0
- package/dist/modules/forms/commands/distribution.js +421 -0
- package/dist/modules/forms/commands/distribution.js.map +7 -0
- package/dist/modules/forms/commands/form-version.js +645 -0
- package/dist/modules/forms/commands/form-version.js.map +7 -0
- package/dist/modules/forms/commands/form.js +411 -0
- package/dist/modules/forms/commands/form.js.map +7 -0
- package/dist/modules/forms/commands/index.js +11 -0
- package/dist/modules/forms/commands/index.js.map +7 -0
- package/dist/modules/forms/commands/invitation.js +333 -0
- package/dist/modules/forms/commands/invitation.js.map +7 -0
- package/dist/modules/forms/commands/shared.js +151 -0
- package/dist/modules/forms/commands/shared.js.map +7 -0
- package/dist/modules/forms/commands/submission.js +139 -0
- package/dist/modules/forms/commands/submission.js.map +7 -0
- package/dist/modules/forms/data/entities.js +654 -0
- package/dist/modules/forms/data/entities.js.map +7 -0
- package/dist/modules/forms/data/validators.js +254 -0
- package/dist/modules/forms/data/validators.js.map +7 -0
- package/dist/modules/forms/di.js +199 -0
- package/dist/modules/forms/di.js.map +7 -0
- package/dist/modules/forms/encryption.js +15 -0
- package/dist/modules/forms/encryption.js.map +7 -0
- package/dist/modules/forms/events-payloads.js +81 -0
- package/dist/modules/forms/events-payloads.js.map +7 -0
- package/dist/modules/forms/events.js +33 -0
- package/dist/modules/forms/events.js.map +7 -0
- package/dist/modules/forms/extension-points.js +36 -0
- package/dist/modules/forms/extension-points.js.map +7 -0
- package/dist/modules/forms/frontend/[orgSlug]/portal/forms/[key]/page.js +33 -0
- package/dist/modules/forms/frontend/[orgSlug]/portal/forms/[key]/page.js.map +7 -0
- package/dist/modules/forms/frontend/[orgSlug]/portal/forms/[key]/page.meta.js +11 -0
- package/dist/modules/forms/frontend/[orgSlug]/portal/forms/[key]/page.meta.js.map +7 -0
- package/dist/modules/forms/frontend/[orgSlug]/portal/submissions/[id]/continue/page.js +41 -0
- package/dist/modules/forms/frontend/[orgSlug]/portal/submissions/[id]/continue/page.js.map +7 -0
- package/dist/modules/forms/frontend/[orgSlug]/portal/submissions/[id]/continue/page.meta.js +11 -0
- package/dist/modules/forms/frontend/[orgSlug]/portal/submissions/[id]/continue/page.meta.js.map +7 -0
- package/dist/modules/forms/frontend/embed/[slug]/page.js +105 -0
- package/dist/modules/forms/frontend/embed/[slug]/page.js.map +7 -0
- package/dist/modules/forms/frontend/embed/[slug]/page.meta.js +11 -0
- package/dist/modules/forms/frontend/embed/[slug]/page.meta.js.map +7 -0
- package/dist/modules/forms/frontend/f/[slug]/page.js +12 -0
- package/dist/modules/forms/frontend/f/[slug]/page.js.map +7 -0
- package/dist/modules/forms/frontend/f/[slug]/page.meta.js +11 -0
- package/dist/modules/forms/frontend/f/[slug]/page.meta.js.map +7 -0
- package/dist/modules/forms/frontend/forms/[id]/run/page.js +62 -0
- package/dist/modules/forms/frontend/forms/[id]/run/page.js.map +7 -0
- package/dist/modules/forms/frontend/forms/[id]/run/page.meta.js +11 -0
- package/dist/modules/forms/frontend/forms/[id]/run/page.meta.js.map +7 -0
- package/dist/modules/forms/frontend/i/[token]/page.js +12 -0
- package/dist/modules/forms/frontend/i/[token]/page.js.map +7 -0
- package/dist/modules/forms/frontend/i/[token]/page.meta.js +11 -0
- package/dist/modules/forms/frontend/i/[token]/page.meta.js.map +7 -0
- package/dist/modules/forms/index.js +20 -0
- package/dist/modules/forms/index.js.map +7 -0
- package/dist/modules/forms/lib/embed-frame-policy.js +68 -0
- package/dist/modules/forms/lib/embed-frame-policy.js.map +7 -0
- package/dist/modules/forms/lib/form-definition-slicing.js +38 -0
- package/dist/modules/forms/lib/form-definition-slicing.js.map +7 -0
- package/dist/modules/forms/lib/log-redaction.js +38 -0
- package/dist/modules/forms/lib/log-redaction.js.map +7 -0
- package/dist/modules/forms/lib/navigable-url.js +24 -0
- package/dist/modules/forms/lib/navigable-url.js.map +7 -0
- package/dist/modules/forms/lib/retention.js +20 -0
- package/dist/modules/forms/lib/retention.js.map +7 -0
- package/dist/modules/forms/lib/runtime-principal.js +59 -0
- package/dist/modules/forms/lib/runtime-principal.js.map +7 -0
- package/dist/modules/forms/migrations/Migration20260508135459_forms.js +17 -0
- package/dist/modules/forms/migrations/Migration20260508135459_forms.js.map +7 -0
- package/dist/modules/forms/migrations/Migration20260508140932_forms.js +23 -0
- package/dist/modules/forms/migrations/Migration20260508140932_forms.js.map +7 -0
- package/dist/modules/forms/migrations/Migration20260508155737_forms.js +15 -0
- package/dist/modules/forms/migrations/Migration20260508155737_forms.js.map +7 -0
- package/dist/modules/forms/migrations/Migration20260520120000_forms.js +23 -0
- package/dist/modules/forms/migrations/Migration20260520120000_forms.js.map +7 -0
- package/dist/modules/forms/migrations/Migration20260521090000_forms.js +13 -0
- package/dist/modules/forms/migrations/Migration20260521090000_forms.js.map +7 -0
- package/dist/modules/forms/migrations/Migration20260521120000_forms.js +17 -0
- package/dist/modules/forms/migrations/Migration20260521120000_forms.js.map +7 -0
- package/dist/modules/forms/runner/FormRunner.js +786 -0
- package/dist/modules/forms/runner/FormRunner.js.map +7 -0
- package/dist/modules/forms/runner/MatrixField.js +150 -0
- package/dist/modules/forms/runner/MatrixField.js.map +7 -0
- package/dist/modules/forms/runner/RankingField.js +131 -0
- package/dist/modules/forms/runner/RankingField.js.map +7 -0
- package/dist/modules/forms/runner/ScaleField.js +78 -0
- package/dist/modules/forms/runner/ScaleField.js.map +7 -0
- package/dist/modules/forms/runner/SignatureField.js +132 -0
- package/dist/modules/forms/runner/SignatureField.js.map +7 -0
- package/dist/modules/forms/runner/tamper-check.js +85 -0
- package/dist/modules/forms/runner/tamper-check.js.map +7 -0
- package/dist/modules/forms/schema/address-countries.js +43 -0
- package/dist/modules/forms/schema/address-countries.js.map +7 -0
- package/dist/modules/forms/schema/field-type-patterns.js +15 -0
- package/dist/modules/forms/schema/field-type-patterns.js.map +7 -0
- package/dist/modules/forms/schema/field-type-registry-advanced.js +32 -0
- package/dist/modules/forms/schema/field-type-registry-advanced.js.map +7 -0
- package/dist/modules/forms/schema/field-type-registry.js +625 -0
- package/dist/modules/forms/schema/field-type-registry.js.map +7 -0
- package/dist/modules/forms/schema/file-field.js +64 -0
- package/dist/modules/forms/schema/file-field.js.map +7 -0
- package/dist/modules/forms/schema/group-field.js +143 -0
- package/dist/modules/forms/schema/group-field.js.map +7 -0
- package/dist/modules/forms/schema/jsonlogic-grammar.js +66 -0
- package/dist/modules/forms/schema/jsonlogic-grammar.js.map +7 -0
- package/dist/modules/forms/schema/jsonschema-extensions.js +739 -0
- package/dist/modules/forms/schema/jsonschema-extensions.js.map +7 -0
- package/dist/modules/forms/schema/layout-catalog.js +38 -0
- package/dist/modules/forms/schema/layout-catalog.js.map +7 -0
- package/dist/modules/forms/schema/signature-field.js +67 -0
- package/dist/modules/forms/schema/signature-field.js.map +7 -0
- package/dist/modules/forms/schema/style-extensions.js +77 -0
- package/dist/modules/forms/schema/style-extensions.js.map +7 -0
- package/dist/modules/forms/schema/style-validators.js +181 -0
- package/dist/modules/forms/schema/style-validators.js.map +7 -0
- package/dist/modules/forms/services/access-audit-logger.js +179 -0
- package/dist/modules/forms/services/access-audit-logger.js.map +7 -0
- package/dist/modules/forms/services/analytics-service.js +306 -0
- package/dist/modules/forms/services/analytics-service.js.map +7 -0
- package/dist/modules/forms/services/anonymize-service.js +123 -0
- package/dist/modules/forms/services/anonymize-service.js.map +7 -0
- package/dist/modules/forms/services/attachment-service.js +123 -0
- package/dist/modules/forms/services/attachment-service.js.map +7 -0
- package/dist/modules/forms/services/captcha-verifier.js +76 -0
- package/dist/modules/forms/services/captcha-verifier.js.map +7 -0
- package/dist/modules/forms/services/consent-record-service.js +96 -0
- package/dist/modules/forms/services/consent-record-service.js.map +7 -0
- package/dist/modules/forms/services/distribution-service.js +366 -0
- package/dist/modules/forms/services/distribution-service.js.map +7 -0
- package/dist/modules/forms/services/distribution-token.js +93 -0
- package/dist/modules/forms/services/distribution-token.js.map +7 -0
- package/dist/modules/forms/services/encryption-service.js +328 -0
- package/dist/modules/forms/services/encryption-service.js.map +7 -0
- package/dist/modules/forms/services/export-service.js +210 -0
- package/dist/modules/forms/services/export-service.js.map +7 -0
- package/dist/modules/forms/services/field-validation-service.js +168 -0
- package/dist/modules/forms/services/field-validation-service.js.map +7 -0
- package/dist/modules/forms/services/form-logic-evaluator.js +517 -0
- package/dist/modules/forms/services/form-logic-evaluator.js.map +7 -0
- package/dist/modules/forms/services/form-version-compiler.js +518 -0
- package/dist/modules/forms/services/form-version-compiler.js.map +7 -0
- package/dist/modules/forms/services/form-version-differ.js +89 -0
- package/dist/modules/forms/services/form-version-differ.js.map +7 -0
- package/dist/modules/forms/services/jsonlogic-evaluator.js +171 -0
- package/dist/modules/forms/services/jsonlogic-evaluator.js.map +7 -0
- package/dist/modules/forms/services/pdf-snapshot-service.js +526 -0
- package/dist/modules/forms/services/pdf-snapshot-service.js.map +7 -0
- package/dist/modules/forms/services/prefill-resolver.js +53 -0
- package/dist/modules/forms/services/prefill-resolver.js.map +7 -0
- package/dist/modules/forms/services/role-policy-service.js +55 -0
- package/dist/modules/forms/services/role-policy-service.js.map +7 -0
- package/dist/modules/forms/services/style-compiler.js +234 -0
- package/dist/modules/forms/services/style-compiler.js.map +7 -0
- package/dist/modules/forms/services/submission-service.js +858 -0
- package/dist/modules/forms/services/submission-service.js.map +7 -0
- package/dist/modules/forms/services/upload-scanner.js +9 -0
- package/dist/modules/forms/services/upload-scanner.js.map +7 -0
- package/dist/modules/forms/services/upload-validation.js +54 -0
- package/dist/modules/forms/services/upload-validation.js.map +7 -0
- package/dist/modules/forms/services/visibility-resolver.js +30 -0
- package/dist/modules/forms/services/visibility-resolver.js.map +7 -0
- package/dist/modules/forms/setup.js +18 -0
- package/dist/modules/forms/setup.js.map +7 -0
- package/dist/modules/forms/subscribers/forms-consent-projector.js +58 -0
- package/dist/modules/forms/subscribers/forms-consent-projector.js.map +7 -0
- package/dist/modules/forms/subscribers/forms-pdf-snapshot.js +68 -0
- package/dist/modules/forms/subscribers/forms-pdf-snapshot.js.map +7 -0
- package/dist/modules/forms/subscribers/forms-webhook-bridge-anonymized.js +29 -0
- package/dist/modules/forms/subscribers/forms-webhook-bridge-anonymized.js.map +7 -0
- package/dist/modules/forms/subscribers/forms-webhook-bridge.js +35 -0
- package/dist/modules/forms/subscribers/forms-webhook-bridge.js.map +7 -0
- package/dist/modules/forms/subscribers/invitation-email.js +100 -0
- package/dist/modules/forms/subscribers/invitation-email.js.map +7 -0
- package/dist/modules/forms/translations.js +10 -0
- package/dist/modules/forms/translations.js.map +7 -0
- package/dist/modules/forms/ui/admin/forms/[id]/distributions/CreateDistributionDialog.js +376 -0
- package/dist/modules/forms/ui/admin/forms/[id]/distributions/CreateDistributionDialog.js.map +7 -0
- package/dist/modules/forms/ui/admin/forms/[id]/distributions/DistributionsPanel.js +313 -0
- package/dist/modules/forms/ui/admin/forms/[id]/distributions/DistributionsPanel.js.map +7 -0
- package/dist/modules/forms/ui/admin/forms/[id]/distributions/EmbedSettingsDialog.js +263 -0
- package/dist/modules/forms/ui/admin/forms/[id]/distributions/EmbedSettingsDialog.js.map +7 -0
- package/dist/modules/forms/ui/admin/forms/[id]/distributions/RecipientsTable.js +373 -0
- package/dist/modules/forms/ui/admin/forms/[id]/distributions/RecipientsTable.js.map +7 -0
- package/dist/modules/forms/ui/public/EmbeddedForm.js +166 -0
- package/dist/modules/forms/ui/public/EmbeddedForm.js.map +7 -0
- package/dist/modules/forms/ui/public/FormRunner.js +359 -0
- package/dist/modules/forms/ui/public/FormRunner.js.map +7 -0
- package/dist/modules/forms/ui/public/FormTrigger.js +45 -0
- package/dist/modules/forms/ui/public/FormTrigger.js.map +7 -0
- package/dist/modules/forms/ui/public/PublicFormRunnerPage.js +14 -0
- package/dist/modules/forms/ui/public/PublicFormRunnerPage.js.map +7 -0
- package/dist/modules/forms/ui/public/components/CompletionScreen.js +55 -0
- package/dist/modules/forms/ui/public/components/CompletionScreen.js.map +7 -0
- package/dist/modules/forms/ui/public/components/LocaleSwitch.js +28 -0
- package/dist/modules/forms/ui/public/components/LocaleSwitch.js.map +7 -0
- package/dist/modules/forms/ui/public/components/ResumeGate.js +52 -0
- package/dist/modules/forms/ui/public/components/ResumeGate.js.map +7 -0
- package/dist/modules/forms/ui/public/components/ReviewStep.js +90 -0
- package/dist/modules/forms/ui/public/components/ReviewStep.js.map +7 -0
- package/dist/modules/forms/ui/public/components/SaveIndicator.js +65 -0
- package/dist/modules/forms/ui/public/components/SaveIndicator.js.map +7 -0
- package/dist/modules/forms/ui/public/components/SectionStepper.js +52 -0
- package/dist/modules/forms/ui/public/components/SectionStepper.js.map +7 -0
- package/dist/modules/forms/ui/public/index.js +38 -0
- package/dist/modules/forms/ui/public/index.js.map +7 -0
- package/dist/modules/forms/ui/public/renderers/index.js +737 -0
- package/dist/modules/forms/ui/public/renderers/index.js.map +7 -0
- package/dist/modules/forms/ui/public/state/logic-derivation.js +42 -0
- package/dist/modules/forms/ui/public/state/logic-derivation.js.map +7 -0
- package/dist/modules/forms/ui/public/state/runtime-client.js +306 -0
- package/dist/modules/forms/ui/public/state/runtime-client.js.map +7 -0
- package/dist/modules/forms/ui/public/state/useAutosave.js +64 -0
- package/dist/modules/forms/ui/public/state/useAutosave.js.map +7 -0
- package/dist/modules/forms/ui/public/state/useFormRunner.js +394 -0
- package/dist/modules/forms/ui/public/state/useFormRunner.js.map +7 -0
- package/dist/modules/forms/ui/public/style/LogoHeader.js +33 -0
- package/dist/modules/forms/ui/public/style/LogoHeader.js.map +7 -0
- package/dist/modules/forms/ui/public/style/applyStyle.js +12 -0
- package/dist/modules/forms/ui/public/style/applyStyle.js.map +7 -0
- package/dist/modules/forms/ui/public/types.js +18 -0
- package/dist/modules/forms/ui/public/types.js.map +7 -0
- package/dist/modules/forms/widgets/injection/access-audit-panel/widget.js +100 -0
- package/dist/modules/forms/widgets/injection/access-audit-panel/widget.js.map +7 -0
- package/dist/modules/forms/widgets/injection/anonymize-button/widget.js +148 -0
- package/dist/modules/forms/widgets/injection/anonymize-button/widget.js.map +7 -0
- package/dist/modules/forms/widgets/injection/audit-format.js +33 -0
- package/dist/modules/forms/widgets/injection/audit-format.js.map +7 -0
- package/dist/modules/forms/widgets/injection/context.js +5 -0
- package/dist/modules/forms/widgets/injection/context.js.map +7 -0
- package/dist/modules/forms/widgets/injection/embedded-form/widget.js +52 -0
- package/dist/modules/forms/widgets/injection/embedded-form/widget.js.map +7 -0
- package/dist/modules/forms/widgets/injection/pdf-download-button/widget.js +83 -0
- package/dist/modules/forms/widgets/injection/pdf-download-button/widget.js.map +7 -0
- package/dist/modules/forms/widgets/injection/self-audit-footer/widget.js +38 -0
- package/dist/modules/forms/widgets/injection/self-audit-footer/widget.js.map +7 -0
- package/dist/modules/forms/widgets/injection-table.js +32 -0
- package/dist/modules/forms/widgets/injection-table.js.map +7 -0
- package/dist/modules/forms/workers/pdf-snapshot.js +60 -0
- package/dist/modules/forms/workers/pdf-snapshot.js.map +7 -0
- package/dist/modules/forms/workers/retention-purge.js +112 -0
- package/dist/modules/forms/workers/retention-purge.js.map +7 -0
- package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js +7 -1
- package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js.map +2 -2
- package/dist/modules/inbox_ops/data/validators.js +2 -2
- package/dist/modules/inbox_ops/data/validators.js.map +2 -2
- package/dist/modules/inbox_ops/lib/llmProvider.js +5 -1
- package/dist/modules/inbox_ops/lib/llmProvider.js.map +2 -2
- package/dist/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.js +2 -2
- package/dist/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.js.map +2 -2
- package/dist/modules/integrations/di.js +3 -1
- package/dist/modules/integrations/di.js.map +2 -2
- package/dist/modules/integrations/lib/credentials-service.js +29 -36
- package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
- package/dist/modules/messages/api/[id]/attachments/route.js +7 -3
- package/dist/modules/messages/api/[id]/attachments/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/forward-preview/route.js +4 -3
- package/dist/modules/messages/api/[id]/forward-preview/route.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +29 -3
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/messages/api/route.js +91 -2
- package/dist/modules/messages/api/route.js.map +2 -2
- package/dist/modules/messages/commands/messages.js +40 -6
- package/dist/modules/messages/commands/messages.js.map +2 -2
- package/dist/modules/messages/components/ComposeMessagePageClient.js +24 -1
- package/dist/modules/messages/components/ComposeMessagePageClient.js.map +2 -2
- package/dist/modules/messages/data/validators.js +11 -1
- package/dist/modules/messages/data/validators.js.map +2 -2
- package/dist/modules/messages/lib/channelThreadAccess.js +30 -0
- package/dist/modules/messages/lib/channelThreadAccess.js.map +7 -0
- package/dist/modules/messages/lib/composeSenderDelegation.js +87 -0
- package/dist/modules/messages/lib/composeSenderDelegation.js.map +7 -0
- package/dist/modules/messages/lib/composeSenderOptions.js +24 -0
- package/dist/modules/messages/lib/composeSenderOptions.js.map +7 -0
- package/dist/modules/messages/lib/email-sender.js +1 -11
- package/dist/modules/messages/lib/email-sender.js.map +2 -2
- package/dist/modules/messages/lib/forwarding.js +4 -4
- package/dist/modules/messages/lib/forwarding.js.map +2 -2
- package/dist/modules/messages/lib/markdownEmailBody.js +15 -0
- package/dist/modules/messages/lib/markdownEmailBody.js.map +7 -0
- package/dist/modules/messages/lib/messageSourceEntityTypes.js +7 -0
- package/dist/modules/messages/lib/messageSourceEntityTypes.js.map +7 -0
- package/dist/modules/messages/lib/routeHelpers.js +43 -0
- package/dist/modules/messages/lib/routeHelpers.js.map +2 -2
- package/dist/modules/messages/subscribers/message-notification.js +30 -1
- package/dist/modules/messages/subscribers/message-notification.js.map +2 -2
- package/dist/modules/notifications/api/[id]/action/route.js +2 -1
- package/dist/modules/notifications/api/[id]/action/route.js.map +2 -2
- package/dist/modules/notifications/api/openapi.js +2 -1
- package/dist/modules/notifications/api/openapi.js.map +2 -2
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +47 -28
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
- package/dist/modules/notifications/lib/deliveryConfig.js +27 -2
- package/dist/modules/notifications/lib/deliveryConfig.js.map +2 -2
- package/dist/modules/notifications/lib/notificationMapper.js +3 -1
- package/dist/modules/notifications/lib/notificationMapper.js.map +2 -2
- package/dist/modules/notifications/lib/safeHref.js +4 -0
- package/dist/modules/notifications/lib/safeHref.js.map +2 -2
- package/dist/modules/planner/api/availability-date-specific.js +2 -1
- package/dist/modules/planner/api/availability-date-specific.js.map +2 -2
- package/dist/modules/planner/api/availability-weekly.js +2 -1
- package/dist/modules/planner/api/availability-weekly.js.map +2 -2
- package/dist/modules/progress/api/active/route.js +14 -2
- package/dist/modules/progress/api/active/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/[id]/route.js +38 -22
- package/dist/modules/progress/api/jobs/[id]/route.js.map +2 -2
- package/dist/modules/progress/api/jobs/route.js +20 -6
- package/dist/modules/progress/api/jobs/route.js.map +2 -2
- package/dist/modules/progress/api/requestScope.js +32 -0
- package/dist/modules/progress/api/requestScope.js.map +7 -0
- package/dist/modules/progress/lib/organizationScope.js +10 -0
- package/dist/modules/progress/lib/organizationScope.js.map +7 -0
- package/dist/modules/progress/lib/progressService.js.map +2 -2
- package/dist/modules/progress/lib/progressServiceImpl.js +48 -15
- package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
- package/dist/modules/push_notifications/api/custom-send/route.js +4 -1
- package/dist/modules/push_notifications/api/custom-send/route.js.map +2 -2
- package/dist/modules/query_index/api/status.js +5 -3
- package/dist/modules/query_index/api/status.js.map +2 -2
- package/dist/modules/query_index/di.js +2 -1
- package/dist/modules/query_index/di.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +2 -1
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/lib/request-encryption.js +16 -0
- package/dist/modules/query_index/lib/request-encryption.js.map +7 -0
- package/dist/modules/query_index/lib/search-tokens.js +31 -1
- package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/assign/route.js +2 -1
- package/dist/modules/resources/api/resources/tags/assign/route.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/unassign/route.js +2 -1
- package/dist/modules/resources/api/resources/tags/unassign/route.js.map +2 -2
- package/dist/modules/resources/commands/activities.js +2 -2
- package/dist/modules/resources/commands/activities.js.map +2 -2
- package/dist/modules/sales/api/document-history/route.js +5 -1
- package/dist/modules/sales/api/document-history/route.js.map +2 -2
- package/dist/modules/sales/api/document-numbers/route.js +2 -1
- package/dist/modules/sales/api/document-numbers/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/convert/route.js +2 -1
- package/dist/modules/sales/api/quotes/convert/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/send/route.js +2 -1
- package/dist/modules/sales/api/quotes/send/route.js.map +2 -2
- package/dist/modules/sales/api/settings/document-numbers/route.js +7 -1
- package/dist/modules/sales/api/settings/document-numbers/route.js.map +2 -2
- package/dist/modules/sales/api/settings/order-editing/route.js +7 -1
- package/dist/modules/sales/api/settings/order-editing/route.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +354 -138
- package/dist/modules/sales/commands/documents.js.map +3 -3
- package/dist/modules/sales/commands/payments.js +2 -1
- package/dist/modules/sales/commands/payments.js.map +2 -2
- package/dist/modules/sales/commands/settings.js +25 -5
- package/dist/modules/sales/commands/settings.js.map +2 -2
- package/dist/modules/sales/commands/shipments.js +2 -1
- package/dist/modules/sales/commands/shipments.js.map +2 -2
- package/dist/modules/sales/components/DocumentNumberSettings.js +32 -21
- package/dist/modules/sales/components/DocumentNumberSettings.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +49 -11
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/lib/lineSnapshots.js +19 -0
- package/dist/modules/sales/lib/lineSnapshots.js.map +2 -2
- package/dist/modules/sales/setup.js +4 -4
- package/dist/modules/sales/setup.js.map +2 -2
- package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +2 -2
- package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +2 -2
- package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +2 -2
- package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/accept/route.js +2 -1
- package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/reject/route.js +2 -1
- package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests.js +2 -1
- package/dist/modules/staff/api/leave-requests.js.map +2 -2
- package/dist/modules/staff/api/team-members/self/route.js +2 -1
- package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/assign/route.js +2 -1
- package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/unassign/route.js +2 -1
- package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.js +12 -11
- package/dist/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.js.map +2 -2
- package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js +2 -1
- package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/my-projects/route.js +2 -1
- package/dist/modules/staff/api/timesheets/my-projects/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/my-work/route.js +2 -1
- package/dist/modules/staff/api/timesheets/my-work/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/projects/kpis/route.js +2 -1
- package/dist/modules/staff/api/timesheets/projects/kpis/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/reports/[id]/close/route.js +2 -1
- package/dist/modules/staff/api/timesheets/reports/[id]/close/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/reports/[id]/export/route.js +3 -2
- package/dist/modules/staff/api/timesheets/reports/[id]/export/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/reports/[id]/sheet/route.js +3 -2
- package/dist/modules/staff/api/timesheets/reports/[id]/sheet/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/reports/[id]/unlock/route.js +3 -2
- package/dist/modules/staff/api/timesheets/reports/[id]/unlock/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/reports/preview/route.js +2 -1
- package/dist/modules/staff/api/timesheets/reports/preview/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/reports/shared.js +2 -1
- package/dist/modules/staff/api/timesheets/reports/shared.js.map +2 -2
- package/dist/modules/staff/api/timesheets/settings/reapply-rounding/route.js +2 -1
- package/dist/modules/staff/api/timesheets/settings/reapply-rounding/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/tags/entry-assignments/route.js +2 -1
- package/dist/modules/staff/api/timesheets/tags/entry-assignments/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/tags/task-assignments/route.js +2 -1
- package/dist/modules/staff/api/timesheets/tags/task-assignments/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js +2 -1
- package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/tasks/[id]/status/route.js +2 -1
- package/dist/modules/staff/api/timesheets/tasks/[id]/status/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.js +3 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js +2 -1
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js +2 -1
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +2 -1
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js +2 -1
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js +2 -1
- package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/copy-day/route.js +3 -2
- package/dist/modules/staff/api/timesheets/time-entries/copy-day/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/overlaps/route.js +2 -1
- package/dist/modules/staff/api/timesheets/time-entries/overlaps/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +3 -2
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.js +3 -2
- package/dist/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-projects/route.js +2 -1
- package/dist/modules/staff/api/timesheets/time-projects/route.js.map +2 -2
- package/dist/modules/staff/commands/activities.js +2 -2
- package/dist/modules/staff/commands/activities.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-entries.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-reports.js +55 -14
- package/dist/modules/staff/commands/timesheets-reports.js.map +2 -2
- package/dist/modules/staff/lib/timesheets-reports/buildReportSheet.js +2 -1
- package/dist/modules/staff/lib/timesheets-reports/buildReportSheet.js.map +2 -2
- package/dist/modules/staff/lib/timesheets-reports/loadReportData.js +32 -3
- package/dist/modules/staff/lib/timesheets-reports/loadReportData.js.map +2 -2
- package/dist/modules/staff/lib/timesheets-reports/reportPeriod.js +16 -0
- package/dist/modules/staff/lib/timesheets-reports/reportPeriod.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/CalendarPicker.js +15 -10
- package/dist/modules/staff/lib/timesheets-ui/CalendarPicker.js.map +2 -2
- package/dist/modules/sync_excel/api/import/route.js +35 -12
- package/dist/modules/sync_excel/api/import/route.js.map +2 -2
- package/dist/modules/sync_excel/api/upload/route.js +29 -4
- package/dist/modules/sync_excel/api/upload/route.js.map +2 -2
- package/dist/modules/translations/api/context.js +2 -1
- package/dist/modules/translations/api/context.js.map +2 -2
- package/dist/modules/warranty_claims/api/ai/assess/route.js +2 -1
- package/dist/modules/warranty_claims/api/ai/assess/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/ai/draft-reply/route.js +2 -1
- package/dist/modules/warranty_claims/api/ai/draft-reply/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/ai/suggest/route.js +2 -1
- package/dist/modules/warranty_claims/api/ai/suggest/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/assign/route.js +2 -1
- package/dist/modules/warranty_claims/api/assign/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/credit-memo/route.js +2 -1
- package/dist/modules/warranty_claims/api/credit-memo/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/entitlement/route.js +2 -1
- package/dist/modules/warranty_claims/api/entitlement/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/events/route.js +2 -1
- package/dist/modules/warranty_claims/api/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/external/claims/route.js +2 -1
- package/dist/modules/warranty_claims/api/external/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/receiving/route.js +2 -1
- package/dist/modules/warranty_claims/api/receiving/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/replacement-order/route.js +2 -1
- package/dist/modules/warranty_claims/api/replacement-order/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/return-label/route.js +2 -1
- package/dist/modules/warranty_claims/api/return-label/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/risk/route.js +2 -1
- package/dist/modules/warranty_claims/api/risk/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/sales-return/route.js +2 -1
- package/dist/modules/warranty_claims/api/sales-return/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/settings-general/route.js +2 -1
- package/dist/modules/warranty_claims/api/settings-general/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/stats/route.js +9 -5
- package/dist/modules/warranty_claims/api/stats/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/submit/route.js +2 -1
- package/dist/modules/warranty_claims/api/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/transition/route.js +2 -1
- package/dist/modules/warranty_claims/api/transition/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js +2 -1
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js +2 -1
- package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js.map +2 -2
- package/dist/modules/warranty_claims/widgets/notifications/WarrantyClaimNotificationRenderer.js +1 -1
- package/dist/modules/warranty_claims/widgets/notifications/WarrantyClaimNotificationRenderer.js.map +2 -2
- package/dist/modules/wms/api/dashboard/operational/route.js +2 -1
- package/dist/modules/wms/api/dashboard/operational/route.js.map +2 -2
- package/dist/modules/wms/api/inventory/cycle-count/route.js +1 -0
- package/dist/modules/wms/api/inventory/cycle-count/route.js.map +2 -2
- package/dist/modules/wms/api/inventory/helpers.js +2 -1
- package/dist/modules/wms/api/inventory/helpers.js.map +2 -2
- package/dist/modules/wms/api/inventory/import/helpers.js +2 -1
- package/dist/modules/wms/api/inventory/import/helpers.js.map +2 -2
- package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js +2 -1
- package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js.map +2 -2
- package/dist/modules/wms/commands/inventory-actions.js +39 -1
- package/dist/modules/wms/commands/inventory-actions.js.map +2 -2
- package/dist/modules/wms/components/backend/CycleCountWizardDialog.js +14 -1
- package/dist/modules/wms/components/backend/CycleCountWizardDialog.js.map +2 -2
- package/dist/modules/wms/di.js +5 -0
- package/dist/modules/wms/di.js.map +2 -2
- package/dist/modules/wms/lib/availabilityCache.js +86 -0
- package/dist/modules/wms/lib/availabilityCache.js.map +7 -0
- package/dist/modules/wms/lib/availabilityCalculation.js +212 -0
- package/dist/modules/wms/lib/availabilityCalculation.js.map +7 -0
- package/dist/modules/wms/lib/availabilityProvider.js +24 -0
- package/dist/modules/wms/lib/availabilityProvider.js.map +7 -0
- package/dist/modules/wms/lib/loadOperationalDashboard.js +42 -5
- package/dist/modules/wms/lib/loadOperationalDashboard.js.map +2 -2
- package/dist/modules/wms/lib/lowStockBalanceFilter.js +23 -10
- package/dist/modules/wms/lib/lowStockBalanceFilter.js.map +2 -2
- package/dist/modules/wms/lib/sqlInClause.js +9 -0
- package/dist/modules/wms/lib/sqlInClause.js.map +7 -0
- package/dist/modules/wms/lib/tryResolve.js +11 -0
- package/dist/modules/wms/lib/tryResolve.js.map +7 -0
- package/dist/modules/wms/subscribers/invalidate-availability-cache-policy.js +14 -0
- package/dist/modules/wms/subscribers/invalidate-availability-cache-policy.js.map +7 -0
- package/dist/modules/wms/subscribers/invalidate-availability-cache-profile.js +14 -0
- package/dist/modules/wms/subscribers/invalidate-availability-cache-profile.js.map +7 -0
- package/dist/modules/wms/subscribers/invalidate-availability-cache.js +16 -0
- package/dist/modules/wms/subscribers/invalidate-availability-cache.js.map +7 -0
- package/dist/modules/wms/widgets/notifications/WmsLowStockRenderer.js +2 -2
- package/dist/modules/wms/widgets/notifications/WmsLowStockRenderer.js.map +2 -2
- package/dist/modules/wms/widgets/notifications/WmsReservationShortfallRenderer.js +4 -3
- package/dist/modules/wms/widgets/notifications/WmsReservationShortfallRenderer.js.map +2 -2
- package/dist/modules/workflows/api/commands/route.js +4 -1
- package/dist/modules/workflows/api/commands/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/callers/route.js +4 -1
- package/dist/modules/workflows/api/definitions/[id]/callers/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/context-schema/route.js +4 -2
- package/dist/modules/workflows/api/definitions/[id]/context-schema/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/customize/route.js +4 -1
- package/dist/modules/workflows/api/definitions/[id]/customize/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/draft/route.js +6 -1
- package/dist/modules/workflows/api/definitions/[id]/draft/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/publish/route.js +6 -1
- package/dist/modules/workflows/api/definitions/[id]/publish/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/reset-to-code/route.js +4 -1
- package/dist/modules/workflows/api/definitions/[id]/reset-to-code/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/route.js +5 -3
- package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/test-step/route.js +4 -2
- package/dist/modules/workflows/api/definitions/[id]/test-step/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/generate/route.js +4 -1
- package/dist/modules/workflows/api/definitions/generate/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/route.js +4 -2
- package/dist/modules/workflows/api/definitions/route.js.map +2 -2
- package/dist/modules/workflows/api/endpoints/route.js +4 -1
- package/dist/modules/workflows/api/endpoints/route.js.map +2 -2
- package/dist/modules/workflows/api/functions/route.js +4 -1
- package/dist/modules/workflows/api/functions/route.js.map +2 -2
- package/dist/modules/workflows/api/grantable-features/route.js +2 -1
- package/dist/modules/workflows/api/grantable-features/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/[id]/advance/route.js +4 -1
- package/dist/modules/workflows/api/instances/[id]/advance/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/[id]/cancel/route.js +4 -1
- package/dist/modules/workflows/api/instances/[id]/cancel/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/[id]/context/route.js +2 -1
- package/dist/modules/workflows/api/instances/[id]/context/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/[id]/rerun-step/route.js +4 -1
- package/dist/modules/workflows/api/instances/[id]/rerun-step/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/[id]/retry/route.js +4 -1
- package/dist/modules/workflows/api/instances/[id]/retry/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/[id]/signal/route.js +4 -1
- package/dist/modules/workflows/api/instances/[id]/signal/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/[id]/step-through/route.js +4 -2
- package/dist/modules/workflows/api/instances/[id]/step-through/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/route.js +4 -2
- package/dist/modules/workflows/api/instances/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/validate-start/route.js +4 -1
- package/dist/modules/workflows/api/instances/validate-start/route.js.map +2 -2
- package/dist/modules/workflows/api/rule-usage/route.js +4 -1
- package/dist/modules/workflows/api/rule-usage/route.js.map +2 -2
- package/dist/modules/workflows/api/signals/route.js +4 -1
- package/dist/modules/workflows/api/signals/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/[id]/claim/route.js +4 -1
- package/dist/modules/workflows/api/tasks/[id]/claim/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/[id]/complete/route.js +4 -1
- package/dist/modules/workflows/api/tasks/[id]/complete/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/[id]/reassign/route.js +2 -1
- package/dist/modules/workflows/api/tasks/[id]/reassign/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/[id]/unclaim/route.js +4 -1
- package/dist/modules/workflows/api/tasks/[id]/unclaim/route.js.map +2 -2
- package/dist/modules/workflows/api/templates/route.js +4 -1
- package/dist/modules/workflows/api/templates/route.js.map +2 -2
- package/dist/modules/workflows/api/work-inbox/next/route.js +4 -1
- package/dist/modules/workflows/api/work-inbox/next/route.js.map +2 -2
- package/dist/modules/workflows/lib/event-trigger-service.js +8 -1
- package/dist/modules/workflows/lib/event-trigger-service.js.map +2 -2
- package/dist/modules/workflows/lib/step-handler.js +4 -4
- package/dist/modules/workflows/lib/step-handler.js.map +2 -2
- package/dist/test-utils/transactionalEntityManagerDouble.js +37 -0
- package/dist/test-utils/transactionalEntityManagerDouble.js.map +7 -0
- package/generated/entities/availability_policy/index.ts +19 -0
- package/generated/entities/communication_channel/index.ts +1 -0
- package/generated/entities/customer_email_conversation_share/index.ts +9 -0
- package/generated/entities/customer_group/index.ts +15 -0
- package/generated/entities/customer_group_membership/index.ts +13 -0
- package/generated/entities/customer_group_terms/index.ts +15 -0
- package/generated/entities/customer_interaction/index.ts +1 -0
- package/generated/entities/form/index.ts +16 -0
- package/generated/entities/form_access_audit/index.ts +9 -0
- package/generated/entities/form_attachment/index.ts +13 -0
- package/generated/entities/form_consent_record/index.ts +17 -0
- package/generated/entities/form_distribution/index.ts +22 -0
- package/generated/entities/form_invitation/index.ts +23 -0
- package/generated/entities/form_submission/index.ts +18 -0
- package/generated/entities/form_submission_actor/index.ts +10 -0
- package/generated/entities/form_submission_revision/index.ts +13 -0
- package/generated/entities/form_version/index.ts +17 -0
- package/generated/entities/forms_encryption_key/index.ts +6 -0
- package/generated/entities.ids.generated.ts +27 -2
- package/generated/entity-fields-registry.ts +269 -0
- package/package.json +19 -9
- package/src/helpers/integration/communicationChannelsFixtures.ts +43 -0
- package/src/helpers/integration/customerGroupsFixtures.ts +171 -0
- package/src/helpers/integration/formsFixtures.ts +757 -0
- package/src/modules/api_docs/api/get/version.ts +28 -11
- package/src/modules/api_keys/api/keys/route.ts +89 -199
- package/src/modules/api_keys/backend/api-keys/create/page.tsx +2 -2
- package/src/modules/api_keys/commands/keys.ts +172 -0
- 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/api_keys/services/apiKeyService.ts +82 -49
- package/src/modules/attachments/api/file/[id]/route.ts +9 -4
- package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +6 -4
- package/src/modules/attachments/api/route.ts +10 -4
- package/src/modules/attachments/components/AttachmentLibrary.tsx +16 -5
- package/src/modules/attachments/lib/access.ts +1 -3
- package/src/modules/attachments/lib/requestScope.ts +35 -3
- package/src/modules/audit_logs/api/audit-logs/actions/export/route.ts +6 -1
- package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +41 -6
- package/src/modules/audit_logs/api/audit-logs/actions/route.ts +6 -1
- package/src/modules/audit_logs/api/audit-logs/actions/undo/route.ts +39 -4
- package/src/modules/audit_logs/data/validators.ts +1 -0
- package/src/modules/audit_logs/lib/actorSubject.ts +82 -0
- package/src/modules/audit_logs/lib/replayAuthorization.ts +61 -0
- package/src/modules/audit_logs/services/actionLogService.ts +191 -47
- package/src/modules/auth/api/features.ts +1 -1
- package/src/modules/auth/cli.ts +31 -17
- package/src/modules/auth/commands/acl.ts +26 -36
- package/src/modules/auth/commands/interceptors.ts +23 -0
- package/src/modules/auth/commands/roles.ts +475 -148
- package/src/modules/auth/commands/users.ts +693 -81
- package/src/modules/auth/i18n/de.json +22 -0
- package/src/modules/auth/i18n/en.json +22 -0
- package/src/modules/auth/i18n/es.json +22 -0
- package/src/modules/auth/i18n/ko.json +22 -0
- package/src/modules/auth/i18n/pl.json +22 -0
- package/src/modules/auth/lib/authorizationStateLocks.ts +340 -0
- package/src/modules/auth/lib/commandReplay.ts +194 -0
- package/src/modules/auth/lib/executionPrincipal.ts +36 -28
- package/src/modules/auth/lib/grantChecks.ts +10 -2
- package/src/modules/auth/lib/setup-app.ts +97 -50
- package/src/modules/auth/lib/tokenHash.ts +16 -1
- package/src/modules/auth/services/rbacService.ts +578 -6
- package/src/modules/availability/acl.ts +12 -0
- package/src/modules/availability/api/check/route.ts +136 -0
- package/src/modules/availability/api/openapi.ts +28 -0
- package/src/modules/availability/api/policies/resolve-preview/route.ts +83 -0
- package/src/modules/availability/api/policies/route.ts +243 -0
- package/src/modules/availability/backend/availability/check/page.meta.ts +12 -0
- package/src/modules/availability/backend/availability/check/page.tsx +226 -0
- package/src/modules/availability/backend/availability/policies/ResolutionPreviewPanel.tsx +112 -0
- package/src/modules/availability/backend/availability/policies/ScopeFields.tsx +55 -0
- package/src/modules/availability/backend/availability/policies/StockManagedField.tsx +37 -0
- package/src/modules/availability/backend/availability/policies/[id]/page.meta.ts +8 -0
- package/src/modules/availability/backend/availability/policies/[id]/page.tsx +202 -0
- package/src/modules/availability/backend/availability/policies/create/page.meta.ts +8 -0
- package/src/modules/availability/backend/availability/policies/create/page.tsx +134 -0
- package/src/modules/availability/backend/availability/policies/formGroups.ts +83 -0
- package/src/modules/availability/backend/availability/policies/page.meta.ts +12 -0
- package/src/modules/availability/backend/availability/policies/page.tsx +232 -0
- package/src/modules/availability/backend/availability/policies/policyPayload.ts +29 -0
- package/src/modules/availability/commands/policies.ts +430 -0
- package/src/modules/availability/commands/scope.ts +26 -0
- package/src/modules/availability/data/entities.ts +93 -0
- package/src/modules/availability/data/validators.ts +87 -0
- package/src/modules/availability/di.ts +68 -0
- package/src/modules/availability/events.ts +17 -0
- package/src/modules/availability/i18n/de.json +93 -0
- package/src/modules/availability/i18n/en.json +93 -0
- package/src/modules/availability/i18n/es.json +93 -0
- package/src/modules/availability/i18n/ko.json +93 -0
- package/src/modules/availability/i18n/pl.json +93 -0
- package/src/modules/availability/index.ts +13 -0
- package/src/modules/availability/lib/catalogTarget.ts +72 -0
- package/src/modules/availability/lib/organizationScope.ts +55 -0
- package/src/modules/availability/lib/policyResolution.ts +289 -0
- package/src/modules/availability/lib/tryResolve.ts +8 -0
- package/src/modules/availability/migrations/.snapshot-open-mercato.json +375 -0
- package/src/modules/availability/migrations/Migration20260922095844_availability.ts +13 -0
- package/src/modules/availability/migrations/Migration20260930120000_availability.ts +22 -0
- package/src/modules/availability/setup.ts +10 -0
- package/src/modules/catalog/api/categories/bulk-create/route.ts +133 -0
- package/src/modules/catalog/api/categories/route.ts +70 -8
- package/src/modules/catalog/api/products/bulk-create/route.ts +133 -0
- package/src/modules/catalog/api/products/route.ts +55 -24
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +9 -16
- package/src/modules/catalog/backend/catalog/products/create/page.tsx +16 -10
- package/src/modules/catalog/commands/categories.ts +1 -0
- package/src/modules/catalog/commands/products.ts +11 -7
- package/src/modules/catalog/components/PriceKindSettings.tsx +6 -20
- package/src/modules/catalog/components/prices/PriceScopeSelectors.tsx +2 -2
- package/src/modules/catalog/components/products/ProductUomSection.tsx +26 -37
- package/src/modules/catalog/components/products/productForm.ts +6 -4
- package/src/modules/catalog/data/validators.ts +22 -0
- package/src/modules/catalog/di.ts +2 -1
- package/src/modules/catalog/events.ts +9 -3
- package/src/modules/catalog/i18n/de.json +40 -0
- package/src/modules/catalog/i18n/en.json +40 -0
- package/src/modules/catalog/i18n/es.json +40 -0
- package/src/modules/catalog/i18n/ko.json +40 -0
- package/src/modules/catalog/i18n/pl.json +40 -0
- package/src/modules/catalog/lib/bulkCreateCategories.ts +349 -0
- package/src/modules/catalog/lib/bulkCreateCheckpoint.ts +176 -0
- package/src/modules/catalog/lib/bulkCreateMutationGuards.ts +62 -0
- package/src/modules/catalog/lib/bulkCreateProducts.ts +340 -0
- package/src/modules/catalog/lib/bulkDelete.ts +10 -14
- package/src/modules/catalog/lib/catalogQueue.ts +14 -0
- package/src/modules/catalog/lib/currencyOptions.ts +28 -0
- package/src/modules/catalog/lib/productSearch.ts +16 -0
- package/src/modules/catalog/lib/seeds.ts +2 -43
- package/src/modules/catalog/lib/unitLabels.ts +52 -0
- package/src/modules/catalog/migrations/Migration20260914120000.ts +51 -0
- package/src/modules/catalog/migrations/Migration20260914120100.ts +50 -0
- package/src/modules/catalog/workers/catalog-category-bulk-create.ts +48 -0
- package/src/modules/catalog/workers/catalog-product-bulk-create.ts +48 -0
- package/src/modules/communication_channels/acl.ts +8 -0
- package/src/modules/communication_channels/api/get/me/channels/route.ts +4 -0
- package/src/modules/communication_channels/api/get/oauth/[provider]/callback/route.ts +23 -0
- package/src/modules/communication_channels/api/post/channels/[id]/import-history/route.ts +3 -4
- package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +9 -1
- package/src/modules/communication_channels/api/post/test-seed/route.ts +109 -24
- package/src/modules/communication_channels/api/put/channels/[id]/visibility/route.ts +165 -0
- package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +247 -129
- package/src/modules/communication_channels/commands/ingest-inbound-message.ts +2 -1
- package/src/modules/communication_channels/commands/set-channel-visibility.ts +138 -0
- package/src/modules/communication_channels/data/enrichers.ts +42 -19
- package/src/modules/communication_channels/data/entities.ts +21 -0
- package/src/modules/communication_channels/di.ts +9 -0
- package/src/modules/communication_channels/events.ts +7 -0
- package/src/modules/communication_channels/i18n/de.json +13 -3
- package/src/modules/communication_channels/i18n/en.json +13 -3
- package/src/modules/communication_channels/i18n/es.json +13 -3
- package/src/modules/communication_channels/i18n/ko.json +13 -3
- package/src/modules/communication_channels/i18n/pl.json +13 -3
- package/src/modules/communication_channels/index.ts +1 -1
- package/src/modules/communication_channels/lib/access-control.ts +4 -1
- package/src/modules/communication_channels/lib/adapter.ts +16 -6
- package/src/modules/communication_channels/lib/channel-thread-access.ts +133 -0
- package/src/modules/communication_channels/lib/credential-refresh.ts +27 -5
- package/src/modules/communication_channels/lib/inbound-message-origin.ts +62 -0
- package/src/modules/communication_channels/lib/oauth-state.ts +96 -1
- package/src/modules/communication_channels/lib/outbound-delivery-intent.ts +57 -0
- package/src/modules/communication_channels/lib/send-as-user.ts +2 -2
- package/src/modules/communication_channels/migrations/.snapshot-open-mercato.json +18 -1
- package/src/modules/communication_channels/migrations/Migration20260825141000_communication_channels.ts +22 -0
- package/src/modules/communication_channels/setup.ts +4 -0
- package/src/modules/communication_channels/subscribers/outbound-bridge.ts +34 -9
- package/src/modules/communication_channels/workers/gmail-history-sync.ts +1 -1
- package/src/modules/communication_channels/workers/poll-channel.ts +1 -1
- package/src/modules/communication_channels/workers/reaction-processor.ts +2 -2
- package/src/modules/configs/api/upgrade-actions/route.ts +2 -1
- package/src/modules/configs/cli.ts +2 -2
- package/src/modules/configs/i18n/de.json +2 -0
- package/src/modules/configs/i18n/en.json +2 -0
- package/src/modules/configs/i18n/es.json +2 -0
- package/src/modules/configs/i18n/ko.json +2 -0
- package/src/modules/configs/i18n/pl.json +2 -0
- package/src/modules/configs/lib/upgrade-actions.ts +29 -6
- package/src/modules/configs/services/upgradeActionsService.ts +45 -30
- package/src/modules/currencies/api/currencies/route.ts +11 -5
- package/src/modules/currencies/api/exchange-rates/route.ts +11 -5
- package/src/modules/customer_accounts/AGENTS.md +1 -0
- package/src/modules/customer_accounts/api/admin/users/[id]/sessions/[sessionId].ts +89 -0
- package/src/modules/customer_groups/acl.ts +10 -0
- package/src/modules/customer_groups/api/customer-groups/[id]/terms/route.ts +355 -0
- package/src/modules/customer_groups/api/customer-groups/crud.ts +627 -0
- package/src/modules/customer_groups/api/customer-groups/explain-terms/route.ts +195 -0
- package/src/modules/customer_groups/api/customer-groups/memberships/crud.ts +525 -0
- package/src/modules/customer_groups/api/customer-groups/memberships/route.ts +65 -0
- package/src/modules/customer_groups/api/customer-groups/price-kinds/route.ts +99 -0
- package/src/modules/customer_groups/api/customer-groups/reconcile/adopt/route.ts +150 -0
- package/src/modules/customer_groups/api/customer-groups/reconcile/route.ts +90 -0
- package/src/modules/customer_groups/api/customer-groups/reorder/route.ts +147 -0
- package/src/modules/customer_groups/api/customer-groups/route.ts +65 -0
- package/src/modules/customer_groups/api/openapi.ts +28 -0
- package/src/modules/customer_groups/backend/customer-groups/[id]/edit/page.meta.ts +12 -0
- package/src/modules/customer_groups/backend/customer-groups/[id]/edit/page.tsx +392 -0
- package/src/modules/customer_groups/backend/customer-groups/create/page.meta.ts +25 -0
- package/src/modules/customer_groups/backend/customer-groups/create/page.tsx +279 -0
- package/src/modules/customer_groups/backend/customer-groups/orphans/page.meta.ts +12 -0
- package/src/modules/customer_groups/backend/customer-groups/orphans/page.tsx +216 -0
- package/src/modules/customer_groups/backend/customer-groups/page.meta.ts +11 -0
- package/src/modules/customer_groups/backend/customer-groups/page.tsx +484 -0
- package/src/modules/customer_groups/cli.ts +87 -0
- package/src/modules/customer_groups/commands/reorderGroups.ts +163 -0
- package/src/modules/customer_groups/components/CustomerGroupDefaultField.tsx +55 -0
- package/src/modules/customer_groups/components/CustomerGroupParentField.tsx +194 -0
- package/src/modules/customer_groups/components/CustomerGroupTermsPriceKindField.tsx +112 -0
- package/src/modules/customer_groups/components/CustomerGroupTermsSection.tsx +277 -0
- package/src/modules/customer_groups/components/GroupPickerField.tsx +142 -0
- package/src/modules/customer_groups/components/OrphanBanner.tsx +92 -0
- package/src/modules/customer_groups/components/customerGroupTree.ts +84 -0
- package/src/modules/customer_groups/data/entities.ts +244 -0
- package/src/modules/customer_groups/data/validators.ts +232 -0
- package/src/modules/customer_groups/di.ts +25 -0
- package/src/modules/customer_groups/events.ts +22 -0
- package/src/modules/customer_groups/i18n/de.json +199 -0
- package/src/modules/customer_groups/i18n/en.json +199 -0
- package/src/modules/customer_groups/i18n/es.json +199 -0
- package/src/modules/customer_groups/i18n/ko.json +199 -0
- package/src/modules/customer_groups/i18n/pl.json +199 -0
- package/src/modules/customer_groups/index.ts +13 -0
- package/src/modules/customer_groups/lib/createConflictRecheck.ts +39 -0
- package/src/modules/customer_groups/lib/customerScope.ts +145 -0
- package/src/modules/customer_groups/lib/groupEvents.ts +50 -0
- package/src/modules/customer_groups/lib/priceKindScope.ts +65 -0
- package/src/modules/customer_groups/lib/reconcile.ts +299 -0
- package/src/modules/customer_groups/migrations/.snapshot-open-mercato.json +905 -0
- package/src/modules/customer_groups/migrations/Migration20260922095316_customer_groups.ts +26 -0
- package/src/modules/customer_groups/migrations/Migration20260922103033_customer_groups.ts +22 -0
- package/src/modules/customer_groups/migrations/Migration20260922105444_customer_groups.ts +17 -0
- package/src/modules/customer_groups/migrations/Migration20260923193330_customer_groups.ts +20 -0
- package/src/modules/customer_groups/migrations/Migration20260929083000_customer_groups.ts +21 -0
- package/src/modules/customer_groups/services/customerGroupsService.ts +358 -0
- package/src/modules/customer_groups/setup.ts +11 -0
- package/src/modules/customer_groups/widgets/injection/group-picker-field/widget.ts +41 -0
- package/src/modules/customer_groups/widgets/injection/person-groups-explain-terms.tsx +227 -0
- package/src/modules/customer_groups/widgets/injection/person-groups-tab/widget.ts +21 -0
- package/src/modules/customer_groups/widgets/injection/person-groups-tab.tsx +573 -0
- package/src/modules/customer_groups/widgets/injection-table.ts +35 -0
- package/src/modules/customers/acl.ts +13 -0
- package/src/modules/customers/api/activities/route.ts +10 -0
- package/src/modules/customers/api/companies/[id]/people/route.ts +2 -1
- package/src/modules/customers/api/companies/[id]/route.ts +20 -6
- package/src/modules/customers/api/deals/[id]/companies/route.ts +2 -1
- package/src/modules/customers/api/deals/[id]/people/route.ts +2 -1
- package/src/modules/customers/api/deals/map/route.ts +11 -10
- package/src/modules/customers/api/dictionaries/context.ts +2 -1
- package/src/modules/customers/api/entity-roles-factory.ts +3 -2
- package/src/modules/customers/api/interactions/[id]/visibility/route.ts +2 -1
- package/src/modules/customers/api/interactions/cancel/route.ts +2 -1
- package/src/modules/customers/api/interactions/complete/route.ts +2 -1
- package/src/modules/customers/api/interactions/conflicts/route.ts +10 -10
- package/src/modules/customers/api/interactions/counts/route.ts +25 -7
- package/src/modules/customers/api/interactions/route.ts +34 -10
- package/src/modules/customers/api/labels/assign/route.ts +2 -1
- package/src/modules/customers/api/labels/route.ts +3 -2
- package/src/modules/customers/api/labels/unassign/route.ts +2 -1
- package/src/modules/customers/api/people/[id]/companies/context.ts +11 -2
- package/src/modules/customers/api/people/[id]/email-share/route.ts +276 -0
- package/src/modules/customers/api/people/[id]/email-threads/route.ts +14 -1
- package/src/modules/customers/api/people/[id]/emails/route.ts +2 -1
- package/src/modules/customers/api/people/[id]/route.ts +31 -22
- package/src/modules/customers/api/people/check-phone/route.ts +2 -1
- package/src/modules/customers/api/pipeline-stages/reorder/route.ts +3 -2
- package/src/modules/customers/api/pipeline-stages/route.ts +3 -3
- package/src/modules/customers/api/pipelines/route.ts +3 -3
- package/src/modules/customers/api/settings/address-format/route.ts +2 -1
- package/src/modules/customers/api/settings/dictionary-sort-modes/route.ts +2 -1
- package/src/modules/customers/api/settings/stuck-threshold/route.ts +2 -1
- package/src/modules/customers/api/tags/assign/route.ts +2 -1
- package/src/modules/customers/api/tags/unassign/route.ts +2 -1
- package/src/modules/customers/backend/customers/companies/[id]/page.tsx +4 -2
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.ts +10 -6
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +6 -1
- package/src/modules/customers/backend/customers/deals/components/ReassignOwnerDialog.tsx +115 -0
- package/src/modules/customers/backend/customers/deals/page.tsx +121 -1
- package/src/modules/customers/backend/customers/people/[id]/page.tsx +4 -2
- package/src/modules/customers/commands/addresses.ts +117 -15
- package/src/modules/customers/commands/companies.ts +26 -5
- package/src/modules/customers/commands/emailConversationShares.ts +261 -0
- package/src/modules/customers/commands/index.ts +1 -0
- package/src/modules/customers/commands/interactions.ts +61 -21
- package/src/modules/customers/commands/people.ts +3 -2
- package/src/modules/customers/commands/shared.ts +4 -0
- package/src/modules/customers/components/calendar/types.ts +1 -1
- package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +3 -11
- package/src/modules/customers/components/detail/DealForm.tsx +111 -2
- package/src/modules/customers/components/detail/DealOwnerSelect.tsx +161 -0
- package/src/modules/customers/components/detail/DealsSection.tsx +8 -14
- package/src/modules/customers/components/detail/PersonEmailShareControl.tsx +273 -0
- package/src/modules/customers/components/detail/PersonEmailThreadsTab.tsx +26 -0
- package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +10 -2
- package/src/modules/customers/components/detail/create/CreateDealForm.tsx +30 -0
- package/src/modules/customers/components/detail/create/DealDetailsFields.tsx +12 -0
- package/src/modules/customers/components/detail/create/DealSectionCard.tsx +7 -2
- package/src/modules/customers/components/detail/create/dealFormTypes.ts +3 -0
- package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +16 -4
- package/src/modules/customers/components/formConfig.tsx +3 -0
- package/src/modules/customers/components/linking/LinkEntityDialog.tsx +8 -0
- package/src/modules/customers/data/enrichers.ts +64 -5
- package/src/modules/customers/data/entities.ts +105 -1
- package/src/modules/customers/data/validators.ts +22 -0
- package/src/modules/customers/events.ts +5 -0
- package/src/modules/customers/i18n/de.json +28 -5
- package/src/modules/customers/i18n/en.json +23 -0
- package/src/modules/customers/i18n/es.json +23 -0
- package/src/modules/customers/i18n/ko.json +23 -0
- package/src/modules/customers/i18n/pl.json +23 -0
- package/src/modules/customers/lib/assignableStaff.ts +17 -0
- package/src/modules/customers/lib/calendar/editorPayload.ts +20 -13
- package/src/modules/customers/lib/calendar/mapItem.ts +1 -0
- package/src/modules/customers/lib/conversationShares.ts +250 -0
- package/src/modules/customers/lib/dealsOrganizationScope.ts +5 -3
- package/src/modules/customers/lib/interactionRequestContext.ts +2 -1
- package/src/modules/customers/lib/link-channel-message-handler.ts +27 -4
- package/src/modules/customers/lib/personDetailCacheTags.ts +68 -0
- package/src/modules/customers/lib/personEmailThreads.ts +61 -6
- package/src/modules/customers/lib/reassignDealOwner.ts +51 -0
- package/src/modules/customers/lib/userNames.ts +39 -0
- package/src/modules/customers/lib/visibilityFilter.ts +170 -3
- package/src/modules/customers/migrations/.snapshot-open-mercato.json +240 -0
- package/src/modules/customers/migrations/Migration20260825120000_customers.ts +19 -0
- package/src/modules/customers/migrations/Migration20260825140000_customers.ts +41 -0
- package/src/modules/customers/setup.ts +1 -0
- package/src/modules/customers/subscribers/channel-visibility-changed.ts +50 -0
- package/src/modules/dashboards/api/widgets/data/batch/route.ts +3 -8
- package/src/modules/dashboards/api/widgets/data/route.ts +12 -18
- package/src/modules/dashboards/lib/aggregations.ts +1 -1
- package/src/modules/dashboards/lib/widgetScope.ts +2 -7
- package/src/modules/data_sync/AGENTS.md +35 -1
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +5 -1
- package/src/modules/data_sync/backend/data-sync/page.tsx +292 -19
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +205 -21
- package/src/modules/data_sync/components/useDataSyncRunAccess.ts +27 -0
- package/src/modules/data_sync/i18n/de.json +24 -0
- package/src/modules/data_sync/i18n/en.json +24 -0
- package/src/modules/data_sync/i18n/es.json +24 -0
- package/src/modules/data_sync/i18n/ko.json +24 -0
- package/src/modules/data_sync/i18n/pl.json +24 -0
- package/src/modules/data_sync/lib/adapter.ts +37 -0
- package/src/modules/data_sync/lib/resume-point.ts +63 -0
- package/src/modules/data_sync/lib/start-run.ts +61 -2
- package/src/modules/data_sync/lib/sync-run-service.ts +27 -34
- package/src/modules/devices/api/[id]/route.ts +3 -2
- package/src/modules/devices/api/admin/devices/route.ts +2 -1
- package/src/modules/devices/api/route.ts +2 -1
- package/src/modules/dictionaries/api/[dictionaryId]/route.ts +3 -5
- package/src/modules/dictionaries/api/context.ts +2 -1
- package/src/modules/dictionaries/api/route.ts +4 -0
- package/src/modules/dictionaries/components/DictionariesManager.tsx +12 -4
- package/src/modules/dictionaries/data/validators.ts +9 -1
- package/src/modules/dictionaries/i18n/de.json +3 -3
- package/src/modules/dictionaries/i18n/en.json +3 -3
- package/src/modules/dictionaries/i18n/es.json +3 -3
- package/src/modules/dictionaries/i18n/ko.json +3 -3
- package/src/modules/dictionaries/i18n/pl.json +3 -3
- package/src/modules/directory/api/organization-branding/route.ts +10 -1
- package/src/modules/directory/api/organizations/route.ts +1 -1
- package/src/modules/directory/backend/directory/branding/page.tsx +24 -2
- package/src/modules/directory/commands/organizations.ts +173 -80
- package/src/modules/directory/i18n/de.json +1 -0
- package/src/modules/directory/i18n/en.json +1 -0
- package/src/modules/directory/i18n/es.json +1 -0
- package/src/modules/directory/i18n/ko.json +1 -0
- package/src/modules/directory/i18n/pl.json +1 -0
- package/src/modules/directory/lib/hierarchy.ts +22 -1
- package/src/modules/directory/utils/organizationScope.ts +102 -15
- package/src/modules/directory/utils/organizationScopeFilter.ts +6 -3
- package/src/modules/directory/utils/organizationScopeGuard.ts +9 -5
- package/src/modules/entities/api/encryption.ts +73 -27
- package/src/modules/entities/api/records.ts +9 -8
- package/src/modules/entities/cli.ts +46 -24
- package/src/modules/entities/data/entities.ts +15 -0
- package/src/modules/entities/lib/definition-scope.ts +5 -1
- package/src/modules/entities/lib/encryption-maps.ts +164 -0
- package/src/modules/entities/lib/helpers.ts +95 -8
- package/src/modules/entities/migrations/.snapshot-open-mercato.json +21 -0
- package/src/modules/entities/migrations/.snapshot-openmercato.json +12 -0
- package/src/modules/entities/migrations/Migration20260910120000.ts +42 -0
- package/src/modules/entities/migrations/Migration20261004120000_encryption_map_scope_uniqueness.ts +130 -0
- package/src/modules/eudr/api/dashboard/widgets/compliance-overview/route.ts +2 -1
- package/src/modules/eudr/api/plots/import/route.ts +2 -1
- package/src/modules/eudr/api/product-mappings/suggestions/apply/route.ts +2 -1
- package/src/modules/eudr/api/product-mappings/suggestions/route.ts +2 -1
- package/src/modules/eudr/api/reports/annual/route.ts +2 -1
- package/src/modules/eudr/api/statements/[id]/export/route.ts +2 -1
- package/src/modules/eudr/api/statements/[id]/readiness/route.ts +2 -1
- package/src/modules/eudr/api/suppliers/compliance/route.ts +2 -1
- package/src/modules/eudr/i18n/de.json +1 -0
- package/src/modules/eudr/i18n/en.json +1 -0
- package/src/modules/eudr/i18n/es.json +1 -0
- package/src/modules/eudr/i18n/ko.json +1 -0
- package/src/modules/eudr/i18n/pl.json +1 -0
- package/src/modules/feature_toggles/commands/global.ts +18 -15
- package/src/modules/feature_toggles/lib/feature-flag-check.ts +8 -3
- package/src/modules/forms/acl.ts +10 -0
- package/src/modules/forms/api/[id]/analytics/route.ts +137 -0
- package/src/modules/forms/api/[id]/distributions/route.ts +192 -0
- package/src/modules/forms/api/[id]/route.ts +245 -0
- package/src/modules/forms/api/[id]/run/context/route.ts +142 -0
- package/src/modules/forms/api/[id]/run/submissions/route.ts +147 -0
- package/src/modules/forms/api/[id]/submissions/route.ts +100 -0
- package/src/modules/forms/api/[id]/theme-logo/route.ts +147 -0
- package/src/modules/forms/api/[id]/versions/[versionId]/diff/route.ts +137 -0
- package/src/modules/forms/api/[id]/versions/[versionId]/publish/route.ts +108 -0
- package/src/modules/forms/api/[id]/versions/[versionId]/route.ts +200 -0
- package/src/modules/forms/api/[id]/versions/fork/route.ts +98 -0
- package/src/modules/forms/api/attachment-helpers.ts +160 -0
- package/src/modules/forms/api/by-key/[key]/active/route.ts +165 -0
- package/src/modules/forms/api/distributions/[distributionId]/invitations/[invitationId]/route.ts +92 -0
- package/src/modules/forms/api/distributions/[distributionId]/invitations/[invitationId]/send/route.ts +96 -0
- package/src/modules/forms/api/distributions/[distributionId]/invitations/route.ts +207 -0
- package/src/modules/forms/api/distributions/[distributionId]/route.ts +213 -0
- package/src/modules/forms/api/form-submissions/[id]/attachments/route.ts +114 -0
- package/src/modules/forms/api/form-submissions/[id]/resume-token/route.ts +140 -0
- package/src/modules/forms/api/form-submissions/[id]/route.ts +176 -0
- package/src/modules/forms/api/form-submissions/[id]/submit/route.ts +91 -0
- package/src/modules/forms/api/form-submissions/by-subject/[subject_type]/[subject_id]/route.ts +93 -0
- package/src/modules/forms/api/form-submissions/route.ts +133 -0
- package/src/modules/forms/api/helpers.ts +175 -0
- package/src/modules/forms/api/public/distributions/[slug]/embed-policy/route.ts +74 -0
- package/src/modules/forms/api/public/distributions/[slug]/route.ts +101 -0
- package/src/modules/forms/api/public/embed-loader/route.ts +136 -0
- package/src/modules/forms/api/public/invitations/[token]/route.ts +107 -0
- package/src/modules/forms/api/public/rate-limit.ts +69 -0
- package/src/modules/forms/api/public/start/route.ts +195 -0
- package/src/modules/forms/api/public/submissions/[id]/attachments/[attachmentId]/route.ts +86 -0
- package/src/modules/forms/api/public/submissions/[id]/attachments/route.ts +108 -0
- package/src/modules/forms/api/public/submissions/[id]/pdf/route.ts +96 -0
- package/src/modules/forms/api/public/submissions/[id]/route.ts +189 -0
- package/src/modules/forms/api/public/submissions/[id]/submit/route.ts +159 -0
- package/src/modules/forms/api/route.ts +240 -0
- package/src/modules/forms/api/runtime-helpers.ts +208 -0
- package/src/modules/forms/api/subjects/[subjectType]/[subjectId]/consents/route.ts +178 -0
- package/src/modules/forms/api/subjects/[subjectType]/[subjectId]/export/route.ts +135 -0
- package/src/modules/forms/api/submissions/[submissionId]/access-audit/route.ts +100 -0
- package/src/modules/forms/api/submissions/[submissionId]/actors/[actorId]/route.ts +70 -0
- package/src/modules/forms/api/submissions/[submissionId]/actors/route.ts +92 -0
- package/src/modules/forms/api/submissions/[submissionId]/anonymize/route.ts +134 -0
- package/src/modules/forms/api/submissions/[submissionId]/attachments/[attachmentId]/route.ts +90 -0
- package/src/modules/forms/api/submissions/[submissionId]/export/route.ts +111 -0
- package/src/modules/forms/api/submissions/[submissionId]/pdf/route.ts +118 -0
- package/src/modules/forms/api/submissions/[submissionId]/reopen/route.ts +69 -0
- package/src/modules/forms/api/submissions/[submissionId]/revisions/route.ts +75 -0
- package/src/modules/forms/api/submissions/[submissionId]/route.ts +90 -0
- package/src/modules/forms/backend/forms/[id]/FormStudio.tsx +3269 -0
- package/src/modules/forms/backend/forms/[id]/analytics/page.meta.ts +15 -0
- package/src/modules/forms/backend/forms/[id]/analytics/page.tsx +289 -0
- package/src/modules/forms/backend/forms/[id]/distributions/page.meta.ts +15 -0
- package/src/modules/forms/backend/forms/[id]/distributions/page.tsx +16 -0
- package/src/modules/forms/backend/forms/[id]/history/page.meta.ts +11 -0
- package/src/modules/forms/backend/forms/[id]/history/page.tsx +229 -0
- package/src/modules/forms/backend/forms/[id]/page.meta.ts +11 -0
- package/src/modules/forms/backend/forms/[id]/page.tsx +17 -0
- package/src/modules/forms/backend/forms/[id]/studio/YesNoRow.tsx +65 -0
- package/src/modules/forms/backend/forms/[id]/studio/autosave-guard.ts +25 -0
- package/src/modules/forms/backend/forms/[id]/studio/canvas/DragOverlayCard.tsx +24 -0
- package/src/modules/forms/backend/forms/[id]/studio/canvas/DropIndicator.tsx +36 -0
- package/src/modules/forms/backend/forms/[id]/studio/canvas/FieldRow.tsx +308 -0
- package/src/modules/forms/backend/forms/[id]/studio/canvas/FormCanvas.tsx +265 -0
- package/src/modules/forms/backend/forms/[id]/studio/canvas/GridSlot.tsx +176 -0
- package/src/modules/forms/backend/forms/[id]/studio/canvas/SectionContainer.tsx +473 -0
- package/src/modules/forms/backend/forms/[id]/studio/canvas/grid-collision.ts +25 -0
- package/src/modules/forms/backend/forms/[id]/studio/canvas/keyboard-coordinates.ts +128 -0
- package/src/modules/forms/backend/forms/[id]/studio/canvas/overlay-size.ts +15 -0
- package/src/modules/forms/backend/forms/[id]/studio/canvas/resize-math.ts +36 -0
- package/src/modules/forms/backend/forms/[id]/studio/canvas/row-layout.ts +110 -0
- package/src/modules/forms/backend/forms/[id]/studio/logic/ConditionBuilder.tsx +264 -0
- package/src/modules/forms/backend/forms/[id]/studio/logic/HiddenFieldsPanel.tsx +121 -0
- package/src/modules/forms/backend/forms/[id]/studio/logic/JumpsEditor.tsx +237 -0
- package/src/modules/forms/backend/forms/[id]/studio/logic/RecallTokenPicker.tsx +87 -0
- package/src/modules/forms/backend/forms/[id]/studio/logic/VariablesPanel.tsx +260 -0
- package/src/modules/forms/backend/forms/[id]/studio/logic/condition-model.ts +207 -0
- package/src/modules/forms/backend/forms/[id]/studio/lucide-icons.ts +87 -0
- package/src/modules/forms/backend/forms/[id]/studio/palette/FormAppearancePanel.tsx +561 -0
- package/src/modules/forms/backend/forms/[id]/studio/palette/FormPalettePanel.tsx +52 -0
- package/src/modules/forms/backend/forms/[id]/studio/palette/InputParametersTab.tsx +378 -0
- package/src/modules/forms/backend/forms/[id]/studio/palette/PaletteCard.tsx +40 -0
- package/src/modules/forms/backend/forms/[id]/studio/palette/RolesEditor.tsx +225 -0
- package/src/modules/forms/backend/forms/[id]/studio/palette/entries.ts +102 -0
- package/src/modules/forms/backend/forms/[id]/studio/preview/PreviewSurface.tsx +1459 -0
- package/src/modules/forms/backend/forms/[id]/studio/preview/ViewportFrame.tsx +49 -0
- package/src/modules/forms/backend/forms/[id]/studio/recall.ts +0 -0
- package/src/modules/forms/backend/forms/[id]/studio/schema-helpers.ts +2525 -0
- package/src/modules/forms/backend/forms/[id]/studio/style/BackgroundControl.tsx +137 -0
- package/src/modules/forms/backend/forms/[id]/studio/style/ColorControl.tsx +257 -0
- package/src/modules/forms/backend/forms/[id]/studio/style/contrast.ts +114 -0
- package/src/modules/forms/backend/forms/[id]/studio/style/presets.ts +104 -0
- package/src/modules/forms/backend/forms/[id]/studio/type-label.ts +13 -0
- package/src/modules/forms/backend/forms/[id]/studio/types.ts +31 -0
- package/src/modules/forms/backend/forms/[id]/studio/undo-controller.ts +52 -0
- package/src/modules/forms/backend/forms/[id]/studio/validation/FileConstraintsEditor.tsx +89 -0
- package/src/modules/forms/backend/forms/[id]/studio/validation/GroupConfigEditor.tsx +167 -0
- package/src/modules/forms/backend/forms/[id]/studio/validation/LengthRangeEditor.tsx +67 -0
- package/src/modules/forms/backend/forms/[id]/studio/validation/MatrixColumnsEditor.tsx +166 -0
- package/src/modules/forms/backend/forms/[id]/studio/validation/MatrixRowsEditor.tsx +184 -0
- package/src/modules/forms/backend/forms/[id]/studio/validation/MessageOverridesEditor.tsx +83 -0
- package/src/modules/forms/backend/forms/[id]/studio/validation/NpsAnchorsEditor.tsx +60 -0
- package/src/modules/forms/backend/forms/[id]/studio/validation/NumberRangeEditor.tsx +65 -0
- package/src/modules/forms/backend/forms/[id]/studio/validation/OpinionIconSelect.tsx +46 -0
- package/src/modules/forms/backend/forms/[id]/studio/validation/PatternEditor.tsx +122 -0
- package/src/modules/forms/backend/forms/[id]/studio/validation/RankingExhaustiveSwitch.tsx +28 -0
- package/src/modules/forms/backend/forms/[id]/studio/validation/SignatureConfigEditor.tsx +84 -0
- package/src/modules/forms/backend/forms/[id]/studio/validation/ValidationPanel.tsx +369 -0
- package/src/modules/forms/backend/forms/[id]/submissions/components/RowBadges.ts +56 -0
- package/src/modules/forms/backend/forms/[id]/submissions/components/SubmissionDrawer.tsx +605 -0
- package/src/modules/forms/backend/forms/[id]/submissions/components/formSummary.ts +19 -0
- package/src/modules/forms/backend/forms/[id]/submissions/page.meta.ts +10 -0
- package/src/modules/forms/backend/forms/[id]/submissions/page.tsx +252 -0
- package/src/modules/forms/backend/forms/create/page.meta.ts +11 -0
- package/src/modules/forms/backend/forms/create/page.tsx +113 -0
- package/src/modules/forms/backend/forms/page.meta.ts +31 -0
- package/src/modules/forms/backend/forms/page.tsx +269 -0
- package/src/modules/forms/commands/distribution.ts +506 -0
- package/src/modules/forms/commands/form-version.ts +811 -0
- package/src/modules/forms/commands/form.ts +477 -0
- package/src/modules/forms/commands/index.ts +18 -0
- package/src/modules/forms/commands/invitation.ts +428 -0
- package/src/modules/forms/commands/shared.ts +261 -0
- package/src/modules/forms/commands/submission.ts +216 -0
- package/src/modules/forms/data/entities.ts +686 -0
- package/src/modules/forms/data/validators.ts +483 -0
- package/src/modules/forms/di.ts +246 -0
- package/src/modules/forms/encryption.ts +31 -0
- package/src/modules/forms/events-payloads.ts +103 -0
- package/src/modules/forms/events.ts +50 -0
- package/src/modules/forms/extension-points.ts +41 -0
- package/src/modules/forms/frontend/[orgSlug]/portal/forms/[key]/page.meta.ts +9 -0
- package/src/modules/forms/frontend/[orgSlug]/portal/forms/[key]/page.tsx +45 -0
- package/src/modules/forms/frontend/[orgSlug]/portal/submissions/[id]/continue/page.meta.ts +9 -0
- package/src/modules/forms/frontend/[orgSlug]/portal/submissions/[id]/continue/page.tsx +56 -0
- package/src/modules/forms/frontend/embed/[slug]/page.meta.ts +42 -0
- package/src/modules/forms/frontend/embed/[slug]/page.tsx +171 -0
- package/src/modules/forms/frontend/f/[slug]/page.meta.ts +9 -0
- package/src/modules/forms/frontend/f/[slug]/page.tsx +10 -0
- package/src/modules/forms/frontend/forms/[id]/run/page.meta.ts +17 -0
- package/src/modules/forms/frontend/forms/[id]/run/page.tsx +83 -0
- package/src/modules/forms/frontend/i/[token]/page.meta.ts +9 -0
- package/src/modules/forms/frontend/i/[token]/page.tsx +10 -0
- package/src/modules/forms/i18n/de.json +821 -0
- package/src/modules/forms/i18n/en.json +821 -0
- package/src/modules/forms/i18n/es.json +821 -0
- package/src/modules/forms/i18n/ko.json +821 -0
- package/src/modules/forms/i18n/pl.json +821 -0
- package/src/modules/forms/index.ts +18 -0
- package/src/modules/forms/lib/embed-frame-policy.ts +144 -0
- package/src/modules/forms/lib/form-definition-slicing.ts +54 -0
- package/src/modules/forms/lib/log-redaction.ts +91 -0
- package/src/modules/forms/lib/navigable-url.ts +57 -0
- package/src/modules/forms/lib/retention.ts +73 -0
- package/src/modules/forms/lib/runtime-principal.ts +101 -0
- package/src/modules/forms/migrations/.snapshot-open-mercato.json +3185 -0
- package/src/modules/forms/migrations/Migration20260508135459_forms.ts +17 -0
- package/src/modules/forms/migrations/Migration20260508140932_forms.ts +26 -0
- package/src/modules/forms/migrations/Migration20260508155737_forms.ts +15 -0
- package/src/modules/forms/migrations/Migration20260520120000_forms.ts +26 -0
- package/src/modules/forms/migrations/Migration20260521090000_forms.ts +15 -0
- package/src/modules/forms/migrations/Migration20260521120000_forms.ts +21 -0
- package/src/modules/forms/runner/FormRunner.tsx +924 -0
- package/src/modules/forms/runner/MatrixField.tsx +210 -0
- package/src/modules/forms/runner/RankingField.tsx +207 -0
- package/src/modules/forms/runner/ScaleField.tsx +102 -0
- package/src/modules/forms/runner/SignatureField.tsx +158 -0
- package/src/modules/forms/runner/tamper-check.ts +131 -0
- package/src/modules/forms/schema/address-countries.ts +60 -0
- package/src/modules/forms/schema/field-type-patterns.ts +21 -0
- package/src/modules/forms/schema/field-type-registry-advanced.ts +58 -0
- package/src/modules/forms/schema/field-type-registry.ts +819 -0
- package/src/modules/forms/schema/file-field.ts +102 -0
- package/src/modules/forms/schema/group-field.ts +235 -0
- package/src/modules/forms/schema/jsonlogic-grammar.ts +95 -0
- package/src/modules/forms/schema/jsonschema-extensions.ts +1060 -0
- package/src/modules/forms/schema/layout-catalog.ts +68 -0
- package/src/modules/forms/schema/signature-field.ts +120 -0
- package/src/modules/forms/schema/style-extensions.ts +194 -0
- package/src/modules/forms/schema/style-validators.ts +252 -0
- package/src/modules/forms/services/access-audit-logger.ts +275 -0
- package/src/modules/forms/services/analytics-service.ts +498 -0
- package/src/modules/forms/services/anonymize-service.ts +177 -0
- package/src/modules/forms/services/attachment-service.ts +223 -0
- package/src/modules/forms/services/captcha-verifier.ts +156 -0
- package/src/modules/forms/services/consent-record-service.ts +162 -0
- package/src/modules/forms/services/distribution-service.ts +509 -0
- package/src/modules/forms/services/distribution-token.ts +153 -0
- package/src/modules/forms/services/encryption-service.ts +500 -0
- package/src/modules/forms/services/export-service.ts +360 -0
- package/src/modules/forms/services/field-validation-service.ts +300 -0
- package/src/modules/forms/services/form-logic-evaluator.ts +718 -0
- package/src/modules/forms/services/form-version-compiler.ts +829 -0
- package/src/modules/forms/services/form-version-differ.ts +143 -0
- package/src/modules/forms/services/jsonlogic-evaluator.ts +228 -0
- package/src/modules/forms/services/pdf-snapshot-service.ts +782 -0
- package/src/modules/forms/services/prefill-resolver.ts +125 -0
- package/src/modules/forms/services/role-policy-service.ts +90 -0
- package/src/modules/forms/services/style-compiler.ts +378 -0
- package/src/modules/forms/services/submission-service.ts +1259 -0
- package/src/modules/forms/services/upload-scanner.ts +36 -0
- package/src/modules/forms/services/upload-validation.ts +94 -0
- package/src/modules/forms/services/visibility-resolver.ts +63 -0
- package/src/modules/forms/setup.ts +16 -0
- package/src/modules/forms/subscribers/forms-consent-projector.ts +100 -0
- package/src/modules/forms/subscribers/forms-pdf-snapshot.ts +112 -0
- package/src/modules/forms/subscribers/forms-webhook-bridge-anonymized.ts +36 -0
- package/src/modules/forms/subscribers/forms-webhook-bridge.ts +56 -0
- package/src/modules/forms/subscribers/invitation-email.ts +180 -0
- package/src/modules/forms/translations.ts +14 -0
- package/src/modules/forms/ui/admin/forms/[id]/distributions/CreateDistributionDialog.tsx +401 -0
- package/src/modules/forms/ui/admin/forms/[id]/distributions/DistributionsPanel.tsx +381 -0
- package/src/modules/forms/ui/admin/forms/[id]/distributions/EmbedSettingsDialog.tsx +332 -0
- package/src/modules/forms/ui/admin/forms/[id]/distributions/RecipientsTable.tsx +460 -0
- package/src/modules/forms/ui/public/EmbeddedForm.tsx +248 -0
- package/src/modules/forms/ui/public/FormRunner.tsx +491 -0
- package/src/modules/forms/ui/public/FormTrigger.tsx +80 -0
- package/src/modules/forms/ui/public/PublicFormRunnerPage.tsx +34 -0
- package/src/modules/forms/ui/public/components/CompletionScreen.tsx +97 -0
- package/src/modules/forms/ui/public/components/LocaleSwitch.tsx +42 -0
- package/src/modules/forms/ui/public/components/ResumeGate.tsx +61 -0
- package/src/modules/forms/ui/public/components/ReviewStep.tsx +153 -0
- package/src/modules/forms/ui/public/components/SaveIndicator.tsx +69 -0
- package/src/modules/forms/ui/public/components/SectionStepper.tsx +60 -0
- package/src/modules/forms/ui/public/index.ts +48 -0
- package/src/modules/forms/ui/public/renderers/index.tsx +889 -0
- package/src/modules/forms/ui/public/state/logic-derivation.ts +91 -0
- package/src/modules/forms/ui/public/state/runtime-client.ts +495 -0
- package/src/modules/forms/ui/public/state/useAutosave.ts +97 -0
- package/src/modules/forms/ui/public/state/useFormRunner.ts +547 -0
- package/src/modules/forms/ui/public/style/LogoHeader.tsx +43 -0
- package/src/modules/forms/ui/public/style/applyStyle.ts +26 -0
- package/src/modules/forms/ui/public/types.ts +228 -0
- package/src/modules/forms/widgets/injection/access-audit-panel/widget.tsx +144 -0
- package/src/modules/forms/widgets/injection/anonymize-button/widget.tsx +177 -0
- package/src/modules/forms/widgets/injection/audit-format.ts +60 -0
- package/src/modules/forms/widgets/injection/context.ts +24 -0
- package/src/modules/forms/widgets/injection/embedded-form/widget.tsx +102 -0
- package/src/modules/forms/widgets/injection/pdf-download-button/widget.tsx +111 -0
- package/src/modules/forms/widgets/injection/self-audit-footer/widget.tsx +56 -0
- package/src/modules/forms/widgets/injection-table.ts +54 -0
- package/src/modules/forms/workers/pdf-snapshot.ts +110 -0
- package/src/modules/forms/workers/retention-purge.ts +169 -0
- package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.ts +7 -1
- package/src/modules/inbox_ops/data/validators.ts +2 -1
- package/src/modules/inbox_ops/lib/llmProvider.ts +11 -0
- package/src/modules/inbox_ops/widgets/notifications/ProposalCreatedRenderer.tsx +2 -2
- package/src/modules/integrations/di.ts +5 -1
- package/src/modules/integrations/lib/credentials-service.ts +45 -36
- package/src/modules/messages/api/[id]/attachments/route.ts +12 -3
- package/src/modules/messages/api/[id]/forward-preview/route.ts +9 -3
- package/src/modules/messages/api/[id]/route.ts +83 -4
- package/src/modules/messages/api/route.ts +123 -2
- package/src/modules/messages/commands/messages.ts +81 -7
- package/src/modules/messages/components/ComposeMessagePageClient.tsx +29 -1
- package/src/modules/messages/data/validators.ts +10 -0
- package/src/modules/messages/i18n/de.json +8 -0
- package/src/modules/messages/i18n/en.json +8 -0
- package/src/modules/messages/i18n/es.json +8 -0
- package/src/modules/messages/i18n/ko.json +8 -0
- package/src/modules/messages/i18n/pl.json +8 -0
- package/src/modules/messages/lib/channelThreadAccess.ts +101 -0
- package/src/modules/messages/lib/composeSenderDelegation.ts +182 -0
- package/src/modules/messages/lib/composeSenderOptions.ts +56 -0
- package/src/modules/messages/lib/email-sender.ts +1 -17
- package/src/modules/messages/lib/forwarding.ts +17 -2
- package/src/modules/messages/lib/markdownEmailBody.ts +25 -0
- package/src/modules/messages/lib/messageSourceEntityTypes.ts +12 -0
- package/src/modules/messages/lib/routeHelpers.ts +92 -0
- package/src/modules/messages/subscribers/message-notification.ts +47 -1
- package/src/modules/notifications/AGENTS.md +1 -1
- package/src/modules/notifications/api/[id]/action/route.ts +2 -1
- package/src/modules/notifications/api/openapi.ts +1 -0
- package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +77 -48
- package/src/modules/notifications/i18n/de.json +1 -0
- package/src/modules/notifications/i18n/en.json +1 -0
- package/src/modules/notifications/i18n/es.json +1 -0
- package/src/modules/notifications/i18n/ko.json +1 -0
- package/src/modules/notifications/i18n/pl.json +1 -0
- package/src/modules/notifications/lib/deliveryConfig.ts +31 -2
- package/src/modules/notifications/lib/notificationMapper.ts +2 -0
- package/src/modules/notifications/lib/safeHref.ts +7 -0
- package/src/modules/planner/api/availability-date-specific.ts +2 -1
- package/src/modules/planner/api/availability-weekly.ts +2 -1
- package/src/modules/progress/AGENTS.md +2 -0
- package/src/modules/progress/api/active/route.ts +14 -2
- package/src/modules/progress/api/jobs/[id]/route.ts +44 -27
- package/src/modules/progress/api/jobs/route.ts +22 -8
- package/src/modules/progress/api/requestScope.ts +43 -0
- package/src/modules/progress/lib/organizationScope.ts +17 -0
- package/src/modules/progress/lib/progressService.ts +3 -2
- package/src/modules/progress/lib/progressServiceImpl.ts +63 -14
- package/src/modules/push_notifications/api/custom-send/route.ts +4 -1
- package/src/modules/push_notifications/i18n/de.json +1 -0
- package/src/modules/push_notifications/i18n/en.json +1 -0
- package/src/modules/push_notifications/i18n/es.json +1 -0
- package/src/modules/push_notifications/i18n/ko.json +1 -0
- package/src/modules/push_notifications/i18n/pl.json +1 -0
- package/src/modules/query_index/api/status.ts +4 -9
- package/src/modules/query_index/di.ts +2 -1
- package/src/modules/query_index/lib/engine.ts +2 -0
- package/src/modules/query_index/lib/request-encryption.ts +31 -0
- package/src/modules/query_index/lib/search-tokens.ts +42 -1
- package/src/modules/resources/api/resources/tags/assign/route.ts +2 -1
- package/src/modules/resources/api/resources/tags/unassign/route.ts +2 -1
- package/src/modules/resources/commands/activities.ts +2 -2
- package/src/modules/sales/api/document-history/route.ts +5 -1
- package/src/modules/sales/api/document-numbers/route.ts +2 -1
- package/src/modules/sales/api/quotes/convert/route.ts +2 -1
- package/src/modules/sales/api/quotes/send/route.ts +2 -1
- package/src/modules/sales/api/settings/document-numbers/route.ts +7 -1
- package/src/modules/sales/api/settings/order-editing/route.ts +7 -1
- package/src/modules/sales/commands/documents.ts +451 -131
- package/src/modules/sales/commands/payments.ts +2 -1
- package/src/modules/sales/commands/settings.ts +34 -6
- package/src/modules/sales/commands/shipments.ts +2 -1
- package/src/modules/sales/components/DocumentNumberSettings.tsx +41 -28
- package/src/modules/sales/components/documents/ItemsSection.tsx +58 -13
- package/src/modules/sales/i18n/de.json +2 -0
- package/src/modules/sales/i18n/en.json +2 -0
- package/src/modules/sales/i18n/es.json +2 -0
- package/src/modules/sales/i18n/ko.json +2 -0
- package/src/modules/sales/i18n/pl.json +2 -0
- package/src/modules/sales/lib/lineSnapshots.ts +36 -0
- package/src/modules/sales/setup.ts +4 -4
- package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +2 -2
- package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +2 -2
- package/src/modules/staff/api/leave-requests/accept/route.ts +2 -1
- package/src/modules/staff/api/leave-requests/reject/route.ts +2 -1
- package/src/modules/staff/api/leave-requests.ts +2 -1
- package/src/modules/staff/api/team-members/self/route.ts +2 -1
- package/src/modules/staff/api/team-members/tags/assign/route.ts +2 -1
- package/src/modules/staff/api/team-members/tags/unassign/route.ts +2 -1
- package/src/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.ts +16 -16
- package/src/modules/staff/api/timesheets/my-projects/[projectId]/route.ts +2 -1
- package/src/modules/staff/api/timesheets/my-projects/route.ts +2 -1
- package/src/modules/staff/api/timesheets/my-work/route.ts +2 -1
- package/src/modules/staff/api/timesheets/projects/kpis/route.ts +2 -1
- package/src/modules/staff/api/timesheets/reports/[id]/close/route.ts +2 -1
- package/src/modules/staff/api/timesheets/reports/[id]/export/route.ts +3 -2
- package/src/modules/staff/api/timesheets/reports/[id]/sheet/route.ts +3 -2
- package/src/modules/staff/api/timesheets/reports/[id]/unlock/route.ts +3 -2
- package/src/modules/staff/api/timesheets/reports/preview/route.ts +2 -1
- package/src/modules/staff/api/timesheets/reports/shared.ts +2 -1
- package/src/modules/staff/api/timesheets/settings/reapply-rounding/route.ts +2 -1
- package/src/modules/staff/api/timesheets/tags/entry-assignments/route.ts +2 -1
- package/src/modules/staff/api/timesheets/tags/task-assignments/route.ts +2 -1
- package/src/modules/staff/api/timesheets/tasks/[id]/comments/route.ts +2 -1
- package/src/modules/staff/api/timesheets/tasks/[id]/status/route.ts +2 -1
- package/src/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.ts +3 -2
- package/src/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.ts +2 -1
- package/src/modules/staff/api/timesheets/time-entries/[id]/segments/route.ts +2 -1
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +2 -1
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.ts +2 -1
- package/src/modules/staff/api/timesheets/time-entries/bulk/route.ts +2 -1
- package/src/modules/staff/api/timesheets/time-entries/copy-day/route.ts +3 -2
- package/src/modules/staff/api/timesheets/time-entries/overlaps/route.ts +2 -1
- package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +3 -2
- package/src/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.ts +3 -2
- package/src/modules/staff/api/timesheets/time-projects/route.ts +2 -1
- package/src/modules/staff/commands/activities.ts +2 -2
- package/src/modules/staff/commands/timesheets-entries.ts +3 -2
- package/src/modules/staff/commands/timesheets-reports.ts +88 -15
- package/src/modules/staff/lib/timesheets-reports/buildReportSheet.ts +1 -0
- package/src/modules/staff/lib/timesheets-reports/loadReportData.ts +69 -8
- package/src/modules/staff/lib/timesheets-reports/reportPeriod.ts +18 -0
- package/src/modules/staff/lib/timesheets-ui/CalendarPicker.tsx +21 -10
- package/src/modules/sync_excel/api/import/route.ts +40 -12
- package/src/modules/sync_excel/api/upload/route.ts +34 -4
- package/src/modules/translations/api/context.ts +2 -1
- package/src/modules/warranty_claims/api/ai/assess/route.ts +2 -1
- package/src/modules/warranty_claims/api/ai/draft-reply/route.ts +2 -1
- package/src/modules/warranty_claims/api/ai/suggest/route.ts +2 -1
- package/src/modules/warranty_claims/api/assign/route.ts +2 -1
- package/src/modules/warranty_claims/api/credit-memo/route.ts +2 -1
- package/src/modules/warranty_claims/api/entitlement/route.ts +2 -1
- package/src/modules/warranty_claims/api/events/route.ts +2 -1
- package/src/modules/warranty_claims/api/external/claims/route.ts +2 -1
- package/src/modules/warranty_claims/api/receiving/route.ts +2 -1
- package/src/modules/warranty_claims/api/replacement-order/route.ts +2 -1
- package/src/modules/warranty_claims/api/return-label/route.ts +2 -1
- package/src/modules/warranty_claims/api/risk/route.ts +2 -1
- package/src/modules/warranty_claims/api/sales-return/route.ts +2 -1
- package/src/modules/warranty_claims/api/settings-general/route.ts +2 -1
- package/src/modules/warranty_claims/api/stats/route.ts +11 -11
- package/src/modules/warranty_claims/api/submit/route.ts +2 -1
- package/src/modules/warranty_claims/api/transition/route.ts +2 -1
- package/src/modules/warranty_claims/api/vendor-recovery/route.ts +2 -1
- package/src/modules/warranty_claims/api/vendor-recovery-suggestions/route.ts +2 -1
- package/src/modules/warranty_claims/i18n/de.json +1 -0
- package/src/modules/warranty_claims/i18n/en.json +1 -0
- package/src/modules/warranty_claims/i18n/es.json +1 -0
- package/src/modules/warranty_claims/i18n/ko.json +1 -0
- package/src/modules/warranty_claims/i18n/pl.json +1 -0
- package/src/modules/warranty_claims/widgets/notifications/WarrantyClaimNotificationRenderer.tsx +1 -1
- package/src/modules/wms/api/dashboard/operational/route.ts +2 -1
- package/src/modules/wms/api/inventory/cycle-count/route.ts +1 -0
- package/src/modules/wms/api/inventory/helpers.ts +2 -1
- package/src/modules/wms/api/inventory/import/helpers.ts +2 -1
- package/src/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.ts +2 -1
- package/src/modules/wms/commands/inventory-actions.ts +45 -1
- package/src/modules/wms/components/backend/CycleCountWizardDialog.tsx +14 -1
- package/src/modules/wms/di.ts +6 -0
- package/src/modules/wms/i18n/de.json +2 -0
- package/src/modules/wms/i18n/en.json +2 -0
- package/src/modules/wms/i18n/es.json +2 -0
- package/src/modules/wms/i18n/ko.json +2 -0
- package/src/modules/wms/i18n/pl.json +2 -0
- package/src/modules/wms/lib/availabilityCache.ts +137 -0
- package/src/modules/wms/lib/availabilityCalculation.ts +351 -0
- package/src/modules/wms/lib/availabilityProvider.ts +47 -0
- package/src/modules/wms/lib/loadOperationalDashboard.ts +51 -3
- package/src/modules/wms/lib/lowStockBalanceFilter.ts +23 -10
- package/src/modules/wms/lib/sqlInClause.ts +21 -0
- package/src/modules/wms/lib/tryResolve.ts +8 -0
- package/src/modules/wms/subscribers/invalidate-availability-cache-policy.ts +18 -0
- package/src/modules/wms/subscribers/invalidate-availability-cache-profile.ts +18 -0
- package/src/modules/wms/subscribers/invalidate-availability-cache.ts +30 -0
- package/src/modules/wms/widgets/notifications/WmsLowStockRenderer.tsx +2 -2
- package/src/modules/wms/widgets/notifications/WmsReservationShortfallRenderer.tsx +4 -3
- package/src/modules/workflows/AGENTS.md +3 -1
- package/src/modules/workflows/api/commands/route.ts +4 -1
- package/src/modules/workflows/api/definitions/[id]/callers/route.ts +4 -1
- package/src/modules/workflows/api/definitions/[id]/context-schema/route.ts +4 -2
- package/src/modules/workflows/api/definitions/[id]/customize/route.ts +4 -1
- package/src/modules/workflows/api/definitions/[id]/draft/route.ts +6 -1
- package/src/modules/workflows/api/definitions/[id]/publish/route.ts +10 -1
- package/src/modules/workflows/api/definitions/[id]/reset-to-code/route.ts +4 -1
- package/src/modules/workflows/api/definitions/[id]/route.ts +5 -3
- package/src/modules/workflows/api/definitions/[id]/test-step/route.ts +4 -2
- package/src/modules/workflows/api/definitions/generate/route.ts +4 -1
- package/src/modules/workflows/api/definitions/route.ts +4 -2
- package/src/modules/workflows/api/endpoints/route.ts +4 -1
- package/src/modules/workflows/api/functions/route.ts +4 -1
- package/src/modules/workflows/api/grantable-features/route.ts +2 -1
- package/src/modules/workflows/api/instances/[id]/advance/route.ts +4 -1
- package/src/modules/workflows/api/instances/[id]/cancel/route.ts +4 -1
- package/src/modules/workflows/api/instances/[id]/context/route.ts +2 -1
- package/src/modules/workflows/api/instances/[id]/rerun-step/route.ts +4 -1
- package/src/modules/workflows/api/instances/[id]/retry/route.ts +4 -1
- package/src/modules/workflows/api/instances/[id]/signal/route.ts +4 -1
- package/src/modules/workflows/api/instances/[id]/step-through/route.ts +4 -2
- package/src/modules/workflows/api/instances/route.ts +4 -2
- package/src/modules/workflows/api/instances/validate-start/route.ts +4 -1
- package/src/modules/workflows/api/rule-usage/route.ts +4 -1
- package/src/modules/workflows/api/signals/route.ts +4 -1
- package/src/modules/workflows/api/tasks/[id]/claim/route.ts +4 -1
- package/src/modules/workflows/api/tasks/[id]/complete/route.ts +4 -1
- package/src/modules/workflows/api/tasks/[id]/reassign/route.ts +2 -1
- package/src/modules/workflows/api/tasks/[id]/unclaim/route.ts +4 -1
- package/src/modules/workflows/api/templates/route.ts +4 -1
- package/src/modules/workflows/api/work-inbox/next/route.ts +4 -1
- package/src/modules/workflows/lib/event-trigger-service.ts +18 -2
- package/src/modules/workflows/lib/step-handler.ts +13 -8
- package/src/test-utils/transactionalEntityManagerDouble.ts +51 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/staff/api/timesheets/time-entries/copy-day/route.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * US-D6 \u2014 \"Kopiuj wczorajszy dzie\u0144\" (screen 1, note 5).\n *\n * The note is the design constraint: the button \"duplicates the previous working\n * day's entries **as versions to correct**\". So this endpoint is not a silent\n * commit \u2014 it answers with the entries it created, in full, so the caller can put\n * them straight in front of the user for review and adjustment rather than\n * reporting a count and leaving them to hunt for what landed.\n *\n * What \"the previous working day\" means is the CALLER's business. Weekends,\n * holidays and four-day weeks are calendar questions this module has no data for,\n * and a server-side weekend rule would be wrong for every tenant that does not\n * work Monday-to-Friday. The endpoint therefore takes two explicit days.\n *\n * Three decisions worth their reasoning:\n *\n * 1. **A locked SOURCE is skipped and reported, not fatal.** The lock protects\n * the numbers a closed report has already billed (D-7), and a copy never\n * writes to its source \u2014 the target day is a fresh, unlocked draft. This is\n * what makes it different from the grid bulk save, where the rows being\n * WRITTEN are the locked ones and the whole batch must fail. Here the caller\n * did not hand-pick rows: they asked for a day. Failing the whole day because\n * one of its entries sits in a closed report would break the button for\n * exactly the people who report most regularly. Every skipped source comes\n * back in `skipped[]` carrying the shared `time_entry_locked` code and the\n * report that froze it, so the UI can say what was left behind.\n * 2. **Idempotency: a non-empty target day is refused.** A day of entries has no\n * natural key to deduplicate on \u2014 two identical 30-minute stand-ups on one day\n * are legitimate data \u2014 so a second call cannot tell a re-run from a genuine\n * repeat, and would silently double the day into something the user cannot\n * untangle from the grid. So the default is to refuse with `409\n * copy_day_target_not_empty`, naming what is already there. `allowDuplicates:\n * true` is the deliberate opt-in for the real case the refusal would\n * otherwise block: the user already logged something today and still wants\n * yesterday's set on top. Refusing is also what makes a partially completed\n * run visible instead of compounding it.\n * 3. **The copy itself is the existing duplicate command, once per source.** It\n * already resolves the source, runs the lock gate and delegates the write to\n * the create command, so every copy inherits ownership, project access, the\n * billable and currency defaults, rounding (D-7), tags and audit unchanged.\n * The loop is NOT wrapped in a route-level transaction: the command bus forks\n * its own EntityManager per command, so an outer `em.transactional` would\n * enclose nothing and the atomicity would be a fiction. Each copy is atomic in\n * itself; a copy that fails is reported in `skipped[]` and the rest still\n * land, which is the behaviour a day of drafts wants.\n */\n\nimport { NextResponse } from 'next/server'\nimport { resolveFeatureAccess } from '../../../../lib/time-tracking/featureAccess'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError, forbidden, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { runRouteMutationGuards } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { emitStaffEvent } from '../../../../events'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { StaffTimeEntry, StaffTimeEntryTag } from '../../../../data/entities'\nimport { staffTimeEntryCommandIds, TIME_ENTRY_LOCKED_CODE } from '../../../../commands/timesheets-entries'\nimport { resolveProjectAccess, type ProjectAccess } from '../../../../lib/time-tracking/access'\nimport { readTimeTrackingSettings } from '../../../../lib/time-tracking/settings'\nimport { STAFF_TIME_TRACKING_RESOURCE_KINDS } from '../../../guards'\nimport { runTimesheetInterceptors } from '../../_shared/withTimesheetInterceptors'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/time-entries/copy-day' })\n\nconst MANAGE_OWN_FEATURE = 'staff.timesheets.manage_own'\nconst MANAGE_ALL_FEATURE = 'staff.timesheets.manage_all'\nconst RESOURCE_KIND = STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry\n\n/** The 409 a second \"copy yesterday\" click gets, rather than a doubled day. */\nexport const COPY_DAY_TARGET_NOT_EMPTY_CODE = 'copy_day_target_not_empty'\n\n/** Reason codes on `skipped[]`. The locked one is the shared code, not a second spelling of it. */\nconst COPY_FAILED_REASON = 'copy_failed'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: [MANAGE_OWN_FEATURE] },\n}\n\nconst copyDayRequestSchema = z.object({\n fromDate: z.coerce.date(),\n toDate: z.coerce.date(),\n /** Only a `staff.timesheets.manage_all` holder may copy somebody else's day. */\n staffMemberId: z.string().uuid().optional(),\n allowDuplicates: z.boolean().optional().default(false),\n})\n\nconst copiedEntrySchema = z.object({\n id: z.string().uuid(),\n sourceId: z.string().uuid(),\n date: z.string(),\n durationMinutes: z.number(),\n roundedMinutes: z.number().nullable(),\n timeProjectId: z.string().uuid().nullable(),\n taskId: z.string().uuid().nullable(),\n isBillable: z.boolean(),\n description: z.string().nullable(),\n tagIds: z.array(z.string().uuid()),\n})\n\nconst skippedEntrySchema = z.object({\n id: z.string().uuid(),\n reason: z.string(),\n lockedReportId: z.string().uuid().nullable(),\n error: z.string().nullable(),\n})\n\nconst copyDayResponseSchema = z.object({\n ok: z.literal(true),\n fromDate: z.string(),\n toDate: z.string(),\n staffMemberId: z.string().uuid(),\n copied: z.number(),\n created: z.array(copiedEntrySchema),\n skipped: z.array(skippedEntrySchema),\n})\n\nconst targetNotEmptySchema = z.object({\n code: z.literal(COPY_DAY_TARGET_NOT_EMPTY_CODE),\n error: z.string(),\n toDate: z.string(),\n existingEntryCount: z.number(),\n existingEntryIds: z.array(z.string().uuid()),\n})\n\ntype ContainerLike = { resolve: (name: string) => unknown }\n\ntype SkippedSource = {\n id: string\n reason: string\n lockedReportId: string | null\n error: string | null\n}\n\n/**\n * A day is addressed by its calendar day, never by a timestamp (D-8), so both the\n * request echo and the copied rows collapse to `YYYY-MM-DD`.\n */\nfunction dayKey(value: Date | string | null | undefined): string {\n if (!value) return ''\n const date = value instanceof Date ? value : new Date(value)\n return Number.isNaN(date.getTime()) ? String(value) : date.toISOString().slice(0, 10)\n}\n\nasync function resolveAssignmentGraceDays(container: ContainerLike, tenantId: string): Promise<number | null> {\n try {\n const configService = container.resolve('moduleConfigService') as ModuleConfigService\n const settings = await readTimeTrackingSettings(configService, { tenantId })\n return settings.access.assignmentGraceDays\n } catch {\n // Fail safe to the documented default rather than widening the window.\n return null\n }\n}\n\nasync function loadProjectAccess(\n container: ContainerLike,\n userId: string | null,\n tenantId: string,\n organizationId: string,\n grantedFeatures: readonly string[],\n): Promise<ProjectAccess> {\n const em = container.resolve('em') as EntityManager\n return resolveProjectAccess({\n em: em.fork(),\n userId,\n tenantId,\n organizationId,\n userFeatures: grantedFeatures,\n assignmentGraceDays: await resolveAssignmentGraceDays(container, tenantId),\n })\n}\n\n/**\n * The per-row form of the list route's `$or` intersection: an entry on a project\n * is visible to the members of that project, and a project-less entry is visible\n * only to the member who logged it. A manager copying somebody else's day still\n * only copies the part of it their own project access covers.\n */\nfunction isTimeEntryVisible(\n entry: { staffMemberId: string; timeProjectId?: string | null },\n access: ProjectAccess,\n): boolean {\n if (access.canManageAll) return true\n const projectId = entry.timeProjectId ?? null\n if (projectId) return access.projectIds.includes(projectId)\n return access.staffMemberId !== null && access.staffMemberId === entry.staffMemberId\n}\n\nasync function loadTagIdsByEntry(\n em: EntityManager,\n entryIds: string[],\n tenantId: string,\n organizationId: string,\n): Promise<Map<string, string[]>> {\n const byEntryId = new Map<string, string[]>()\n if (entryIds.length === 0) return byEntryId\n const assignments = await em.find(StaffTimeEntryTag, {\n timeEntryId: { $in: entryIds },\n tenantId,\n organizationId,\n })\n for (const assignment of assignments) {\n const list = byEntryId.get(assignment.timeEntryId) ?? []\n list.push(assignment.tagId)\n byEntryId.set(assignment.timeEntryId, list)\n }\n return byEntryId\n}\n\nexport async function POST(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) {\n throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, {\n error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.'),\n })\n }\n\n const actorId = auth.sub ?? ''\n // Both decisions come from the module's single RBAC authority, which asks the\n // service and fails closed on every path. The nullable grant array they were\n // matched against could not say whether an empty answer meant \"no grants\" or\n // \"could not ask\", and `grantedFeatures ? \u2026 : false` silently answered the\n // second as the first.\n const manageOwn = await resolveFeatureAccess(container, actorId, [MANAGE_OWN_FEATURE], { tenantId, organizationId })\n if (!manageOwn.allowed) {\n throw forbidden(translate('staff.errors.forbidden', 'Forbidden'))\n }\n const grantedFeatures = manageOwn.grantedFeatures\n const hasManageAll = (\n await resolveFeatureAccess(container, actorId, [MANAGE_ALL_FEATURE], { tenantId, organizationId })\n ).allowed\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'POST',\n scope: { container, userId: actorId, tenantId, organizationId, userFeatures: grantedFeatures },\n body: await readJsonSafe<Record<string, unknown>>(req, {}),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const parsed = copyDayRequestSchema.parse(session.body)\n const fromDay = dayKey(parsed.fromDate)\n const toDay = dayKey(parsed.toDate)\n if (fromDay === toDay) {\n // Copying a day onto itself is always a mistake, never intent, and\n // `allowDuplicates` would turn it into a silent doubling.\n throw new CrudHttpError(400, {\n error: translate(\n 'staff.timesheets.errors.copyDaySameDate',\n 'The source and target day must be different.',\n ),\n })\n }\n\n const access = await loadProjectAccess(container, actorId || null, tenantId, organizationId, grantedFeatures)\n // Ownership: the copies are attributed by the create command, which silently\n // files them under the caller unless they hold `staff.timesheets.manage_all`.\n // Refusing here rather than letting that happen keeps a projects-manager from\n // asking for a colleague's day and quietly receiving it under their own name.\n if (parsed.staffMemberId && parsed.staffMemberId !== access.staffMemberId && !hasManageAll) {\n throw forbidden(\n translate('staff.timesheets.errors.notOwner', 'You can only manage your own time entries.'),\n )\n }\n const staffMemberId = parsed.staffMemberId ?? access.staffMemberId\n if (!staffMemberId) {\n throw new CrudHttpError(403, {\n error: translate('staff.timesheets.errors.noStaffMember', 'No staff member linked to your account.'),\n })\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const scopeCtx = { tenantId, organizationId }\n\n const sources = (\n await findWithDecryption(\n em,\n StaffTimeEntry,\n { tenantId, organizationId, staffMemberId, date: parsed.fromDate, deletedAt: null },\n {},\n scopeCtx,\n )\n ).filter((entry) => isTimeEntryVisible(entry, access))\n\n if (sources.length === 0) {\n return session.respond(200, {\n ok: true,\n fromDate: fromDay,\n toDate: toDay,\n staffMemberId,\n copied: 0,\n created: [],\n skipped: [],\n })\n }\n\n if (!parsed.allowDuplicates) {\n const existing = await em.find(\n StaffTimeEntry,\n { tenantId, organizationId, staffMemberId, date: parsed.toDate, deletedAt: null },\n { fields: ['id'] },\n )\n if (existing.length > 0) {\n throw new CrudHttpError(409, {\n code: COPY_DAY_TARGET_NOT_EMPTY_CODE,\n error: translate(\n 'staff.timesheets.errors.copyDayTargetNotEmpty',\n 'The target day already has time entries. Review them first, or confirm to add these copies anyway.',\n ),\n toDate: toDay,\n existingEntryCount: existing.length,\n existingEntryIds: existing.map((entry) => entry.id),\n })\n }\n }\n\n const guardResult = await runRouteMutationGuards({\n container,\n req,\n auth: {\n userId: actorId,\n tenantId,\n organizationId,\n userFeatures: grantedFeatures,\n },\n input: {\n resourceKind: RESOURCE_KIND,\n resourceId: staffMemberId,\n operation: 'create',\n mutationPayload: parsed,\n },\n })\n if (!guardResult.ok) return guardResult.response\n\n const skipped: SkippedSource[] = []\n const copyable: StaffTimeEntry[] = []\n for (const entry of sources) {\n if (entry.lockedReportId) {\n skipped.push({\n id: entry.id,\n reason: TIME_ENTRY_LOCKED_CODE,\n lockedReportId: entry.lockedReportId,\n error: null,\n })\n continue\n }\n copyable.push(entry)\n }\n\n // Tags are read here rather than inside the command: the command writes tags\n // only through the tag commands and never touches `staff_time_entry_tags`, so\n // \u2014 exactly as with the single-entry duplicate, where the client supplies the\n // `tags[]` it already holds \u2014 the caller of the command supplies them.\n const tagIdsByEntry = await loadTagIdsByEntry(em, copyable.map((entry) => entry.id), tenantId, organizationId)\n\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n\n const copiedIdBySource = new Map<string, string>()\n for (const entry of copyable) {\n try {\n const { result } = await commandBus.execute<unknown, { timeEntryId: string }>(\n staffTimeEntryCommandIds.duplicate,\n {\n input: {\n id: entry.id,\n date: parsed.toDate,\n tagIds: tagIdsByEntry.get(entry.id) ?? [],\n },\n ctx,\n },\n )\n if (result?.timeEntryId) copiedIdBySource.set(entry.id, result.timeEntryId)\n } catch (err) {\n // A source can stop being copyable between the read and the write \u2014 its\n // project soft-deleted, say. The day is a set of drafts, so one source\n // that cannot be copied is reported beside the locked ones instead of\n // discarding the copies that already landed.\n //\n // A command interceptor that refuses one source with an explicit status is\n // that same per-source refusal, so it is reported here rather than in the\n // route-level catch: this is the clause the rejection actually reaches, and\n // aborting the day from it would strand the copies already committed. The\n // batch contract carries no per-item status, so the interceptor's message\n // travels on the skipped row instead of degrading to `null`.\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n const skippedError = interceptorRejection\n ? String(interceptorRejection.body.error ?? '') || null\n : isCrudHttpError(err)\n ? String((err.body as { error?: unknown })?.error ?? '') || null\n : null\n logger.warn('staff.timesheets.time-entries.copy-day source copy failed', { err, sourceId: entry.id })\n skipped.push({\n id: entry.id,\n reason: COPY_FAILED_REASON,\n lockedReportId: null,\n error: skippedError,\n })\n }\n }\n\n await guardResult.runAfterSuccess()\n\n for (const [sourceId, createdId] of copiedIdBySource) {\n void emitStaffEvent('staff.timesheets.time_entry.copied', {\n id: createdId,\n sourceId,\n tenantId,\n organizationId,\n staffMemberId,\n date: toDay,\n }, { persistent: true }).catch((err) => {\n logger.error('staff.timesheets emit time_entry.copied failed', { err, sourceId })\n })\n }\n\n // Screen 1 note 5: these are drafts to correct, so the response carries the\n // rows themselves rather than a count the UI would have to go and re-fetch.\n const createdIds = Array.from(copiedIdBySource.values())\n const createdRows = createdIds.length\n ? await findWithDecryption(\n em.fork(),\n StaffTimeEntry,\n { id: { $in: createdIds }, tenantId, organizationId, deletedAt: null },\n {},\n scopeCtx,\n )\n : []\n const createdRowById = new Map(createdRows.map((row) => [row.id, row]))\n\n const created = Array.from(copiedIdBySource.entries()).flatMap(([sourceId, createdId]) => {\n const row = createdRowById.get(createdId)\n if (!row) return []\n return [\n {\n id: row.id,\n sourceId,\n date: dayKey(row.date),\n durationMinutes: row.durationMinutes,\n roundedMinutes: row.roundedMinutes ?? null,\n timeProjectId: row.timeProjectId ?? null,\n taskId: row.taskId ?? null,\n isBillable: row.isBillable,\n description: row.notes ?? null,\n tagIds: tagIdsByEntry.get(sourceId) ?? [],\n },\n ]\n })\n\n return session.respond(200, {\n ok: true,\n fromDate: fromDay,\n toDate: toDay,\n staffMemberId,\n copied: created.length,\n created,\n skipped,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: translate('staff.errors.invalid_request', 'Invalid request'), details: err.issues },\n { status: 400 },\n )\n }\n logger.error('staff.timesheets.time-entries.copy-day failed', { err })\n return NextResponse.json(\n { error: translate('staff.errors.internal', 'Internal server error') },\n { status: 500 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Copy a day of time entries',\n methods: {\n POST: {\n summary: 'Copy a day of time entries',\n description:\n 'US-D6, screen 1 note 5. Copies every unlocked time entry the caller owns on `fromDate` onto `toDate` as editable drafts, and returns the created rows so they can be reviewed and corrected. Which day is \"the previous working day\" is the caller decision \u2014 this endpoint takes explicit dates and applies no weekend or holiday rule. A source entry locked into a closed report is skipped and reported in `skipped[]` with the shared `time_entry_locked` code (nothing is written to it, so the batch is not failed). The request is refused with 409 `copy_day_target_not_empty` when the target day already holds entries, so a repeated call cannot silently double a day; pass `allowDuplicates: true` to add the copies anyway. `staffMemberId` targets another member and requires `staff.timesheets.manage_all`. Every copy is written through the duplicate command and therefore through the create command, so ownership, project access, the billable and currency defaults and the tenant rounding rule apply to it exactly as they do to a hand-made entry.',\n requestBody: {\n contentType: 'application/json',\n schema: copyDayRequestSchema,\n },\n responses: [\n { status: 200, description: 'Copies created, with the skipped sources reported', schema: copyDayResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid body, missing scope, or fromDate equal to toDate' },\n { status: 401, description: 'Unauthorized' },\n { status: 403, description: 'Missing staff.timesheets.manage_own, no linked staff member, or another member day without staff.timesheets.manage_all' },\n {\n status: 409,\n description: 'The target day already has entries; nothing was copied',\n schema: targetNotEmptySchema,\n },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/**\n * US-D6 \u2014 \"Kopiuj wczorajszy dzie\u0144\" (screen 1, note 5).\n *\n * The note is the design constraint: the button \"duplicates the previous working\n * day's entries **as versions to correct**\". So this endpoint is not a silent\n * commit \u2014 it answers with the entries it created, in full, so the caller can put\n * them straight in front of the user for review and adjustment rather than\n * reporting a count and leaving them to hunt for what landed.\n *\n * What \"the previous working day\" means is the CALLER's business. Weekends,\n * holidays and four-day weeks are calendar questions this module has no data for,\n * and a server-side weekend rule would be wrong for every tenant that does not\n * work Monday-to-Friday. The endpoint therefore takes two explicit days.\n *\n * Three decisions worth their reasoning:\n *\n * 1. **A locked SOURCE is skipped and reported, not fatal.** The lock protects\n * the numbers a closed report has already billed (D-7), and a copy never\n * writes to its source \u2014 the target day is a fresh, unlocked draft. This is\n * what makes it different from the grid bulk save, where the rows being\n * WRITTEN are the locked ones and the whole batch must fail. Here the caller\n * did not hand-pick rows: they asked for a day. Failing the whole day because\n * one of its entries sits in a closed report would break the button for\n * exactly the people who report most regularly. Every skipped source comes\n * back in `skipped[]` carrying the shared `time_entry_locked` code and the\n * report that froze it, so the UI can say what was left behind.\n * 2. **Idempotency: a non-empty target day is refused.** A day of entries has no\n * natural key to deduplicate on \u2014 two identical 30-minute stand-ups on one day\n * are legitimate data \u2014 so a second call cannot tell a re-run from a genuine\n * repeat, and would silently double the day into something the user cannot\n * untangle from the grid. So the default is to refuse with `409\n * copy_day_target_not_empty`, naming what is already there. `allowDuplicates:\n * true` is the deliberate opt-in for the real case the refusal would\n * otherwise block: the user already logged something today and still wants\n * yesterday's set on top. Refusing is also what makes a partially completed\n * run visible instead of compounding it.\n * 3. **The copy itself is the existing duplicate command, once per source.** It\n * already resolves the source, runs the lock gate and delegates the write to\n * the create command, so every copy inherits ownership, project access, the\n * billable and currency defaults, rounding (D-7), tags and audit unchanged.\n * The loop is NOT wrapped in a route-level transaction: the command bus forks\n * its own EntityManager per command, so an outer `em.transactional` would\n * enclose nothing and the atomicity would be a fiction. Each copy is atomic in\n * itself; a copy that fails is reported in `skipped[]` and the rest still\n * land, which is the behaviour a day of drafts wants.\n */\n\nimport { NextResponse } from 'next/server'\nimport { resolveFeatureAccess } from '../../../../lib/time-tracking/featureAccess'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveSingleOrganizationIdOrDeny } from '@open-mercato/core/modules/directory/utils/organizationScopeFilter'\nimport { CrudHttpError, forbidden, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { runRouteMutationGuards } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { emitStaffEvent } from '../../../../events'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { StaffTimeEntry, StaffTimeEntryTag } from '../../../../data/entities'\nimport { staffTimeEntryCommandIds, TIME_ENTRY_LOCKED_CODE } from '../../../../commands/timesheets-entries'\nimport { resolveProjectAccess, type ProjectAccess } from '../../../../lib/time-tracking/access'\nimport { readTimeTrackingSettings } from '../../../../lib/time-tracking/settings'\nimport { STAFF_TIME_TRACKING_RESOURCE_KINDS } from '../../../guards'\nimport { runTimesheetInterceptors } from '../../_shared/withTimesheetInterceptors'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/time-entries/copy-day' })\n\nconst MANAGE_OWN_FEATURE = 'staff.timesheets.manage_own'\nconst MANAGE_ALL_FEATURE = 'staff.timesheets.manage_all'\nconst RESOURCE_KIND = STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry\n\n/** The 409 a second \"copy yesterday\" click gets, rather than a doubled day. */\nexport const COPY_DAY_TARGET_NOT_EMPTY_CODE = 'copy_day_target_not_empty'\n\n/** Reason codes on `skipped[]`. The locked one is the shared code, not a second spelling of it. */\nconst COPY_FAILED_REASON = 'copy_failed'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: [MANAGE_OWN_FEATURE] },\n}\n\nconst copyDayRequestSchema = z.object({\n fromDate: z.coerce.date(),\n toDate: z.coerce.date(),\n /** Only a `staff.timesheets.manage_all` holder may copy somebody else's day. */\n staffMemberId: z.string().uuid().optional(),\n allowDuplicates: z.boolean().optional().default(false),\n})\n\nconst copiedEntrySchema = z.object({\n id: z.string().uuid(),\n sourceId: z.string().uuid(),\n date: z.string(),\n durationMinutes: z.number(),\n roundedMinutes: z.number().nullable(),\n timeProjectId: z.string().uuid().nullable(),\n taskId: z.string().uuid().nullable(),\n isBillable: z.boolean(),\n description: z.string().nullable(),\n tagIds: z.array(z.string().uuid()),\n})\n\nconst skippedEntrySchema = z.object({\n id: z.string().uuid(),\n reason: z.string(),\n lockedReportId: z.string().uuid().nullable(),\n error: z.string().nullable(),\n})\n\nconst copyDayResponseSchema = z.object({\n ok: z.literal(true),\n fromDate: z.string(),\n toDate: z.string(),\n staffMemberId: z.string().uuid(),\n copied: z.number(),\n created: z.array(copiedEntrySchema),\n skipped: z.array(skippedEntrySchema),\n})\n\nconst targetNotEmptySchema = z.object({\n code: z.literal(COPY_DAY_TARGET_NOT_EMPTY_CODE),\n error: z.string(),\n toDate: z.string(),\n existingEntryCount: z.number(),\n existingEntryIds: z.array(z.string().uuid()),\n})\n\ntype ContainerLike = { resolve: (name: string) => unknown }\n\ntype SkippedSource = {\n id: string\n reason: string\n lockedReportId: string | null\n error: string | null\n}\n\n/**\n * A day is addressed by its calendar day, never by a timestamp (D-8), so both the\n * request echo and the copied rows collapse to `YYYY-MM-DD`.\n */\nfunction dayKey(value: Date | string | null | undefined): string {\n if (!value) return ''\n const date = value instanceof Date ? value : new Date(value)\n return Number.isNaN(date.getTime()) ? String(value) : date.toISOString().slice(0, 10)\n}\n\nasync function resolveAssignmentGraceDays(container: ContainerLike, tenantId: string): Promise<number | null> {\n try {\n const configService = container.resolve('moduleConfigService') as ModuleConfigService\n const settings = await readTimeTrackingSettings(configService, { tenantId })\n return settings.access.assignmentGraceDays\n } catch {\n // Fail safe to the documented default rather than widening the window.\n return null\n }\n}\n\nasync function loadProjectAccess(\n container: ContainerLike,\n userId: string | null,\n tenantId: string,\n organizationId: string,\n grantedFeatures: readonly string[],\n): Promise<ProjectAccess> {\n const em = container.resolve('em') as EntityManager\n return resolveProjectAccess({\n em: em.fork(),\n userId,\n tenantId,\n organizationId,\n userFeatures: grantedFeatures,\n assignmentGraceDays: await resolveAssignmentGraceDays(container, tenantId),\n })\n}\n\n/**\n * The per-row form of the list route's `$or` intersection: an entry on a project\n * is visible to the members of that project, and a project-less entry is visible\n * only to the member who logged it. A manager copying somebody else's day still\n * only copies the part of it their own project access covers.\n */\nfunction isTimeEntryVisible(\n entry: { staffMemberId: string; timeProjectId?: string | null },\n access: ProjectAccess,\n): boolean {\n if (access.canManageAll) return true\n const projectId = entry.timeProjectId ?? null\n if (projectId) return access.projectIds.includes(projectId)\n return access.staffMemberId !== null && access.staffMemberId === entry.staffMemberId\n}\n\nasync function loadTagIdsByEntry(\n em: EntityManager,\n entryIds: string[],\n tenantId: string,\n organizationId: string,\n): Promise<Map<string, string[]>> {\n const byEntryId = new Map<string, string[]>()\n if (entryIds.length === 0) return byEntryId\n const assignments = await em.find(StaffTimeEntryTag, {\n timeEntryId: { $in: entryIds },\n tenantId,\n organizationId,\n })\n for (const assignment of assignments) {\n const list = byEntryId.get(assignment.timeEntryId) ?? []\n list.push(assignment.tagId)\n byEntryId.set(assignment.timeEntryId, list)\n }\n return byEntryId\n}\n\nexport async function POST(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) {\n throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = resolveSingleOrganizationIdOrDeny(scope, auth) ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, {\n error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.'),\n })\n }\n\n const actorId = auth.sub ?? ''\n // Both decisions come from the module's single RBAC authority, which asks the\n // service and fails closed on every path. The nullable grant array they were\n // matched against could not say whether an empty answer meant \"no grants\" or\n // \"could not ask\", and `grantedFeatures ? \u2026 : false` silently answered the\n // second as the first.\n const manageOwn = await resolveFeatureAccess(container, actorId, [MANAGE_OWN_FEATURE], { tenantId, organizationId })\n if (!manageOwn.allowed) {\n throw forbidden(translate('staff.errors.forbidden', 'Forbidden'))\n }\n const grantedFeatures = manageOwn.grantedFeatures\n const hasManageAll = (\n await resolveFeatureAccess(container, actorId, [MANAGE_ALL_FEATURE], { tenantId, organizationId })\n ).allowed\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'POST',\n scope: { container, userId: actorId, tenantId, organizationId, userFeatures: grantedFeatures },\n body: await readJsonSafe<Record<string, unknown>>(req, {}),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const parsed = copyDayRequestSchema.parse(session.body)\n const fromDay = dayKey(parsed.fromDate)\n const toDay = dayKey(parsed.toDate)\n if (fromDay === toDay) {\n // Copying a day onto itself is always a mistake, never intent, and\n // `allowDuplicates` would turn it into a silent doubling.\n throw new CrudHttpError(400, {\n error: translate(\n 'staff.timesheets.errors.copyDaySameDate',\n 'The source and target day must be different.',\n ),\n })\n }\n\n const access = await loadProjectAccess(container, actorId || null, tenantId, organizationId, grantedFeatures)\n // Ownership: the copies are attributed by the create command, which silently\n // files them under the caller unless they hold `staff.timesheets.manage_all`.\n // Refusing here rather than letting that happen keeps a projects-manager from\n // asking for a colleague's day and quietly receiving it under their own name.\n if (parsed.staffMemberId && parsed.staffMemberId !== access.staffMemberId && !hasManageAll) {\n throw forbidden(\n translate('staff.timesheets.errors.notOwner', 'You can only manage your own time entries.'),\n )\n }\n const staffMemberId = parsed.staffMemberId ?? access.staffMemberId\n if (!staffMemberId) {\n throw new CrudHttpError(403, {\n error: translate('staff.timesheets.errors.noStaffMember', 'No staff member linked to your account.'),\n })\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const scopeCtx = { tenantId, organizationId }\n\n const sources = (\n await findWithDecryption(\n em,\n StaffTimeEntry,\n { tenantId, organizationId, staffMemberId, date: parsed.fromDate, deletedAt: null },\n {},\n scopeCtx,\n )\n ).filter((entry) => isTimeEntryVisible(entry, access))\n\n if (sources.length === 0) {\n return session.respond(200, {\n ok: true,\n fromDate: fromDay,\n toDate: toDay,\n staffMemberId,\n copied: 0,\n created: [],\n skipped: [],\n })\n }\n\n if (!parsed.allowDuplicates) {\n const existing = await em.find(\n StaffTimeEntry,\n { tenantId, organizationId, staffMemberId, date: parsed.toDate, deletedAt: null },\n { fields: ['id'] },\n )\n if (existing.length > 0) {\n throw new CrudHttpError(409, {\n code: COPY_DAY_TARGET_NOT_EMPTY_CODE,\n error: translate(\n 'staff.timesheets.errors.copyDayTargetNotEmpty',\n 'The target day already has time entries. Review them first, or confirm to add these copies anyway.',\n ),\n toDate: toDay,\n existingEntryCount: existing.length,\n existingEntryIds: existing.map((entry) => entry.id),\n })\n }\n }\n\n const guardResult = await runRouteMutationGuards({\n container,\n req,\n auth: {\n userId: actorId,\n tenantId,\n organizationId,\n userFeatures: grantedFeatures,\n },\n input: {\n resourceKind: RESOURCE_KIND,\n resourceId: staffMemberId,\n operation: 'create',\n mutationPayload: parsed,\n },\n })\n if (!guardResult.ok) return guardResult.response\n\n const skipped: SkippedSource[] = []\n const copyable: StaffTimeEntry[] = []\n for (const entry of sources) {\n if (entry.lockedReportId) {\n skipped.push({\n id: entry.id,\n reason: TIME_ENTRY_LOCKED_CODE,\n lockedReportId: entry.lockedReportId,\n error: null,\n })\n continue\n }\n copyable.push(entry)\n }\n\n // Tags are read here rather than inside the command: the command writes tags\n // only through the tag commands and never touches `staff_time_entry_tags`, so\n // \u2014 exactly as with the single-entry duplicate, where the client supplies the\n // `tags[]` it already holds \u2014 the caller of the command supplies them.\n const tagIdsByEntry = await loadTagIdsByEntry(em, copyable.map((entry) => entry.id), tenantId, organizationId)\n\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: resolveSingleOrganizationIdOrDeny(scope, auth) ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n const commandBus = container.resolve('commandBus') as CommandBus\n\n const copiedIdBySource = new Map<string, string>()\n for (const entry of copyable) {\n try {\n const { result } = await commandBus.execute<unknown, { timeEntryId: string }>(\n staffTimeEntryCommandIds.duplicate,\n {\n input: {\n id: entry.id,\n date: parsed.toDate,\n tagIds: tagIdsByEntry.get(entry.id) ?? [],\n },\n ctx,\n },\n )\n if (result?.timeEntryId) copiedIdBySource.set(entry.id, result.timeEntryId)\n } catch (err) {\n // A source can stop being copyable between the read and the write \u2014 its\n // project soft-deleted, say. The day is a set of drafts, so one source\n // that cannot be copied is reported beside the locked ones instead of\n // discarding the copies that already landed.\n //\n // A command interceptor that refuses one source with an explicit status is\n // that same per-source refusal, so it is reported here rather than in the\n // route-level catch: this is the clause the rejection actually reaches, and\n // aborting the day from it would strand the copies already committed. The\n // batch contract carries no per-item status, so the interceptor's message\n // travels on the skipped row instead of degrading to `null`.\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n const skippedError = interceptorRejection\n ? String(interceptorRejection.body.error ?? '') || null\n : isCrudHttpError(err)\n ? String((err.body as { error?: unknown })?.error ?? '') || null\n : null\n logger.warn('staff.timesheets.time-entries.copy-day source copy failed', { err, sourceId: entry.id })\n skipped.push({\n id: entry.id,\n reason: COPY_FAILED_REASON,\n lockedReportId: null,\n error: skippedError,\n })\n }\n }\n\n await guardResult.runAfterSuccess()\n\n for (const [sourceId, createdId] of copiedIdBySource) {\n void emitStaffEvent('staff.timesheets.time_entry.copied', {\n id: createdId,\n sourceId,\n tenantId,\n organizationId,\n staffMemberId,\n date: toDay,\n }, { persistent: true }).catch((err) => {\n logger.error('staff.timesheets emit time_entry.copied failed', { err, sourceId })\n })\n }\n\n // Screen 1 note 5: these are drafts to correct, so the response carries the\n // rows themselves rather than a count the UI would have to go and re-fetch.\n const createdIds = Array.from(copiedIdBySource.values())\n const createdRows = createdIds.length\n ? await findWithDecryption(\n em.fork(),\n StaffTimeEntry,\n { id: { $in: createdIds }, tenantId, organizationId, deletedAt: null },\n {},\n scopeCtx,\n )\n : []\n const createdRowById = new Map(createdRows.map((row) => [row.id, row]))\n\n const created = Array.from(copiedIdBySource.entries()).flatMap(([sourceId, createdId]) => {\n const row = createdRowById.get(createdId)\n if (!row) return []\n return [\n {\n id: row.id,\n sourceId,\n date: dayKey(row.date),\n durationMinutes: row.durationMinutes,\n roundedMinutes: row.roundedMinutes ?? null,\n timeProjectId: row.timeProjectId ?? null,\n taskId: row.taskId ?? null,\n isBillable: row.isBillable,\n description: row.notes ?? null,\n tagIds: tagIdsByEntry.get(sourceId) ?? [],\n },\n ]\n })\n\n return session.respond(200, {\n ok: true,\n fromDate: fromDay,\n toDate: toDay,\n staffMemberId,\n copied: created.length,\n created,\n skipped,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: translate('staff.errors.invalid_request', 'Invalid request'), details: err.issues },\n { status: 400 },\n )\n }\n logger.error('staff.timesheets.time-entries.copy-day failed', { err })\n return NextResponse.json(\n { error: translate('staff.errors.internal', 'Internal server error') },\n { status: 500 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Copy a day of time entries',\n methods: {\n POST: {\n summary: 'Copy a day of time entries',\n description:\n 'US-D6, screen 1 note 5. Copies every unlocked time entry the caller owns on `fromDate` onto `toDate` as editable drafts, and returns the created rows so they can be reviewed and corrected. Which day is \"the previous working day\" is the caller decision \u2014 this endpoint takes explicit dates and applies no weekend or holiday rule. A source entry locked into a closed report is skipped and reported in `skipped[]` with the shared `time_entry_locked` code (nothing is written to it, so the batch is not failed). The request is refused with 409 `copy_day_target_not_empty` when the target day already holds entries, so a repeated call cannot silently double a day; pass `allowDuplicates: true` to add the copies anyway. `staffMemberId` targets another member and requires `staff.timesheets.manage_all`. Every copy is written through the duplicate command and therefore through the create command, so ownership, project access, the billable and currency defaults and the tenant rounding rule apply to it exactly as they do to a hand-made entry.',\n requestBody: {\n contentType: 'application/json',\n schema: copyDayRequestSchema,\n },\n responses: [\n { status: 200, description: 'Copies created, with the skipped sources reported', schema: copyDayResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid body, missing scope, or fromDate equal to toDate' },\n { status: 401, description: 'Unauthorized' },\n { status: 403, description: 'Missing staff.timesheets.manage_own, no linked staff member, or another member day without staff.timesheets.manage_all' },\n {\n status: 409,\n description: 'The target day already has entries; nothing was copied',\n schema: targetNotEmptySchema,\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,6 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
4
4
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
5
5
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
|
+
import { resolveSingleOrganizationIdOrDeny } from "@open-mercato/core/modules/directory/utils/organizationScopeFilter";
|
|
6
7
|
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
7
8
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
8
9
|
import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
@@ -177,7 +178,7 @@ async function GET(req) {
|
|
|
177
178
|
}
|
|
178
179
|
const orgScope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
|
|
179
180
|
const tenantId = orgScope?.tenantId ?? auth.tenantId ?? null;
|
|
180
|
-
const organizationId = orgScope
|
|
181
|
+
const organizationId = resolveSingleOrganizationIdOrDeny(orgScope, auth) ?? null;
|
|
181
182
|
if (!tenantId || !organizationId) {
|
|
182
183
|
throw new CrudHttpError(400, {
|
|
183
184
|
error: translate("staff.errors.missingScope", "Missing tenant or organization scope.")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/staff/api/timesheets/time-entries/overlaps/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { authorizeFeatures } from '@open-mercato/shared/security/featurePolicy'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { StaffTimeEntry, StaffTimeProject, StaffTimeTask } from '../../../../data/entities'\nimport { deriveInterval, formatClock, parseClock } from '../../../../lib/time-tracking/interval'\nimport {\n evaluateOverlapPolicies,\n findOverlaps,\n type OverlapSpan,\n} from '../../../../lib/time-tracking/overlap'\nimport { resolveFeatureAccess } from '../../../../lib/time-tracking/featureAccess'\nimport {\n MANAGE_PROJECTS_FEATURE,\n assertProjectAccess,\n resolveProjectAccess,\n} from '../../../../lib/time-tracking/access'\nimport { readTimeTrackingSettings } from '../../../../lib/time-tracking/settings'\nimport {\n readSearchParamsRecord,\n runTimesheetInterceptors,\n} from '../../_shared/withTimesheetInterceptors'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/time-entries/overlaps' })\n\nconst VIEW_FEATURE = 'staff.timesheets.view'\nconst MANAGE_ALL_FEATURE = 'staff.timesheets.manage_all'\n\n/**\n * Upper bound for the day-window scan. One staff member can only hold a handful\n * of entries across three days, so this is a guard against pathological data\n * rather than a paging contract.\n */\nconst MAX_WINDOW_ROWS = 500\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: [VIEW_FEATURE] },\n}\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/\nconst DATE_PREFIX_PATTERN = /^(\\d{4}-\\d{2}-\\d{2})/\n\nconst querySchema = z.object({\n date: z.string().regex(DATE_PATTERN),\n startedAt: z.string().optional(),\n endedAt: z.string().optional(),\n durationMinutes: z.coerce.number().int().min(0).max(1440).optional(),\n excludeId: z.string().uuid().optional(),\n staffMemberId: z.string().uuid().optional(),\n})\n\nconst overlapItemSchema = z.object({\n id: z.string().uuid(),\n staff_member_id: z.string().uuid(),\n date: z.string(),\n started_at: z.string(),\n ended_at: z.string(),\n duration_minutes: z.number(),\n overlap_minutes: z.number(),\n crosses_midnight: z.boolean(),\n task_id: z.string().uuid().nullable(),\n task_title: z.string().nullable(),\n time_project_id: z.string().uuid().nullable(),\n project_name: z.string().nullable(),\n notes: z.string().nullable(),\n})\n\nconst overlapsResponseSchema = z.object({\n items: z.array(overlapItemSchema),\n total: z.number(),\n /**\n * EP-38. `warn` is what this route has always meant; the field exists so a\n * contributed policy that escalates to `block` reaches the form, and so a\n * caller can tell \"the tenant turned the warning off\" from \"nothing overlaps\".\n */\n decision: z.enum(['allow', 'warn', 'block']),\n})\n\ntype OverlapItem = z.infer<typeof overlapItemSchema>\n\ntype RbacServiceLike = {\n getGrantedFeatures?: (\n userId: string,\n options: { tenantId: string | null; organizationId: string | null },\n ) => Promise<string[]>\n}\n\ntype EntryRow = Pick<\n StaffTimeEntry,\n | 'id'\n | 'staffMemberId'\n | 'durationMinutes'\n | 'startedAt'\n | 'endedAt'\n | 'notes'\n | 'taskId'\n | 'timeProjectId'\n> & { date: unknown }\n\nconst EMPTY_OVERLAP_RESULT = { items: [], total: 0, decision: 'allow' as const }\n\nfunction pad(value: number): string {\n return String(value).padStart(2, '0')\n}\n\nfunction formatLocalDate(value: Date): string {\n return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`\n}\n\nfunction shiftDate(date: string, days: number): string {\n const [year, month, day] = date.split('-').map(Number)\n const shifted = new Date(Date.UTC(year, month - 1, day + days))\n return `${shifted.getUTCFullYear()}-${pad(shifted.getUTCMonth() + 1)}-${pad(shifted.getUTCDate())}`\n}\n\nfunction toDayString(value: unknown): string | null {\n if (typeof value === 'string') {\n const match = DATE_PREFIX_PATTERN.exec(value.trim())\n return match ? match[1] : null\n }\n if (value instanceof Date && !Number.isNaN(value.getTime())) return formatLocalDate(value)\n return null\n}\n\n/**\n * Accepts either a wall clock (`11:45`, what the entry form sends) or a full\n * timestamp (what the timer endpoints persist), and always answers in the\n * calendar the rest of the module reads dates in.\n */\nfunction toClock(value: unknown): string | null {\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) return null\n return formatClock(value.getHours() * 60 + value.getMinutes())\n }\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n if (!trimmed) return null\n const clock = parseClock(trimmed)\n if (clock !== null) return formatClock(clock)\n const parsed = new Date(trimmed)\n if (Number.isNaN(parsed.getTime())) return null\n return formatClock(parsed.getHours() * 60 + parsed.getMinutes())\n}\n\nfunction toRowSpan(row: EntryRow): OverlapSpan | null {\n const start = toClock(row.startedAt)\n if (!start) return null\n const date =\n (row.startedAt instanceof Date && !Number.isNaN(row.startedAt.getTime())\n ? formatLocalDate(row.startedAt)\n : null) ?? toDayString(row.date)\n if (!date) return null\n return {\n id: row.id,\n date,\n start,\n end: toClock(row.endedAt),\n durationMinutes: typeof row.durationMinutes === 'number' ? row.durationMinutes : null,\n }\n}\n\nfunction readQuery(params: URLSearchParams): z.infer<typeof querySchema> {\n const raw: Record<string, string> = {}\n for (const key of ['date', 'startedAt', 'endedAt', 'durationMinutes', 'excludeId', 'staffMemberId']) {\n const value = params.get(key)\n if (value !== null && value.trim().length > 0) raw[key] = value.trim()\n }\n return querySchema.parse(raw)\n}\n\nasync function resolveGrantedFeatures(\n container: Awaited<ReturnType<typeof createRequestContainer>>,\n userId: string,\n scope: { tenantId: string; organizationId: string },\n): Promise<string[]> {\n try {\n const rbac = container.resolve('rbacService') as RbacServiceLike | undefined\n if (!rbac?.getGrantedFeatures) return []\n return (await rbac.getGrantedFeatures(userId, scope)) ?? []\n } catch {\n return []\n }\n}\n\nasync function resolveOverlapSettings(\n container: Awaited<ReturnType<typeof createRequestContainer>>,\n tenantId: string,\n): Promise<{ assignmentGraceDays: number | null; warningsEnabled: boolean }> {\n try {\n const configService = container.resolve('moduleConfigService') as ModuleConfigService\n const settings = await readTimeTrackingSettings(configService, { tenantId })\n return {\n assignmentGraceDays: settings.access.assignmentGraceDays,\n warningsEnabled: settings.warnings.overlap,\n }\n } catch {\n // The same fail-safe the grace window uses: an unreadable settings row keeps\n // the advisory warning on, which is what the module default says.\n return { assignmentGraceDays: null, warningsEnabled: true }\n }\n}\n\nasync function loadLabels(\n em: EntityManager,\n scope: { tenantId: string; organizationId: string },\n taskIds: string[],\n projectIds: string[],\n): Promise<{ tasks: Map<string, string>; projects: Map<string, string> }> {\n const tasks = new Map<string, string>()\n const projects = new Map<string, string>()\n if (taskIds.length) {\n const rows = await findWithDecryption(\n em,\n StaffTimeTask,\n { id: { $in: taskIds }, ...scope, deletedAt: null },\n {},\n scope,\n )\n for (const row of rows) tasks.set(row.id, row.title)\n }\n if (projectIds.length) {\n const rows = await findWithDecryption(\n em,\n StaffTimeProject,\n { id: { $in: projectIds }, ...scope, deletedAt: null },\n {},\n scope,\n )\n for (const row of rows) projects.set(row.id, row.name)\n }\n return { tasks, projects }\n}\n\n/**\n * GET /api/staff/timesheets/time-entries/overlaps\n *\n * Advisory-only overlap probe for the time entry form (mockup screen 9). It is\n * read-only and never blocks a save: overlapping time is legitimate in\n * consulting work (a client call while taking notes), so the UI renders the\n * result as a warning with a suggested fix, never as a validation error. Any\n * failure here MUST degrade to \"no warning\" rather than break the form \u2014 the\n * route therefore answers `{ items: [], total: 0 }` for unusable input, a\n * caller without a linked staff member, or a lookup that yields nothing, and\n * the client is expected to ignore non-200 responses.\n *\n * Scoping: rows are always filtered by `tenant_id` + `organization_id` and\n * intersected with `resolveProjectAccess`, so the endpoint cannot be used to\n * probe entries on projects the caller cannot see. Asking about another staff\n * member requires `staff.timesheets.manage_all`; without it the request is\n * silently scoped back to the caller's own member instead of erroring.\n *\n * Query shape: the form calls this on blur, so the lookup reads a three-day\n * window (`date - 1 .. date + 1`) against the\n * `(organization_id, staff_member_id, date, started_at)` index. The adjacent\n * days cover midnight-crossing spans in both directions \u2014 a candidate that ends\n * after midnight, and a stored entry that started the previous evening \u2014 without\n * ever scanning history.\n */\nexport async function GET(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) {\n throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n }\n\n const orgScope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = orgScope?.tenantId ?? auth.tenantId ?? null\n const organizationId = orgScope?.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, {\n error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.'),\n })\n }\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'GET',\n scope: { container, userId: auth.sub, tenantId, organizationId },\n query: readSearchParamsRecord(req.url),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const query = readQuery(session.searchParams)\n const candidate: OverlapSpan = {\n date: query.date,\n start: toClock(query.startedAt),\n end: toClock(query.endedAt),\n durationMinutes: query.durationMinutes ?? null,\n }\n const derived = deriveInterval({\n start: candidate.start,\n end: candidate.end,\n durationMinutes: candidate.durationMinutes,\n })\n if (!derived.start || derived.durationMinutes === null || derived.durationMinutes <= 0) {\n return session.respond(200, EMPTY_OVERLAP_RESULT)\n }\n\n const scope = { tenantId, organizationId }\n const em = (container.resolve('em') as EntityManager).fork()\n const userId = typeof auth.sub === 'string' ? auth.sub : null\n // Both questions go to the service. Asking one and inferring the other from a\n // grant array is how the two could disagree when the read failed.\n const manageAll = await resolveFeatureAccess(container, userId, [MANAGE_ALL_FEATURE], scope)\n const manageProjects = await resolveFeatureAccess(container, userId, [MANAGE_PROJECTS_FEATURE], scope)\n const canManageAll = manageAll.allowed\n\n const overlapSettings = await resolveOverlapSettings(container, tenantId)\n const access = await resolveProjectAccess({\n em,\n userId,\n tenantId,\n organizationId,\n canManageAll: manageProjects.allowed,\n userFeatures: manageAll.grantedFeatures,\n assignmentGraceDays: overlapSettings.assignmentGraceDays,\n })\n\n const staffMemberId =\n canManageAll && query.staffMemberId ? query.staffMemberId : access.staffMemberId\n if (!staffMemberId) return session.respond(200, EMPTY_OVERLAP_RESULT)\n const isSelfScope = staffMemberId === access.staffMemberId\n\n const where = {\n tenantId,\n organizationId,\n staffMemberId,\n deletedAt: null,\n date: { $gte: shiftDate(query.date, -1), $lte: shiftDate(query.date, 1) },\n } as unknown as FilterQuery<StaffTimeEntry>\n const rows = (await findWithDecryption(\n em,\n StaffTimeEntry,\n where,\n { orderBy: { startedAt: 'asc' }, limit: MAX_WINDOW_ROWS },\n scope,\n )) as unknown as EntryRow[]\n\n const visible = rows.filter(\n (row) =>\n assertProjectAccess(access, row.timeProjectId ?? null) ||\n (isSelfScope && !row.timeProjectId),\n )\n const byId = new Map(visible.map((row) => [row.id, row]))\n const spans = visible\n .map((row) => toRowSpan(row))\n .filter((span): span is OverlapSpan => span !== null)\n\n const candidateSpan: OverlapSpan = {\n date: query.date,\n start: derived.start,\n end: derived.end,\n durationMinutes: derived.durationMinutes,\n }\n const overlaps = findOverlaps(\n candidateSpan,\n spans,\n query.excludeId ? { excludeId: query.excludeId } : undefined,\n )\n const decision = evaluateOverlapPolicies(overlaps, {\n tenantId,\n organizationId,\n candidate: candidateSpan,\n warningsEnabled: overlapSettings.warningsEnabled,\n staffMemberId,\n excludeId: query.excludeId ?? null,\n })\n // The item list is unchanged by the policy: this endpoint answers \"what\n // intersects\", and `decision` answers \"and what should happen\". Collapsing\n // the list when a tenant turns the warning off would make the two\n // indistinguishable from \"nothing intersects\" for every other caller.\n if (!overlaps.length) return session.respond(200, { ...EMPTY_OVERLAP_RESULT, decision })\n\n const taskIds = new Set<string>()\n const projectIds = new Set<string>()\n for (const overlap of overlaps) {\n const row = overlap.id ? byId.get(overlap.id) : undefined\n if (row?.taskId) taskIds.add(row.taskId)\n if (row?.timeProjectId) projectIds.add(row.timeProjectId)\n }\n const labels = await loadLabels(em, scope, [...taskIds], [...projectIds])\n\n const items: OverlapItem[] = []\n for (const overlap of overlaps) {\n const row = overlap.id ? byId.get(overlap.id) : undefined\n if (!row) continue\n const interval = deriveInterval({\n start: overlap.entry.start,\n end: overlap.entry.end,\n durationMinutes: overlap.entry.durationMinutes,\n })\n items.push({\n id: row.id,\n staff_member_id: row.staffMemberId,\n date: overlap.entry.date,\n started_at: interval.start ?? '',\n ended_at: interval.end ?? '',\n duration_minutes: interval.durationMinutes ?? 0,\n overlap_minutes: overlap.overlapMinutes,\n crosses_midnight: interval.crossesMidnight,\n task_id: row.taskId ?? null,\n task_title: row.taskId ? labels.tasks.get(row.taskId) ?? null : null,\n time_project_id: row.timeProjectId ?? null,\n project_name: row.timeProjectId ? labels.projects.get(row.timeProjectId) ?? null : null,\n notes: row.notes ?? null,\n })\n }\n\n return session.respond(200, { items, total: items.length, decision })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: translate('staff.errors.invalid_request', 'Invalid request'), details: err.issues },\n { status: 400 },\n )\n }\n logger.error('staff.timesheets.time-entries.overlaps GET failed', { err })\n return NextResponse.json(\n { error: translate('staff.errors.internal', 'Internal server error') },\n { status: 500 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Advisory time entry overlap check',\n methods: {\n GET: {\n summary: 'List time entries overlapping a candidate span',\n description:\n 'Advisory, read-only lookup used by the time entry form to warn about conflicting entries. Overlapping time is allowed, so the result never blocks a save. Returns an empty list when the supplied time information is insufficient. Checking another staff member requires staff.timesheets.manage_all; without it the request is silently scoped to the caller.',\n query: querySchema,\n responses: [\n { status: 200, description: 'Overlapping entries', schema: overlapsResponseSchema },\n { status: 400, description: 'Invalid query or missing scope', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveSingleOrganizationIdOrDeny } from '@open-mercato/core/modules/directory/utils/organizationScopeFilter'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { authorizeFeatures } from '@open-mercato/shared/security/featurePolicy'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { ModuleConfigService } from '@open-mercato/core/modules/configs/lib/module-config-service'\nimport { StaffTimeEntry, StaffTimeProject, StaffTimeTask } from '../../../../data/entities'\nimport { deriveInterval, formatClock, parseClock } from '../../../../lib/time-tracking/interval'\nimport {\n evaluateOverlapPolicies,\n findOverlaps,\n type OverlapSpan,\n} from '../../../../lib/time-tracking/overlap'\nimport { resolveFeatureAccess } from '../../../../lib/time-tracking/featureAccess'\nimport {\n MANAGE_PROJECTS_FEATURE,\n assertProjectAccess,\n resolveProjectAccess,\n} from '../../../../lib/time-tracking/access'\nimport { readTimeTrackingSettings } from '../../../../lib/time-tracking/settings'\nimport {\n readSearchParamsRecord,\n runTimesheetInterceptors,\n} from '../../_shared/withTimesheetInterceptors'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/time-entries/overlaps' })\n\nconst VIEW_FEATURE = 'staff.timesheets.view'\nconst MANAGE_ALL_FEATURE = 'staff.timesheets.manage_all'\n\n/**\n * Upper bound for the day-window scan. One staff member can only hold a handful\n * of entries across three days, so this is a guard against pathological data\n * rather than a paging contract.\n */\nconst MAX_WINDOW_ROWS = 500\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: [VIEW_FEATURE] },\n}\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/\nconst DATE_PREFIX_PATTERN = /^(\\d{4}-\\d{2}-\\d{2})/\n\nconst querySchema = z.object({\n date: z.string().regex(DATE_PATTERN),\n startedAt: z.string().optional(),\n endedAt: z.string().optional(),\n durationMinutes: z.coerce.number().int().min(0).max(1440).optional(),\n excludeId: z.string().uuid().optional(),\n staffMemberId: z.string().uuid().optional(),\n})\n\nconst overlapItemSchema = z.object({\n id: z.string().uuid(),\n staff_member_id: z.string().uuid(),\n date: z.string(),\n started_at: z.string(),\n ended_at: z.string(),\n duration_minutes: z.number(),\n overlap_minutes: z.number(),\n crosses_midnight: z.boolean(),\n task_id: z.string().uuid().nullable(),\n task_title: z.string().nullable(),\n time_project_id: z.string().uuid().nullable(),\n project_name: z.string().nullable(),\n notes: z.string().nullable(),\n})\n\nconst overlapsResponseSchema = z.object({\n items: z.array(overlapItemSchema),\n total: z.number(),\n /**\n * EP-38. `warn` is what this route has always meant; the field exists so a\n * contributed policy that escalates to `block` reaches the form, and so a\n * caller can tell \"the tenant turned the warning off\" from \"nothing overlaps\".\n */\n decision: z.enum(['allow', 'warn', 'block']),\n})\n\ntype OverlapItem = z.infer<typeof overlapItemSchema>\n\ntype RbacServiceLike = {\n getGrantedFeatures?: (\n userId: string,\n options: { tenantId: string | null; organizationId: string | null },\n ) => Promise<string[]>\n}\n\ntype EntryRow = Pick<\n StaffTimeEntry,\n | 'id'\n | 'staffMemberId'\n | 'durationMinutes'\n | 'startedAt'\n | 'endedAt'\n | 'notes'\n | 'taskId'\n | 'timeProjectId'\n> & { date: unknown }\n\nconst EMPTY_OVERLAP_RESULT = { items: [], total: 0, decision: 'allow' as const }\n\nfunction pad(value: number): string {\n return String(value).padStart(2, '0')\n}\n\nfunction formatLocalDate(value: Date): string {\n return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`\n}\n\nfunction shiftDate(date: string, days: number): string {\n const [year, month, day] = date.split('-').map(Number)\n const shifted = new Date(Date.UTC(year, month - 1, day + days))\n return `${shifted.getUTCFullYear()}-${pad(shifted.getUTCMonth() + 1)}-${pad(shifted.getUTCDate())}`\n}\n\nfunction toDayString(value: unknown): string | null {\n if (typeof value === 'string') {\n const match = DATE_PREFIX_PATTERN.exec(value.trim())\n return match ? match[1] : null\n }\n if (value instanceof Date && !Number.isNaN(value.getTime())) return formatLocalDate(value)\n return null\n}\n\n/**\n * Accepts either a wall clock (`11:45`, what the entry form sends) or a full\n * timestamp (what the timer endpoints persist), and always answers in the\n * calendar the rest of the module reads dates in.\n */\nfunction toClock(value: unknown): string | null {\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) return null\n return formatClock(value.getHours() * 60 + value.getMinutes())\n }\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n if (!trimmed) return null\n const clock = parseClock(trimmed)\n if (clock !== null) return formatClock(clock)\n const parsed = new Date(trimmed)\n if (Number.isNaN(parsed.getTime())) return null\n return formatClock(parsed.getHours() * 60 + parsed.getMinutes())\n}\n\nfunction toRowSpan(row: EntryRow): OverlapSpan | null {\n const start = toClock(row.startedAt)\n if (!start) return null\n const date =\n (row.startedAt instanceof Date && !Number.isNaN(row.startedAt.getTime())\n ? formatLocalDate(row.startedAt)\n : null) ?? toDayString(row.date)\n if (!date) return null\n return {\n id: row.id,\n date,\n start,\n end: toClock(row.endedAt),\n durationMinutes: typeof row.durationMinutes === 'number' ? row.durationMinutes : null,\n }\n}\n\nfunction readQuery(params: URLSearchParams): z.infer<typeof querySchema> {\n const raw: Record<string, string> = {}\n for (const key of ['date', 'startedAt', 'endedAt', 'durationMinutes', 'excludeId', 'staffMemberId']) {\n const value = params.get(key)\n if (value !== null && value.trim().length > 0) raw[key] = value.trim()\n }\n return querySchema.parse(raw)\n}\n\nasync function resolveGrantedFeatures(\n container: Awaited<ReturnType<typeof createRequestContainer>>,\n userId: string,\n scope: { tenantId: string; organizationId: string },\n): Promise<string[]> {\n try {\n const rbac = container.resolve('rbacService') as RbacServiceLike | undefined\n if (!rbac?.getGrantedFeatures) return []\n return (await rbac.getGrantedFeatures(userId, scope)) ?? []\n } catch {\n return []\n }\n}\n\nasync function resolveOverlapSettings(\n container: Awaited<ReturnType<typeof createRequestContainer>>,\n tenantId: string,\n): Promise<{ assignmentGraceDays: number | null; warningsEnabled: boolean }> {\n try {\n const configService = container.resolve('moduleConfigService') as ModuleConfigService\n const settings = await readTimeTrackingSettings(configService, { tenantId })\n return {\n assignmentGraceDays: settings.access.assignmentGraceDays,\n warningsEnabled: settings.warnings.overlap,\n }\n } catch {\n // The same fail-safe the grace window uses: an unreadable settings row keeps\n // the advisory warning on, which is what the module default says.\n return { assignmentGraceDays: null, warningsEnabled: true }\n }\n}\n\nasync function loadLabels(\n em: EntityManager,\n scope: { tenantId: string; organizationId: string },\n taskIds: string[],\n projectIds: string[],\n): Promise<{ tasks: Map<string, string>; projects: Map<string, string> }> {\n const tasks = new Map<string, string>()\n const projects = new Map<string, string>()\n if (taskIds.length) {\n const rows = await findWithDecryption(\n em,\n StaffTimeTask,\n { id: { $in: taskIds }, ...scope, deletedAt: null },\n {},\n scope,\n )\n for (const row of rows) tasks.set(row.id, row.title)\n }\n if (projectIds.length) {\n const rows = await findWithDecryption(\n em,\n StaffTimeProject,\n { id: { $in: projectIds }, ...scope, deletedAt: null },\n {},\n scope,\n )\n for (const row of rows) projects.set(row.id, row.name)\n }\n return { tasks, projects }\n}\n\n/**\n * GET /api/staff/timesheets/time-entries/overlaps\n *\n * Advisory-only overlap probe for the time entry form (mockup screen 9). It is\n * read-only and never blocks a save: overlapping time is legitimate in\n * consulting work (a client call while taking notes), so the UI renders the\n * result as a warning with a suggested fix, never as a validation error. Any\n * failure here MUST degrade to \"no warning\" rather than break the form \u2014 the\n * route therefore answers `{ items: [], total: 0 }` for unusable input, a\n * caller without a linked staff member, or a lookup that yields nothing, and\n * the client is expected to ignore non-200 responses.\n *\n * Scoping: rows are always filtered by `tenant_id` + `organization_id` and\n * intersected with `resolveProjectAccess`, so the endpoint cannot be used to\n * probe entries on projects the caller cannot see. Asking about another staff\n * member requires `staff.timesheets.manage_all`; without it the request is\n * silently scoped back to the caller's own member instead of erroring.\n *\n * Query shape: the form calls this on blur, so the lookup reads a three-day\n * window (`date - 1 .. date + 1`) against the\n * `(organization_id, staff_member_id, date, started_at)` index. The adjacent\n * days cover midnight-crossing spans in both directions \u2014 a candidate that ends\n * after midnight, and a stored entry that started the previous evening \u2014 without\n * ever scanning history.\n */\nexport async function GET(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) {\n throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n }\n\n const orgScope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = orgScope?.tenantId ?? auth.tenantId ?? null\n const organizationId = resolveSingleOrganizationIdOrDeny(orgScope, auth) ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, {\n error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.'),\n })\n }\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'GET',\n scope: { container, userId: auth.sub, tenantId, organizationId },\n query: readSearchParamsRecord(req.url),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const query = readQuery(session.searchParams)\n const candidate: OverlapSpan = {\n date: query.date,\n start: toClock(query.startedAt),\n end: toClock(query.endedAt),\n durationMinutes: query.durationMinutes ?? null,\n }\n const derived = deriveInterval({\n start: candidate.start,\n end: candidate.end,\n durationMinutes: candidate.durationMinutes,\n })\n if (!derived.start || derived.durationMinutes === null || derived.durationMinutes <= 0) {\n return session.respond(200, EMPTY_OVERLAP_RESULT)\n }\n\n const scope = { tenantId, organizationId }\n const em = (container.resolve('em') as EntityManager).fork()\n const userId = typeof auth.sub === 'string' ? auth.sub : null\n // Both questions go to the service. Asking one and inferring the other from a\n // grant array is how the two could disagree when the read failed.\n const manageAll = await resolveFeatureAccess(container, userId, [MANAGE_ALL_FEATURE], scope)\n const manageProjects = await resolveFeatureAccess(container, userId, [MANAGE_PROJECTS_FEATURE], scope)\n const canManageAll = manageAll.allowed\n\n const overlapSettings = await resolveOverlapSettings(container, tenantId)\n const access = await resolveProjectAccess({\n em,\n userId,\n tenantId,\n organizationId,\n canManageAll: manageProjects.allowed,\n userFeatures: manageAll.grantedFeatures,\n assignmentGraceDays: overlapSettings.assignmentGraceDays,\n })\n\n const staffMemberId =\n canManageAll && query.staffMemberId ? query.staffMemberId : access.staffMemberId\n if (!staffMemberId) return session.respond(200, EMPTY_OVERLAP_RESULT)\n const isSelfScope = staffMemberId === access.staffMemberId\n\n const where = {\n tenantId,\n organizationId,\n staffMemberId,\n deletedAt: null,\n date: { $gte: shiftDate(query.date, -1), $lte: shiftDate(query.date, 1) },\n } as unknown as FilterQuery<StaffTimeEntry>\n const rows = (await findWithDecryption(\n em,\n StaffTimeEntry,\n where,\n { orderBy: { startedAt: 'asc' }, limit: MAX_WINDOW_ROWS },\n scope,\n )) as unknown as EntryRow[]\n\n const visible = rows.filter(\n (row) =>\n assertProjectAccess(access, row.timeProjectId ?? null) ||\n (isSelfScope && !row.timeProjectId),\n )\n const byId = new Map(visible.map((row) => [row.id, row]))\n const spans = visible\n .map((row) => toRowSpan(row))\n .filter((span): span is OverlapSpan => span !== null)\n\n const candidateSpan: OverlapSpan = {\n date: query.date,\n start: derived.start,\n end: derived.end,\n durationMinutes: derived.durationMinutes,\n }\n const overlaps = findOverlaps(\n candidateSpan,\n spans,\n query.excludeId ? { excludeId: query.excludeId } : undefined,\n )\n const decision = evaluateOverlapPolicies(overlaps, {\n tenantId,\n organizationId,\n candidate: candidateSpan,\n warningsEnabled: overlapSettings.warningsEnabled,\n staffMemberId,\n excludeId: query.excludeId ?? null,\n })\n // The item list is unchanged by the policy: this endpoint answers \"what\n // intersects\", and `decision` answers \"and what should happen\". Collapsing\n // the list when a tenant turns the warning off would make the two\n // indistinguishable from \"nothing intersects\" for every other caller.\n if (!overlaps.length) return session.respond(200, { ...EMPTY_OVERLAP_RESULT, decision })\n\n const taskIds = new Set<string>()\n const projectIds = new Set<string>()\n for (const overlap of overlaps) {\n const row = overlap.id ? byId.get(overlap.id) : undefined\n if (row?.taskId) taskIds.add(row.taskId)\n if (row?.timeProjectId) projectIds.add(row.timeProjectId)\n }\n const labels = await loadLabels(em, scope, [...taskIds], [...projectIds])\n\n const items: OverlapItem[] = []\n for (const overlap of overlaps) {\n const row = overlap.id ? byId.get(overlap.id) : undefined\n if (!row) continue\n const interval = deriveInterval({\n start: overlap.entry.start,\n end: overlap.entry.end,\n durationMinutes: overlap.entry.durationMinutes,\n })\n items.push({\n id: row.id,\n staff_member_id: row.staffMemberId,\n date: overlap.entry.date,\n started_at: interval.start ?? '',\n ended_at: interval.end ?? '',\n duration_minutes: interval.durationMinutes ?? 0,\n overlap_minutes: overlap.overlapMinutes,\n crosses_midnight: interval.crossesMidnight,\n task_id: row.taskId ?? null,\n task_title: row.taskId ? labels.tasks.get(row.taskId) ?? null : null,\n time_project_id: row.timeProjectId ?? null,\n project_name: row.timeProjectId ? labels.projects.get(row.timeProjectId) ?? null : null,\n notes: row.notes ?? null,\n })\n }\n\n return session.respond(200, { items, total: items.length, decision })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: translate('staff.errors.invalid_request', 'Invalid request'), details: err.issues },\n { status: 400 },\n )\n }\n logger.error('staff.timesheets.time-entries.overlaps GET failed', { err })\n return NextResponse.json(\n { error: translate('staff.errors.internal', 'Internal server error') },\n { status: 500 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Advisory time entry overlap check',\n methods: {\n GET: {\n summary: 'List time entries overlapping a candidate span',\n description:\n 'Advisory, read-only lookup used by the time entry form to warn about conflicting entries. Overlapping time is allowed, so the result never blocks a save. Returns an empty list when the supplied time information is insufficient. Checking another staff member requires staff.timesheets.manage_all; without it the request is silently scoped to the caller.',\n query: querySchema,\n responses: [\n { status: 200, description: 'Overlapping entries', schema: overlapsResponseSchema },\n { status: 400, description: 'Invalid query or missing scope', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,6 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
4
4
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
5
5
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
6
|
+
import { resolveSingleOrganizationIdOrDeny } from "@open-mercato/core/modules/directory/utils/organizationScopeFilter";
|
|
6
7
|
import { serializeOperationMetadata } from "@open-mercato/shared/lib/commands/operationMetadata";
|
|
7
8
|
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
8
9
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
@@ -27,7 +28,7 @@ async function buildContext(req) {
|
|
|
27
28
|
container,
|
|
28
29
|
auth,
|
|
29
30
|
organizationScope: scope,
|
|
30
|
-
selectedOrganizationId: scope
|
|
31
|
+
selectedOrganizationId: resolveSingleOrganizationIdOrDeny(scope, auth) ?? null,
|
|
31
32
|
organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),
|
|
32
33
|
request: req
|
|
33
34
|
};
|
|
@@ -35,7 +36,7 @@ async function buildContext(req) {
|
|
|
35
36
|
ctx,
|
|
36
37
|
translate,
|
|
37
38
|
tenantId: scope?.tenantId ?? auth.tenantId ?? null,
|
|
38
|
-
organizationId: scope
|
|
39
|
+
organizationId: resolveSingleOrganizationIdOrDeny(scope, auth) ?? null
|
|
39
40
|
};
|
|
40
41
|
}
|
|
41
42
|
async function POST(req) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/staff/api/timesheets/time-entries/start-timer/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { staffTimeEntryStartTimerSchema, type StaffTimeEntryStartTimerInput } from '../../../../data/validators'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { STAFF_TIME_TRACKING_RESOURCE_KINDS } from '../../../guards'\nimport { runTimesheetInterceptors } from '../../_shared/withTimesheetInterceptors'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{\n ctx: CommandRuntimeContext\n translate: (key: string, fallback?: string) => string\n tenantId: string | null\n organizationId: string | null\n}> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AACnD,SAAS,kCAAkC;AAE3C,SAAS,eAAe,uBAAuB;AAC/C,SAAS,2BAA2B;AACpC,SAAS,+BAA+B;AAExC,SAAS,sCAA0E;AACnF,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,0CAA0C;AACnD,SAAS,0CAA0C;AACnD,SAAS,gCAAgC;AAEzC,MAAM,SAAS,aAAa,OAAO;AAE5B,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,6BAA6B,EAAE;AAC9E;AAEA,eAAe,aACb,KAMC;AACD,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,MAAI,CAAC,KAAM,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,6BAA6B,cAAc,EAAE,CAAC;AACzG,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,SAAS,IAAI,CAAC;AACxF,QAAM,MAA6B;AAAA,IACjC;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB,wBAAwB,OAAO,
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveSingleOrganizationIdOrDeny } from '@open-mercato/core/modules/directory/utils/organizationScopeFilter'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { staffTimeEntryStartTimerSchema, type StaffTimeEntryStartTimerInput } from '../../../../data/validators'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { STAFF_TIME_TRACKING_RESOURCE_KINDS } from '../../../guards'\nimport { runTimesheetInterceptors } from '../../_shared/withTimesheetInterceptors'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{\n ctx: CommandRuntimeContext\n translate: (key: string, fallback?: string) => string\n tenantId: string | null\n organizationId: string | null\n}> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: resolveSingleOrganizationIdOrDeny(scope, auth) ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return {\n ctx,\n translate,\n tenantId: scope?.tenantId ?? auth.tenantId ?? null,\n organizationId: resolveSingleOrganizationIdOrDeny(scope, auth) ?? null,\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, translate, tenantId, organizationId } = await buildContext(req)\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'POST',\n scope: { container: ctx.container, userId: ctx.auth?.sub, tenantId, organizationId },\n body: await readJsonSafe<Record<string, unknown>>(req, {}),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const input = parseScopedCommandInput(staffTimeEntryStartTimerSchema, session.body, ctx, translate)\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<StaffTimeEntryStartTimerInput, { timeEntryId: string }>(\n 'staff.timesheets.time_entries.start_timer',\n { input, ctx },\n )\n const response = await session.respond(201, { ok: true, id: result?.timeEntryId ?? null })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry,\n resourceId: logEntry.resourceId ?? result?.timeEntryId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.timesheets.time-entries.start-timer failed', { err })\n return NextResponse.json(\n { error: translate('staff.timesheets.errors.timerStart', 'Failed to start timer.') },\n { status: 400 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Start a timesheet timer',\n methods: {\n POST: {\n summary: 'Start a timesheet timer',\n description:\n 'Atomically creates a timer-sourced time entry and starts it (sets startedAt and creates the initial work segment) in a single transaction, so a partial failure cannot leave an orphaned, unstarted entry. An optional `taskId` is validated and stored by the same write, so starting a timer from a board card or the task drawer takes one request instead of a start followed by a link that can fail; when only `taskId` is sent, the project follows from the task.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTimeEntryStartTimerSchema,\n },\n responses: [\n {\n status: 201,\n description: 'Timer started',\n schema: z.object({ ok: z.literal(true), id: z.string().uuid().nullable() }),\n },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n {\n status: 409,\n description: 'Another timer is already running for this staff member',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 422,\n description:\n 'Referenced time project or task not found, out of scope, or the task belongs to another project',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,6 +4,7 @@ import { z } from "zod";
|
|
|
4
4
|
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
5
5
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
6
6
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
7
|
+
import { resolveSingleOrganizationIdOrDeny } from "@open-mercato/core/modules/directory/utils/organizationScopeFilter";
|
|
7
8
|
import { CrudHttpError, forbidden, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
8
9
|
import { runRouteMutationGuards } from "@open-mercato/shared/lib/crud/route-mutation-guard";
|
|
9
10
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
@@ -44,7 +45,7 @@ async function POST(req) {
|
|
|
44
45
|
}
|
|
45
46
|
const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
|
|
46
47
|
const tenantId = scope?.tenantId ?? auth.tenantId ?? null;
|
|
47
|
-
const organizationId = scope
|
|
48
|
+
const organizationId = resolveSingleOrganizationIdOrDeny(scope, auth) ?? null;
|
|
48
49
|
if (!tenantId || !organizationId) {
|
|
49
50
|
throw new CrudHttpError(400, {
|
|
50
51
|
error: translate("staff.errors.missingScope", "Missing tenant or organization scope.")
|
|
@@ -93,7 +94,7 @@ async function POST(req) {
|
|
|
93
94
|
container,
|
|
94
95
|
auth,
|
|
95
96
|
organizationScope: scope,
|
|
96
|
-
selectedOrganizationId: scope
|
|
97
|
+
selectedOrganizationId: resolveSingleOrganizationIdOrDeny(scope, auth) ?? null,
|
|
97
98
|
organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),
|
|
98
99
|
request: req
|
|
99
100
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../../src/modules/staff/api/timesheets/time-projects/%5Bid%5D/change-currency/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { resolveFeatureAccess } from '../../../../../lib/time-tracking/featureAccess'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError, forbidden, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { runRouteMutationGuards } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { emitStaffEvent } from '../../../../../events'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { staffTimeProjectChangeCurrencySchema } from '../../../../../data/validators'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { STAFF_TIME_TRACKING_RESOURCE_KINDS } from '../../../../guards'\nimport { runTimesheetInterceptors } from '../../../_shared/withTimesheetInterceptors'\nimport {\n staffTimeProjectChangeCurrencyCommandId,\n type StaffTimeProjectChangeCurrencyCommandInput,\n type StaffTimeProjectChangeCurrencyResult,\n} from '../../../../../commands/timesheets-projects'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/time-projects/change-currency' })\n\nconst MANAGE_FEATURE = 'staff.timesheets.projects.manage'\nconst RESOURCE_KIND = STAFF_TIME_TRACKING_RESOURCE_KINDS.timeProject\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: [MANAGE_FEATURE] },\n}\n\nfunction extractProjectIdFromUrl(request?: Request): string | null {\n if (!request?.url) return null\n try {\n const url = new URL(request.url)\n const match = url.pathname.match(/\\/time-projects\\/([^/]+)\\/change-currency/)\n return match?.[1] ?? null\n } catch {\n return null\n }\n}\n\n/**\n * D-3: relabels a project's currency without converting a single historical\n * amount. The `acknowledged: true` literal in the body is deliberate friction \u2014\n * the client must have shown the non-conversion warning \u2014 and the request is\n * refused with `409 project_has_locked_entries` while any of the project's\n * entries is frozen inside a closed report.\n */\nexport async function POST(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) {\n throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, {\n error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.'),\n })\n }\n\n const timeProjectId = extractProjectIdFromUrl(req)\n if (!timeProjectId) {\n throw new CrudHttpError(400, {\n error: translate('staff.timesheets.errors.projectRequired', 'Time project id is required.'),\n })\n }\n\n const actorId = auth.sub ?? ''\n // One lookup through the module's single RBAC authority, answering both\n // questions this route asks: may the caller manage projects, and what grant\n // list do the interceptors and the mutation-guard registry gate on. The\n // decision is checked unconditionally and fails closed; the list is plumbing\n // and is empty \u2014 never absent \u2014 when RBAC could not produce it.\n const access = await resolveFeatureAccess(container, actorId, [MANAGE_FEATURE], { tenantId, organizationId })\n if (!access.allowed) {\n throw forbidden(translate('staff.errors.forbidden', 'Forbidden'))\n }\n const grantedFeatures = access.grantedFeatures\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'POST',\n scope: { container, userId: actorId, tenantId, organizationId, userFeatures: grantedFeatures },\n body: await readJsonSafe<Record<string, unknown>>(req, {}),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const parsed = staffTimeProjectChangeCurrencySchema.parse(session.body)\n\n const guardResult = await runRouteMutationGuards({\n container,\n req,\n auth: {\n userId: actorId,\n tenantId,\n organizationId,\n userFeatures: grantedFeatures,\n },\n input: {\n resourceKind: RESOURCE_KIND,\n resourceId: timeProjectId,\n operation: 'update',\n mutationPayload: parsed,\n },\n })\n if (!guardResult.ok) return guardResult.response\n\n const effective = guardResult.modifiedPayload\n ? staffTimeProjectChangeCurrencySchema.parse({ ...parsed, ...guardResult.modifiedPayload })\n : parsed\n\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<\n StaffTimeProjectChangeCurrencyCommandInput,\n StaffTimeProjectChangeCurrencyResult\n >(staffTimeProjectChangeCurrencyCommandId, {\n input: { ...effective, id: timeProjectId },\n ctx,\n })\n\n await guardResult.runAfterSuccess()\n\n void emitStaffEvent('staff.timesheets.time_project.currency_changed', {\n id: result?.timeProjectId ?? timeProjectId,\n tenantId,\n organizationId,\n currencyCode: result?.currencyCode ?? effective.currencyCode,\n previousCurrencyCode: result?.previousCurrencyCode ?? null,\n converted: false,\n }, { persistent: true }).catch((err) => {\n logger.error('staff.timesheets emit time_project.currency_changed failed', { err })\n })\n\n const response = await session.respond(200, {\n id: result?.timeProjectId ?? timeProjectId,\n currencyCode: result?.currencyCode ?? effective.currencyCode,\n previousCurrencyCode: result?.previousCurrencyCode ?? null,\n converted: false,\n })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? RESOURCE_KIND,\n resourceId: logEntry.resourceId ?? timeProjectId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: translate('staff.errors.invalid_request', 'Invalid request'), details: err.issues },\n { status: 400 },\n )\n }\n logger.error('staff.timesheets.time-projects.change-currency failed', { err })\n return NextResponse.json(\n { error: translate('staff.errors.internal', 'Internal server error') },\n { status: 500 },\n )\n }\n}\n\nconst blockingReportSchema = z.object({\n id: z.string().uuid(),\n reference: z.string().nullable(),\n title: z.string().nullable(),\n})\n\nconst changeCurrencyResponseSchema = z.object({\n id: z.string().uuid(),\n currencyCode: z.string(),\n previousCurrencyCode: z.string().nullable(),\n converted: z.literal(false),\n})\n\nconst lockedConflictSchema = z.object({\n code: z.literal('project_has_locked_entries'),\n error: z.string(),\n lockedEntryCount: z.number().int(),\n lockedReports: z.array(blockingReportSchema),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Change a time project currency',\n methods: {\n POST: {\n summary: 'Change a time project currency',\n description:\n 'Relabels the project currency without converting any historical amount. Requires an explicit `acknowledged: true` and is refused while any of the project entries is locked into a closed report.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTimeProjectChangeCurrencySchema,\n },\n responses: [\n { status: 200, description: 'Currency relabelled', schema: changeCurrencyResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid body, or the change was not acknowledged' },\n { status: 401, description: 'Unauthorized' },\n { status: 403, description: 'Missing staff.timesheets.projects.manage' },\n { status: 404, description: 'Time project not found' },\n {\n status: 409,\n description: 'Project has entries locked into a closed report',\n schema: lockedConflictSchema,\n },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,4BAA4B;AACrC,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AACnD,SAAS,eAAe,WAAW,uBAAuB;AAC1D,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AACpC,SAAS,kCAAkC;AAE3C,SAAS,0CAA0C;AACnD,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAE/B,SAAS,4CAA4C;AACrD,SAAS,oBAAoB;AAC7B,SAAS,0CAA0C;AACnD,SAAS,gCAAgC;AACzC;AAAA,EACE;AAAA,OAGK;AAEP,MAAM,SAAS,aAAa,OAAO,EAAE,MAAM,EAAE,WAAW,+CAA+C,CAAC;AAExG,MAAM,iBAAiB;AACvB,MAAM,gBAAgB,mCAAmC;AAElD,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,cAAc,EAAE;AAC/D;AAEA,SAAS,wBAAwB,SAAkC;AACjE,MAAI,CAAC,SAAS,IAAK,QAAO;AAC1B,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAC/B,UAAM,QAAQ,IAAI,SAAS,MAAM,2CAA2C;AAC5E,WAAO,QAAQ,CAAC,KAAK;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASA,eAAsB,KAAK,KAAc;AACvC,MAAI;AACF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,UAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,6BAA6B,cAAc,EAAE,CAAC;AAAA,IAChG;AAEA,UAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,SAAS,IAAI,CAAC;AACxF,UAAM,WAAW,OAAO,YAAY,KAAK,YAAY;AACrD,UAAM,iBAAiB,OAAO,
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { resolveFeatureAccess } from '../../../../../lib/time-tracking/featureAccess'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { resolveSingleOrganizationIdOrDeny } from '@open-mercato/core/modules/directory/utils/organizationScopeFilter'\nimport { CrudHttpError, forbidden, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { runRouteMutationGuards } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { emitStaffEvent } from '../../../../../events'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { staffTimeProjectChangeCurrencySchema } from '../../../../../data/validators'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { STAFF_TIME_TRACKING_RESOURCE_KINDS } from '../../../../guards'\nimport { runTimesheetInterceptors } from '../../../_shared/withTimesheetInterceptors'\nimport {\n staffTimeProjectChangeCurrencyCommandId,\n type StaffTimeProjectChangeCurrencyCommandInput,\n type StaffTimeProjectChangeCurrencyResult,\n} from '../../../../../commands/timesheets-projects'\n\nconst logger = createLogger('staff').child({ component: 'api/timesheets/time-projects/change-currency' })\n\nconst MANAGE_FEATURE = 'staff.timesheets.projects.manage'\nconst RESOURCE_KIND = STAFF_TIME_TRACKING_RESOURCE_KINDS.timeProject\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: [MANAGE_FEATURE] },\n}\n\nfunction extractProjectIdFromUrl(request?: Request): string | null {\n if (!request?.url) return null\n try {\n const url = new URL(request.url)\n const match = url.pathname.match(/\\/time-projects\\/([^/]+)\\/change-currency/)\n return match?.[1] ?? null\n } catch {\n return null\n }\n}\n\n/**\n * D-3: relabels a project's currency without converting a single historical\n * amount. The `acknowledged: true` literal in the body is deliberate friction \u2014\n * the client must have shown the non-conversion warning \u2014 and the request is\n * refused with `409 project_has_locked_entries` while any of the project's\n * entries is frozen inside a closed report.\n */\nexport async function POST(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) {\n throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = resolveSingleOrganizationIdOrDeny(scope, auth) ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, {\n error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.'),\n })\n }\n\n const timeProjectId = extractProjectIdFromUrl(req)\n if (!timeProjectId) {\n throw new CrudHttpError(400, {\n error: translate('staff.timesheets.errors.projectRequired', 'Time project id is required.'),\n })\n }\n\n const actorId = auth.sub ?? ''\n // One lookup through the module's single RBAC authority, answering both\n // questions this route asks: may the caller manage projects, and what grant\n // list do the interceptors and the mutation-guard registry gate on. The\n // decision is checked unconditionally and fails closed; the list is plumbing\n // and is empty \u2014 never absent \u2014 when RBAC could not produce it.\n const access = await resolveFeatureAccess(container, actorId, [MANAGE_FEATURE], { tenantId, organizationId })\n if (!access.allowed) {\n throw forbidden(translate('staff.errors.forbidden', 'Forbidden'))\n }\n const grantedFeatures = access.grantedFeatures\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'POST',\n scope: { container, userId: actorId, tenantId, organizationId, userFeatures: grantedFeatures },\n body: await readJsonSafe<Record<string, unknown>>(req, {}),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const parsed = staffTimeProjectChangeCurrencySchema.parse(session.body)\n\n const guardResult = await runRouteMutationGuards({\n container,\n req,\n auth: {\n userId: actorId,\n tenantId,\n organizationId,\n userFeatures: grantedFeatures,\n },\n input: {\n resourceKind: RESOURCE_KIND,\n resourceId: timeProjectId,\n operation: 'update',\n mutationPayload: parsed,\n },\n })\n if (!guardResult.ok) return guardResult.response\n\n const effective = guardResult.modifiedPayload\n ? staffTimeProjectChangeCurrencySchema.parse({ ...parsed, ...guardResult.modifiedPayload })\n : parsed\n\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: resolveSingleOrganizationIdOrDeny(scope, auth) ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n\n const commandBus = container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<\n StaffTimeProjectChangeCurrencyCommandInput,\n StaffTimeProjectChangeCurrencyResult\n >(staffTimeProjectChangeCurrencyCommandId, {\n input: { ...effective, id: timeProjectId },\n ctx,\n })\n\n await guardResult.runAfterSuccess()\n\n void emitStaffEvent('staff.timesheets.time_project.currency_changed', {\n id: result?.timeProjectId ?? timeProjectId,\n tenantId,\n organizationId,\n currencyCode: result?.currencyCode ?? effective.currencyCode,\n previousCurrencyCode: result?.previousCurrencyCode ?? null,\n converted: false,\n }, { persistent: true }).catch((err) => {\n logger.error('staff.timesheets emit time_project.currency_changed failed', { err })\n })\n\n const response = await session.respond(200, {\n id: result?.timeProjectId ?? timeProjectId,\n currencyCode: result?.currencyCode ?? effective.currencyCode,\n previousCurrencyCode: result?.previousCurrencyCode ?? null,\n converted: false,\n })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? RESOURCE_KIND,\n resourceId: logEntry.resourceId ?? timeProjectId,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n if (err instanceof z.ZodError) {\n return NextResponse.json(\n { error: translate('staff.errors.invalid_request', 'Invalid request'), details: err.issues },\n { status: 400 },\n )\n }\n logger.error('staff.timesheets.time-projects.change-currency failed', { err })\n return NextResponse.json(\n { error: translate('staff.errors.internal', 'Internal server error') },\n { status: 500 },\n )\n }\n}\n\nconst blockingReportSchema = z.object({\n id: z.string().uuid(),\n reference: z.string().nullable(),\n title: z.string().nullable(),\n})\n\nconst changeCurrencyResponseSchema = z.object({\n id: z.string().uuid(),\n currencyCode: z.string(),\n previousCurrencyCode: z.string().nullable(),\n converted: z.literal(false),\n})\n\nconst lockedConflictSchema = z.object({\n code: z.literal('project_has_locked_entries'),\n error: z.string(),\n lockedEntryCount: z.number().int(),\n lockedReports: z.array(blockingReportSchema),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Change a time project currency',\n methods: {\n POST: {\n summary: 'Change a time project currency',\n description:\n 'Relabels the project currency without converting any historical amount. Requires an explicit `acknowledged: true` and is refused while any of the project entries is locked into a closed report.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTimeProjectChangeCurrencySchema,\n },\n responses: [\n { status: 200, description: 'Currency relabelled', schema: changeCurrencyResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid body, or the change was not acknowledged' },\n { status: 401, description: 'Unauthorized' },\n { status: 403, description: 'Missing staff.timesheets.projects.manage' },\n { status: 404, description: 'Time project not found' },\n {\n status: 409,\n description: 'Project has entries locked into a closed report',\n schema: lockedConflictSchema,\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -9,6 +9,7 @@ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find"
|
|
|
9
9
|
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
10
10
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
11
11
|
import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
12
|
+
import { resolveSingleOrganizationIdOrDeny } from "@open-mercato/core/modules/directory/utils/organizationScopeFilter";
|
|
12
13
|
import { isIdsParamProvided, parseIdsParam } from "@open-mercato/shared/lib/crud/ids";
|
|
13
14
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
14
15
|
import { StaffTimeProject, StaffTimeProjectMember, StaffTeamMember } from "../../../data/entities.js";
|
|
@@ -356,7 +357,7 @@ async function resolveProjectAccessDenial(req) {
|
|
|
356
357
|
if (!auth) return null;
|
|
357
358
|
const orgScope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
|
|
358
359
|
const tenantId = orgScope?.tenantId ?? auth.tenantId ?? null;
|
|
359
|
-
const organizationId = orgScope
|
|
360
|
+
const organizationId = resolveSingleOrganizationIdOrDeny(orgScope, auth) ?? null;
|
|
360
361
|
if (!tenantId || !organizationId) return null;
|
|
361
362
|
const access = await resolveRequestProjectAccess(
|
|
362
363
|
req,
|