@baruk/core 0.4.2 → 0.5.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/.baruk-provenance.json +4 -4
- package/CHANGELOG.md +42 -0
- package/dist/audit/repository.d.ts +33 -1
- package/dist/audit/repository.d.ts.map +1 -1
- package/dist/audit/repository.js +41 -7
- package/dist/audit/repository.js.map +1 -1
- package/dist/auth/service.d.ts +21 -0
- package/dist/auth/service.d.ts.map +1 -1
- package/dist/auth/service.js +46 -1
- package/dist/auth/service.js.map +1 -1
- package/dist/billing/ar-repository.d.ts +41 -1
- package/dist/billing/ar-repository.d.ts.map +1 -1
- package/dist/billing/ar-repository.js +82 -6
- package/dist/billing/ar-repository.js.map +1 -1
- package/dist/billing/provider-account.d.ts +38 -0
- package/dist/billing/provider-account.d.ts.map +1 -1
- package/dist/billing/provider-account.js +58 -2
- package/dist/billing/provider-account.js.map +1 -1
- package/dist/billing/repository.d.ts +44 -0
- package/dist/billing/repository.d.ts.map +1 -1
- package/dist/billing/repository.js +49 -5
- package/dist/billing/repository.js.map +1 -1
- package/dist/cjs/audit/repository.js +41 -7
- package/dist/cjs/audit/repository.js.map +1 -1
- package/dist/cjs/auth/service.js +46 -1
- package/dist/cjs/auth/service.js.map +1 -1
- package/dist/cjs/billing/ar-repository.js +82 -6
- package/dist/cjs/billing/ar-repository.js.map +1 -1
- package/dist/cjs/billing/provider-account.js +60 -1
- package/dist/cjs/billing/provider-account.js.map +1 -1
- package/dist/cjs/billing/repository.js +51 -5
- package/dist/cjs/billing/repository.js.map +1 -1
- package/dist/cjs/clients/ibge.js +103 -0
- package/dist/cjs/clients/ibge.js.map +1 -0
- package/dist/cjs/clients/repository.js +186 -9
- package/dist/cjs/clients/repository.js.map +1 -1
- package/dist/cjs/collection/branding.js +190 -0
- package/dist/cjs/collection/branding.js.map +1 -1
- package/dist/cjs/collection/clearance-store.js +74 -0
- package/dist/cjs/collection/clearance-store.js.map +1 -0
- package/dist/cjs/collection/communication-runtime.js +65 -2
- package/dist/cjs/collection/communication-runtime.js.map +1 -1
- package/dist/cjs/collection/engine.js +20 -0
- package/dist/cjs/collection/engine.js.map +1 -1
- package/dist/cjs/collection/policy.js +29 -1
- package/dist/cjs/collection/policy.js.map +1 -1
- package/dist/cjs/communication/clearance.js +18 -0
- package/dist/cjs/communication/clearance.js.map +1 -0
- package/dist/cjs/communication/dry-run-scenario.js +5 -0
- package/dist/cjs/communication/dry-run-scenario.js.map +1 -1
- package/dist/cjs/communication/engine.js +12 -0
- package/dist/cjs/communication/engine.js.map +1 -1
- package/dist/cjs/communication/policy.js.map +1 -1
- package/dist/cjs/competence/business-days.js +155 -0
- package/dist/cjs/competence/business-days.js.map +1 -0
- package/dist/cjs/competence/catalog-validation.js +174 -0
- package/dist/cjs/competence/catalog-validation.js.map +1 -0
- package/dist/cjs/competence/obligation-preview.js +190 -0
- package/dist/cjs/competence/obligation-preview.js.map +1 -0
- package/dist/cjs/competence/repository.js +330 -17
- package/dist/cjs/competence/repository.js.map +1 -1
- package/dist/cjs/dashboard/pilot-panel.js +90 -0
- package/dist/cjs/dashboard/pilot-panel.js.map +1 -0
- package/dist/cjs/dashboard/repository.js +11 -1
- package/dist/cjs/dashboard/repository.js.map +1 -1
- package/dist/cjs/db/client.js +17 -11
- package/dist/cjs/db/client.js.map +1 -1
- package/dist/cjs/db/provision-e2e-identity.js +111 -0
- package/dist/cjs/db/provision-e2e-identity.js.map +1 -0
- package/dist/cjs/fiscal/applicability.js +164 -0
- package/dist/cjs/fiscal/applicability.js.map +1 -0
- package/dist/cjs/fiscal/conditions.js +198 -0
- package/dist/cjs/fiscal/conditions.js.map +1 -0
- package/dist/cjs/fiscal/contracts/nfse-adn-contract.js +390 -0
- package/dist/cjs/fiscal/contracts/nfse-adn-contract.js.map +1 -0
- package/dist/cjs/fiscal/contracts/nfse-adn-endpoint.js +108 -0
- package/dist/cjs/fiscal/contracts/nfse-adn-endpoint.js.map +1 -0
- package/dist/cjs/fiscal/contracts/nfse-adn-envelope.js +220 -0
- package/dist/cjs/fiscal/contracts/nfse-adn-envelope.js.map +1 -0
- package/dist/cjs/fiscal/coverage/model.js +127 -0
- package/dist/cjs/fiscal/coverage/model.js.map +1 -0
- package/dist/cjs/fiscal/coverage/requests.js +99 -0
- package/dist/cjs/fiscal/coverage/requests.js.map +1 -0
- package/dist/cjs/fiscal/credentials/material.js +168 -0
- package/dist/cjs/fiscal/credentials/material.js.map +1 -0
- package/dist/cjs/fiscal/credentials/readiness.js +163 -0
- package/dist/cjs/fiscal/credentials/readiness.js.map +1 -0
- package/dist/cjs/fiscal/eligibility.js +199 -0
- package/dist/cjs/fiscal/eligibility.js.map +1 -0
- package/dist/cjs/fiscal/ingestion/__fixtures__/nfse.js +128 -0
- package/dist/cjs/fiscal/ingestion/__fixtures__/nfse.js.map +1 -0
- package/dist/cjs/fiscal/ingestion/adn-transport.js +229 -0
- package/dist/cjs/fiscal/ingestion/adn-transport.js.map +1 -0
- package/dist/cjs/fiscal/ingestion/direction.js +36 -0
- package/dist/cjs/fiscal/ingestion/direction.js.map +1 -0
- package/dist/cjs/fiscal/ingestion/nfse-adn.js +228 -0
- package/dist/cjs/fiscal/ingestion/nfse-adn.js.map +1 -0
- package/dist/cjs/fiscal/ingestion/pipeline.js +704 -0
- package/dist/cjs/fiscal/ingestion/pipeline.js.map +1 -0
- package/dist/cjs/fiscal/ingestion/provider.js +51 -0
- package/dist/cjs/fiscal/ingestion/provider.js.map +1 -0
- package/dist/cjs/fiscal/ingestion/repository.js +132 -0
- package/dist/cjs/fiscal/ingestion/repository.js.map +1 -0
- package/dist/cjs/fiscal/ingestion/synthetic-provider.js +103 -0
- package/dist/cjs/fiscal/ingestion/synthetic-provider.js.map +1 -0
- package/dist/cjs/fiscal/ingestion/synthetic-transport-types.js +13 -0
- package/dist/cjs/fiscal/ingestion/synthetic-transport-types.js.map +1 -0
- package/dist/cjs/fiscal/ingestion/synthetic-transport.js +61 -0
- package/dist/cjs/fiscal/ingestion/synthetic-transport.js.map +1 -0
- package/dist/cjs/fiscal/ingestion/xml-reader.js +121 -0
- package/dist/cjs/fiscal/ingestion/xml-reader.js.map +1 -0
- package/dist/cjs/fiscal/repository.js +470 -0
- package/dist/cjs/fiscal/repository.js.map +1 -0
- package/dist/cjs/fiscal/sync/circuit.js +124 -0
- package/dist/cjs/fiscal/sync/circuit.js.map +1 -0
- package/dist/cjs/fiscal/sync/intervention.js +140 -0
- package/dist/cjs/fiscal/sync/intervention.js.map +1 -0
- package/dist/cjs/fiscal/sync/lease.js +146 -0
- package/dist/cjs/fiscal/sync/lease.js.map +1 -0
- package/dist/cjs/fiscal/sync/observability.js +244 -0
- package/dist/cjs/fiscal/sync/observability.js.map +1 -0
- package/dist/cjs/fiscal/sync/orchestrator.js +490 -0
- package/dist/cjs/fiscal/sync/orchestrator.js.map +1 -0
- package/dist/cjs/fiscal/sync/policy.js +143 -0
- package/dist/cjs/fiscal/sync/policy.js.map +1 -0
- package/dist/cjs/fiscal/sync/reprocess.js +241 -0
- package/dist/cjs/fiscal/sync/reprocess.js.map +1 -0
- package/dist/cjs/fiscal/sync/retry.js +183 -0
- package/dist/cjs/fiscal/sync/retry.js.map +1 -0
- package/dist/cjs/fiscal/sync/secret-boundary.js +123 -0
- package/dist/cjs/fiscal/sync/secret-boundary.js.map +1 -0
- package/dist/cjs/fiscal/transport/https.js +309 -0
- package/dist/cjs/fiscal/transport/https.js.map +1 -0
- package/dist/cjs/fiscal/transport/synthetic-tls.js +328 -0
- package/dist/cjs/fiscal/transport/synthetic-tls.js.map +1 -0
- package/dist/cjs/fiscal/transport/types.js +93 -0
- package/dist/cjs/fiscal/transport/types.js.map +1 -0
- package/dist/cjs/import/br-documents.js +85 -0
- package/dist/cjs/import/br-documents.js.map +1 -0
- package/dist/cjs/import/clients-import.js +347 -40
- package/dist/cjs/import/clients-import.js.map +1 -1
- package/dist/cjs/index.js +316 -11
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/legal/documents.js +100 -15
- package/dist/cjs/legal/documents.js.map +1 -1
- package/dist/cjs/legal/index.js +3 -1
- package/dist/cjs/legal/index.js.map +1 -1
- package/dist/cjs/legal/types.js.map +1 -1
- package/dist/cjs/onboarding/persistence.js +4 -4
- package/dist/cjs/onboarding/persistence.js.map +1 -1
- package/dist/cjs/pilot/go-live-checklist.js +212 -0
- package/dist/cjs/pilot/go-live-checklist.js.map +1 -0
- package/dist/cjs/pilot/milestone.js +79 -0
- package/dist/cjs/pilot/milestone.js.map +1 -0
- package/dist/cjs/platform/capabilities.js +144 -0
- package/dist/cjs/platform/capabilities.js.map +1 -0
- package/dist/cjs/platform/job-contract.js +411 -0
- package/dist/cjs/platform/job-contract.js.map +1 -0
- package/dist/cjs/rbac/catalog.js +34 -0
- package/dist/cjs/rbac/catalog.js.map +1 -1
- package/dist/cjs/subscription-billing/index.js +2 -1
- package/dist/cjs/subscription-billing/index.js.map +1 -1
- package/dist/cjs/subscription-billing/service.js +153 -77
- package/dist/cjs/subscription-billing/service.js.map +1 -1
- package/dist/cjs/tenancy/features.js +131 -0
- package/dist/cjs/tenancy/features.js.map +1 -0
- package/dist/cjs/tenancy/settings.js +224 -0
- package/dist/cjs/tenancy/settings.js.map +1 -0
- package/dist/cjs/users/repository.js +24 -1
- package/dist/cjs/users/repository.js.map +1 -1
- package/dist/clients/ibge.d.ts +17 -0
- package/dist/clients/ibge.d.ts.map +1 -0
- package/dist/clients/ibge.js +98 -0
- package/dist/clients/ibge.js.map +1 -0
- package/dist/clients/repository.d.ts +154 -0
- package/dist/clients/repository.d.ts.map +1 -1
- package/dist/clients/repository.js +182 -9
- package/dist/clients/repository.js.map +1 -1
- package/dist/collection/branding.d.ts +65 -0
- package/dist/collection/branding.d.ts.map +1 -1
- package/dist/collection/branding.js +186 -0
- package/dist/collection/branding.js.map +1 -1
- package/dist/collection/clearance-store.d.ts +39 -0
- package/dist/collection/clearance-store.d.ts.map +1 -0
- package/dist/collection/clearance-store.js +69 -0
- package/dist/collection/clearance-store.js.map +1 -0
- package/dist/collection/communication-runtime.d.ts +37 -0
- package/dist/collection/communication-runtime.d.ts.map +1 -1
- package/dist/collection/communication-runtime.js +63 -1
- package/dist/collection/communication-runtime.js.map +1 -1
- package/dist/collection/engine.d.ts +23 -0
- package/dist/collection/engine.d.ts.map +1 -1
- package/dist/collection/engine.js +20 -0
- package/dist/collection/engine.js.map +1 -1
- package/dist/collection/policy.d.ts +25 -0
- package/dist/collection/policy.d.ts.map +1 -1
- package/dist/collection/policy.js +27 -0
- package/dist/collection/policy.js.map +1 -1
- package/dist/communication/clearance.d.ts +53 -0
- package/dist/communication/clearance.d.ts.map +1 -0
- package/dist/communication/clearance.js +15 -0
- package/dist/communication/clearance.js.map +1 -0
- package/dist/communication/dry-run-scenario.d.ts.map +1 -1
- package/dist/communication/dry-run-scenario.js +5 -0
- package/dist/communication/dry-run-scenario.js.map +1 -1
- package/dist/communication/engine.d.ts +10 -0
- package/dist/communication/engine.d.ts.map +1 -1
- package/dist/communication/engine.js +12 -0
- package/dist/communication/engine.js.map +1 -1
- package/dist/communication/policy.d.ts +16 -0
- package/dist/communication/policy.d.ts.map +1 -1
- package/dist/communication/policy.js.map +1 -1
- package/dist/competence/business-days.d.ts +51 -0
- package/dist/competence/business-days.d.ts.map +1 -0
- package/dist/competence/business-days.js +145 -0
- package/dist/competence/business-days.js.map +1 -0
- package/dist/competence/catalog-validation.d.ts +86 -0
- package/dist/competence/catalog-validation.d.ts.map +1 -0
- package/dist/competence/catalog-validation.js +168 -0
- package/dist/competence/catalog-validation.js.map +1 -0
- package/dist/competence/obligation-preview.d.ts +103 -0
- package/dist/competence/obligation-preview.d.ts.map +1 -0
- package/dist/competence/obligation-preview.js +184 -0
- package/dist/competence/obligation-preview.js.map +1 -0
- package/dist/competence/repository.d.ts +139 -1
- package/dist/competence/repository.d.ts.map +1 -1
- package/dist/competence/repository.js +325 -17
- package/dist/competence/repository.js.map +1 -1
- package/dist/dashboard/pilot-panel.d.ts +100 -0
- package/dist/dashboard/pilot-panel.d.ts.map +1 -0
- package/dist/dashboard/pilot-panel.js +87 -0
- package/dist/dashboard/pilot-panel.js.map +1 -0
- package/dist/dashboard/repository.d.ts.map +1 -1
- package/dist/dashboard/repository.js +11 -1
- package/dist/dashboard/repository.js.map +1 -1
- package/dist/db/client.d.ts +45 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +16 -11
- package/dist/db/client.js.map +1 -1
- package/dist/db/migrate.d.ts +36 -1
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +199 -32
- package/dist/db/migrate.js.map +1 -1
- package/dist/db/provision-e2e-identity.d.ts +2 -0
- package/dist/db/provision-e2e-identity.d.ts.map +1 -0
- package/dist/db/provision-e2e-identity.js +109 -0
- package/dist/db/provision-e2e-identity.js.map +1 -0
- package/dist/fiscal/applicability.d.ts +73 -0
- package/dist/fiscal/applicability.d.ts.map +1 -0
- package/dist/fiscal/applicability.js +160 -0
- package/dist/fiscal/applicability.js.map +1 -0
- package/dist/fiscal/conditions.d.ts +99 -0
- package/dist/fiscal/conditions.d.ts.map +1 -0
- package/dist/fiscal/conditions.js +190 -0
- package/dist/fiscal/conditions.js.map +1 -0
- package/dist/fiscal/contracts/nfse-adn-contract.d.ts +315 -0
- package/dist/fiscal/contracts/nfse-adn-contract.d.ts.map +1 -0
- package/dist/fiscal/contracts/nfse-adn-contract.js +386 -0
- package/dist/fiscal/contracts/nfse-adn-contract.js.map +1 -0
- package/dist/fiscal/contracts/nfse-adn-endpoint.d.ts +13 -0
- package/dist/fiscal/contracts/nfse-adn-endpoint.d.ts.map +1 -0
- package/dist/fiscal/contracts/nfse-adn-endpoint.js +103 -0
- package/dist/fiscal/contracts/nfse-adn-endpoint.js.map +1 -0
- package/dist/fiscal/contracts/nfse-adn-envelope.d.ts +77 -0
- package/dist/fiscal/contracts/nfse-adn-envelope.d.ts.map +1 -0
- package/dist/fiscal/contracts/nfse-adn-envelope.js +215 -0
- package/dist/fiscal/contracts/nfse-adn-envelope.js.map +1 -0
- package/dist/fiscal/coverage/model.d.ts +86 -0
- package/dist/fiscal/coverage/model.d.ts.map +1 -0
- package/dist/fiscal/coverage/model.js +120 -0
- package/dist/fiscal/coverage/model.js.map +1 -0
- package/dist/fiscal/coverage/requests.d.ts +70 -0
- package/dist/fiscal/coverage/requests.d.ts.map +1 -0
- package/dist/fiscal/coverage/requests.js +95 -0
- package/dist/fiscal/coverage/requests.js.map +1 -0
- package/dist/fiscal/credentials/material.d.ts +81 -0
- package/dist/fiscal/credentials/material.d.ts.map +1 -0
- package/dist/fiscal/credentials/material.js +164 -0
- package/dist/fiscal/credentials/material.js.map +1 -0
- package/dist/fiscal/credentials/readiness.d.ts +92 -0
- package/dist/fiscal/credentials/readiness.d.ts.map +1 -0
- package/dist/fiscal/credentials/readiness.js +159 -0
- package/dist/fiscal/credentials/readiness.js.map +1 -0
- package/dist/fiscal/eligibility.d.ts +106 -0
- package/dist/fiscal/eligibility.d.ts.map +1 -0
- package/dist/fiscal/eligibility.js +194 -0
- package/dist/fiscal/eligibility.js.map +1 -0
- package/dist/fiscal/ingestion/__fixtures__/nfse.d.ts +57 -0
- package/dist/fiscal/ingestion/__fixtures__/nfse.d.ts.map +1 -0
- package/dist/fiscal/ingestion/__fixtures__/nfse.js +121 -0
- package/dist/fiscal/ingestion/__fixtures__/nfse.js.map +1 -0
- package/dist/fiscal/ingestion/adn-transport.d.ts +123 -0
- package/dist/fiscal/ingestion/adn-transport.d.ts.map +1 -0
- package/dist/fiscal/ingestion/adn-transport.js +220 -0
- package/dist/fiscal/ingestion/adn-transport.js.map +1 -0
- package/dist/fiscal/ingestion/direction.d.ts +60 -0
- package/dist/fiscal/ingestion/direction.d.ts.map +1 -0
- package/dist/fiscal/ingestion/direction.js +32 -0
- package/dist/fiscal/ingestion/direction.js.map +1 -0
- package/dist/fiscal/ingestion/nfse-adn.d.ts +91 -0
- package/dist/fiscal/ingestion/nfse-adn.d.ts.map +1 -0
- package/dist/fiscal/ingestion/nfse-adn.js +222 -0
- package/dist/fiscal/ingestion/nfse-adn.js.map +1 -0
- package/dist/fiscal/ingestion/pipeline.d.ts +168 -0
- package/dist/fiscal/ingestion/pipeline.d.ts.map +1 -0
- package/dist/fiscal/ingestion/pipeline.js +699 -0
- package/dist/fiscal/ingestion/pipeline.js.map +1 -0
- package/dist/fiscal/ingestion/provider.d.ts +206 -0
- package/dist/fiscal/ingestion/provider.d.ts.map +1 -0
- package/dist/fiscal/ingestion/provider.js +47 -0
- package/dist/fiscal/ingestion/provider.js.map +1 -0
- package/dist/fiscal/ingestion/repository.d.ts +82 -0
- package/dist/fiscal/ingestion/repository.d.ts.map +1 -0
- package/dist/fiscal/ingestion/repository.js +127 -0
- package/dist/fiscal/ingestion/repository.js.map +1 -0
- package/dist/fiscal/ingestion/synthetic-provider.d.ts +92 -0
- package/dist/fiscal/ingestion/synthetic-provider.d.ts.map +1 -0
- package/dist/fiscal/ingestion/synthetic-provider.js +99 -0
- package/dist/fiscal/ingestion/synthetic-provider.js.map +1 -0
- package/dist/fiscal/ingestion/synthetic-transport-types.d.ts +19 -0
- package/dist/fiscal/ingestion/synthetic-transport-types.d.ts.map +1 -0
- package/dist/fiscal/ingestion/synthetic-transport-types.js +9 -0
- package/dist/fiscal/ingestion/synthetic-transport-types.js.map +1 -0
- package/dist/fiscal/ingestion/synthetic-transport.d.ts +55 -0
- package/dist/fiscal/ingestion/synthetic-transport.d.ts.map +1 -0
- package/dist/fiscal/ingestion/synthetic-transport.js +57 -0
- package/dist/fiscal/ingestion/synthetic-transport.js.map +1 -0
- package/dist/fiscal/ingestion/xml-reader.d.ts +55 -0
- package/dist/fiscal/ingestion/xml-reader.d.ts.map +1 -0
- package/dist/fiscal/ingestion/xml-reader.js +112 -0
- package/dist/fiscal/ingestion/xml-reader.js.map +1 -0
- package/dist/fiscal/repository.d.ts +220 -0
- package/dist/fiscal/repository.d.ts.map +1 -0
- package/dist/fiscal/repository.js +449 -0
- package/dist/fiscal/repository.js.map +1 -0
- package/dist/fiscal/sync/circuit.d.ts +86 -0
- package/dist/fiscal/sync/circuit.d.ts.map +1 -0
- package/dist/fiscal/sync/circuit.js +118 -0
- package/dist/fiscal/sync/circuit.js.map +1 -0
- package/dist/fiscal/sync/intervention.d.ts +81 -0
- package/dist/fiscal/sync/intervention.d.ts.map +1 -0
- package/dist/fiscal/sync/intervention.js +135 -0
- package/dist/fiscal/sync/intervention.js.map +1 -0
- package/dist/fiscal/sync/lease.d.ts +118 -0
- package/dist/fiscal/sync/lease.d.ts.map +1 -0
- package/dist/fiscal/sync/lease.js +139 -0
- package/dist/fiscal/sync/lease.js.map +1 -0
- package/dist/fiscal/sync/observability.d.ts +148 -0
- package/dist/fiscal/sync/observability.d.ts.map +1 -0
- package/dist/fiscal/sync/observability.js +239 -0
- package/dist/fiscal/sync/observability.js.map +1 -0
- package/dist/fiscal/sync/orchestrator.d.ts +132 -0
- package/dist/fiscal/sync/orchestrator.d.ts.map +1 -0
- package/dist/fiscal/sync/orchestrator.js +487 -0
- package/dist/fiscal/sync/orchestrator.js.map +1 -0
- package/dist/fiscal/sync/policy.d.ts +87 -0
- package/dist/fiscal/sync/policy.d.ts.map +1 -0
- package/dist/fiscal/sync/policy.js +136 -0
- package/dist/fiscal/sync/policy.js.map +1 -0
- package/dist/fiscal/sync/reprocess.d.ts +76 -0
- package/dist/fiscal/sync/reprocess.d.ts.map +1 -0
- package/dist/fiscal/sync/reprocess.js +237 -0
- package/dist/fiscal/sync/reprocess.js.map +1 -0
- package/dist/fiscal/sync/retry.d.ts +82 -0
- package/dist/fiscal/sync/retry.d.ts.map +1 -0
- package/dist/fiscal/sync/retry.js +176 -0
- package/dist/fiscal/sync/retry.js.map +1 -0
- package/dist/fiscal/sync/secret-boundary.d.ts +169 -0
- package/dist/fiscal/sync/secret-boundary.d.ts.map +1 -0
- package/dist/fiscal/sync/secret-boundary.js +116 -0
- package/dist/fiscal/sync/secret-boundary.js.map +1 -0
- package/dist/fiscal/transport/https.d.ts +86 -0
- package/dist/fiscal/transport/https.d.ts.map +1 -0
- package/dist/fiscal/transport/https.js +303 -0
- package/dist/fiscal/transport/https.js.map +1 -0
- package/dist/fiscal/transport/synthetic-tls.d.ts +80 -0
- package/dist/fiscal/transport/synthetic-tls.d.ts.map +1 -0
- package/dist/fiscal/transport/synthetic-tls.js +323 -0
- package/dist/fiscal/transport/synthetic-tls.js.map +1 -0
- package/dist/fiscal/transport/types.d.ts +134 -0
- package/dist/fiscal/transport/types.d.ts.map +1 -0
- package/dist/fiscal/transport/types.js +88 -0
- package/dist/fiscal/transport/types.js.map +1 -0
- package/dist/import/br-documents.d.ts +26 -0
- package/dist/import/br-documents.d.ts.map +1 -0
- package/dist/import/br-documents.js +78 -0
- package/dist/import/br-documents.js.map +1 -0
- package/dist/import/clients-import.d.ts +122 -4
- package/dist/import/clients-import.d.ts.map +1 -1
- package/dist/import/clients-import.js +341 -41
- package/dist/import/clients-import.js.map +1 -1
- package/dist/index.d.ts +115 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +100 -7
- package/dist/index.js.map +1 -1
- package/dist/legal/documents.d.ts +12 -1
- package/dist/legal/documents.d.ts.map +1 -1
- package/dist/legal/documents.js +98 -14
- package/dist/legal/documents.js.map +1 -1
- package/dist/legal/index.d.ts +1 -1
- package/dist/legal/index.d.ts.map +1 -1
- package/dist/legal/index.js +1 -1
- package/dist/legal/index.js.map +1 -1
- package/dist/legal/types.d.ts +37 -2
- package/dist/legal/types.d.ts.map +1 -1
- package/dist/legal/types.js.map +1 -1
- package/dist/onboarding/persistence.d.ts +10 -1
- package/dist/onboarding/persistence.d.ts.map +1 -1
- package/dist/onboarding/persistence.js +4 -4
- package/dist/onboarding/persistence.js.map +1 -1
- package/dist/pilot/go-live-checklist.d.ts +41 -0
- package/dist/pilot/go-live-checklist.d.ts.map +1 -0
- package/dist/pilot/go-live-checklist.js +209 -0
- package/dist/pilot/go-live-checklist.js.map +1 -0
- package/dist/pilot/milestone.d.ts +56 -0
- package/dist/pilot/milestone.d.ts.map +1 -0
- package/dist/pilot/milestone.js +74 -0
- package/dist/pilot/milestone.js.map +1 -0
- package/dist/platform/capabilities.d.ts +88 -0
- package/dist/platform/capabilities.d.ts.map +1 -0
- package/dist/platform/capabilities.js +140 -0
- package/dist/platform/capabilities.js.map +1 -0
- package/dist/platform/job-contract.d.ts +232 -0
- package/dist/platform/job-contract.d.ts.map +1 -0
- package/dist/platform/job-contract.js +403 -0
- package/dist/platform/job-contract.js.map +1 -0
- package/dist/rbac/catalog.d.ts.map +1 -1
- package/dist/rbac/catalog.js +34 -0
- package/dist/rbac/catalog.js.map +1 -1
- package/dist/subscription-billing/index.d.ts +6 -5
- package/dist/subscription-billing/index.d.ts.map +1 -1
- package/dist/subscription-billing/index.js +3 -3
- package/dist/subscription-billing/index.js.map +1 -1
- package/dist/subscription-billing/service.d.ts +21 -0
- package/dist/subscription-billing/service.d.ts.map +1 -1
- package/dist/subscription-billing/service.js +90 -15
- package/dist/subscription-billing/service.js.map +1 -1
- package/dist/tenancy/features.d.ts +69 -0
- package/dist/tenancy/features.d.ts.map +1 -0
- package/dist/tenancy/features.js +123 -0
- package/dist/tenancy/features.js.map +1 -0
- package/dist/tenancy/settings.d.ts +63 -0
- package/dist/tenancy/settings.d.ts.map +1 -0
- package/dist/tenancy/settings.js +218 -0
- package/dist/tenancy/settings.js.map +1 -0
- package/dist/users/repository.d.ts +16 -1
- package/dist/users/repository.d.ts.map +1 -1
- package/dist/users/repository.js +24 -1
- package/dist/users/repository.js.map +1 -1
- package/package.json +9 -2
- package/src/db/migrations/0045_role_write_scope.sql +122 -0
- package/src/db/migrations/0046_pilot_activation_model.sql +184 -0
- package/src/db/migrations/0047_recipient_clearance.sql +89 -0
- package/src/db/migrations/0048_item_type_note.sql +16 -0
- package/src/db/migrations/0049_national_fiscal_engine.sql +279 -0
- package/src/db/migrations/0050_fiscal_vocabulary_readonly.sql +29 -0
- package/src/db/migrations/0051_seed_rules_from_catalog.sql +72 -0
- package/src/db/migrations/0052_fiscal_rule_delete_scope.sql +57 -0
- package/src/db/migrations/0053_rule_validity_lower_bound.sql +44 -0
- package/src/db/migrations/0054_tenant_profile_and_rule_source.sql +89 -0
- package/src/db/migrations/0055_job_capability_contract.sql +300 -0
- package/src/db/migrations/0056_fiscal_document_ingestion.sql +337 -0
- package/src/db/migrations/0057_fiscal_pending_event_inbox.sql +228 -0
- package/src/db/migrations/0058_fiscal_operational_engine.sql +516 -0
- package/src/db/migrations/0059_fiscal_contract_version.sql +59 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
-- 0049 — a fundação nacional do motor fiscal.
|
|
2
|
+
--
|
|
3
|
+
-- ═══ A decisão de modelagem que organiza tudo ═══════════════════════════════
|
|
4
|
+
--
|
|
5
|
+
-- Hoje `recurring_item_type` responde duas perguntas diferentes na mesma linha:
|
|
6
|
+
--
|
|
7
|
+
-- "o que é esta obrigação?" → DAS, ISS, eSocial. Nacional e estável.
|
|
8
|
+
-- "para quem, quando e como vence?" → varia por UF, por município e no tempo.
|
|
9
|
+
--
|
|
10
|
+
-- Enquanto as duas moram juntas, o índice único em `(key)` permite UMA linha
|
|
11
|
+
-- por chave, e daí saem as três limitações que a auditoria mediu: o ISS de
|
|
12
|
+
-- Goiânia e o de São Paulo não coexistem, duas versões da mesma regra não
|
|
13
|
+
-- coexistem, e a aplicabilidade só sabe olhar regime.
|
|
14
|
+
--
|
|
15
|
+
-- Esta migration separa as duas perguntas:
|
|
16
|
+
--
|
|
17
|
+
-- `recurring_item_type` → a IDENTIDADE. Continua como está. Nada quebra.
|
|
18
|
+
-- `fiscal_rule` → a REGRA: jurisdição + vigência + versão +
|
|
19
|
+
-- condições + vencimento.
|
|
20
|
+
--
|
|
21
|
+
-- Um tipo de item passa a ter N regras: uma federal, uma por UF, uma por
|
|
22
|
+
-- município, e mais de uma no tempo. É o que abrangência nacional exige, e é
|
|
23
|
+
-- aditivo — nenhuma linha existente é reescrita.
|
|
24
|
+
--
|
|
25
|
+
-- ═══ O que esta migration NÃO faz ══════════════════════════════════════════
|
|
26
|
+
--
|
|
27
|
+
-- Não cataloga regra nenhuma do Brasil. Ela cria a capacidade; o conteúdo
|
|
28
|
+
-- entra depois, com governança. Os 17 seeds atuais são migrados na 0050 SEM
|
|
29
|
+
-- ganhar precisão que não têm.
|
|
30
|
+
|
|
31
|
+
-- ── 1 · Vocabulário do perfil fiscal ───────────────────────────────────────
|
|
32
|
+
--
|
|
33
|
+
-- Registro de atributos, e não colunas em `client`.
|
|
34
|
+
--
|
|
35
|
+
-- A alternativa seria `has_employees`, `icms_taxpayer`, `iss_taxpayer`,
|
|
36
|
+
-- `ipi_taxpayer`… uma coluna por pergunta fiscal. Isso não escala: cada UF e
|
|
37
|
+
-- cada segmento acrescenta perguntas, e a tabela de cliente viraria uma
|
|
38
|
+
-- estrutura rígida impossível de evoluir — exatamente o que a premissa pede
|
|
39
|
+
-- para evitar.
|
|
40
|
+
--
|
|
41
|
+
-- Com um REGISTRO, acrescentar um atributo é INSERT, não migration. E a FK
|
|
42
|
+
-- impede erro de digitação virar atributo novo em silêncio.
|
|
43
|
+
|
|
44
|
+
CREATE TABLE IF NOT EXISTS fiscal_attribute_definition (
|
|
45
|
+
key text PRIMARY KEY,
|
|
46
|
+
label text NOT NULL,
|
|
47
|
+
description text,
|
|
48
|
+
value_type text NOT NULL CHECK (value_type IN ('boolean', 'text', 'enum')),
|
|
49
|
+
-- Para `enum`: os valores aceitos. Para os demais, `null`.
|
|
50
|
+
allowed_values jsonb,
|
|
51
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
52
|
+
CHECK ((value_type = 'enum') = (allowed_values IS NOT NULL))
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
COMMENT ON TABLE fiscal_attribute_definition IS
|
|
56
|
+
'Vocabulário GLOBAL do perfil fiscal. Acrescentar atributo é INSERT, não migration — é o que permite o modelo evoluir sem alterar a tabela de cliente.';
|
|
57
|
+
|
|
58
|
+
INSERT INTO fiscal_attribute_definition (key, label, description, value_type, allowed_values) VALUES
|
|
59
|
+
('has_employees', 'Possui empregados',
|
|
60
|
+
'Decide a aplicabilidade das obrigações trabalhistas. A AUSÊNCIA deste dado não é "não": é desconhecido, e o motor devolve INSUFFICIENT_DATA.',
|
|
61
|
+
'boolean', NULL),
|
|
62
|
+
('icms_taxpayer', 'Contribuinte de ICMS', NULL, 'boolean', NULL),
|
|
63
|
+
('ipi_taxpayer', 'Contribuinte de IPI', NULL, 'boolean', NULL),
|
|
64
|
+
('iss_taxpayer', 'Contribuinte de ISS', NULL, 'boolean', NULL),
|
|
65
|
+
('activity_segment', 'Segmento de atividade', NULL,
|
|
66
|
+
'enum', '["comercio","industria","servicos","misto"]'::jsonb),
|
|
67
|
+
('cnae_primary', 'CNAE principal', 'Código, sem interpretação pelo motor nesta fase.', 'text', NULL),
|
|
68
|
+
('state_registration', 'Inscrição estadual', NULL, 'text', NULL),
|
|
69
|
+
('municipal_registration', 'Inscrição municipal', NULL, 'text', NULL)
|
|
70
|
+
ON CONFLICT (key) DO NOTHING;
|
|
71
|
+
|
|
72
|
+
-- ── 2 · Perfil fiscal do cliente ───────────────────────────────────────────
|
|
73
|
+
--
|
|
74
|
+
-- Ausência de linha = atributo DESCONHECIDO, e é distinto de `false`. É a
|
|
75
|
+
-- distinção que sustenta o `INSUFFICIENT_DATA` do motor: um cliente sobre o
|
|
76
|
+
-- qual ninguém disse se tem empregados não pode receber eSocial nem ser
|
|
77
|
+
-- declarado isento dele.
|
|
78
|
+
|
|
79
|
+
CREATE TABLE IF NOT EXISTS client_fiscal_attribute (
|
|
80
|
+
id uuid PRIMARY KEY,
|
|
81
|
+
tenant_id uuid NOT NULL REFERENCES tenant(id),
|
|
82
|
+
client_id uuid NOT NULL,
|
|
83
|
+
attribute_key text NOT NULL REFERENCES fiscal_attribute_definition(key),
|
|
84
|
+
value_bool boolean,
|
|
85
|
+
value_text text,
|
|
86
|
+
source text,
|
|
87
|
+
updated_by uuid REFERENCES "user"(id),
|
|
88
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
89
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
90
|
+
CONSTRAINT client_fiscal_attribute_tenant_id_uq UNIQUE (tenant_id, id),
|
|
91
|
+
FOREIGN KEY (tenant_id, client_id) REFERENCES client (tenant_id, id),
|
|
92
|
+
-- Exatamente um valor. Nenhum seria linha sem informação; dois seriam duas
|
|
93
|
+
-- respostas para a mesma pergunta.
|
|
94
|
+
CHECK ((value_bool IS NOT NULL)::int + (value_text IS NOT NULL)::int = 1)
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
CREATE UNIQUE INDEX IF NOT EXISTS client_fiscal_attribute_uq
|
|
98
|
+
ON client_fiscal_attribute (tenant_id, client_id, attribute_key);
|
|
99
|
+
|
|
100
|
+
ALTER TABLE client_fiscal_attribute ENABLE ROW LEVEL SECURITY;
|
|
101
|
+
ALTER TABLE client_fiscal_attribute FORCE ROW LEVEL SECURITY;
|
|
102
|
+
CREATE POLICY client_fiscal_attribute_tenant_isolation ON client_fiscal_attribute
|
|
103
|
+
USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
|
|
104
|
+
WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
|
|
105
|
+
|
|
106
|
+
-- Código IBGE do município: o identificador canônico.
|
|
107
|
+
--
|
|
108
|
+
-- `client.municipality` é texto livre e serve para a tela. Casar regra
|
|
109
|
+
-- municipal por texto livre falharia no primeiro "Goiania" sem acento. O
|
|
110
|
+
-- código é opcional: sem ele, regra municipal devolve INSUFFICIENT_DATA em vez
|
|
111
|
+
-- de casar por aproximação.
|
|
112
|
+
ALTER TABLE client ADD COLUMN IF NOT EXISTS municipality_ibge_code text;
|
|
113
|
+
|
|
114
|
+
DO $$
|
|
115
|
+
BEGIN
|
|
116
|
+
IF NOT EXISTS (SELECT FROM pg_constraint WHERE conname = 'client_ibge_format') THEN
|
|
117
|
+
ALTER TABLE client ADD CONSTRAINT client_ibge_format
|
|
118
|
+
CHECK (municipality_ibge_code IS NULL OR municipality_ibge_code ~ '^[0-9]{7}$');
|
|
119
|
+
END IF;
|
|
120
|
+
END $$;
|
|
121
|
+
|
|
122
|
+
-- ── 3 · A regra fiscal versionada ──────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
CREATE TABLE IF NOT EXISTS fiscal_rule (
|
|
125
|
+
id uuid PRIMARY KEY,
|
|
126
|
+
-- NULL = regra GLOBAL do produto. NOT NULL = regra do próprio escritório.
|
|
127
|
+
-- Mesmo padrão de `recurring_item_type` e `role`: ler o global é direito de
|
|
128
|
+
-- todos; escrevê-lo, de ninguém que opere como tenant.
|
|
129
|
+
tenant_id uuid REFERENCES tenant(id),
|
|
130
|
+
item_type_id uuid NOT NULL REFERENCES recurring_item_type(id),
|
|
131
|
+
|
|
132
|
+
-- ── jurisdição ──────────────────────────────────────────────────────────
|
|
133
|
+
jurisdiction_scope text NOT NULL CHECK (jurisdiction_scope IN ('federal', 'state', 'municipal')),
|
|
134
|
+
jurisdiction_uf text CHECK (jurisdiction_uf IS NULL OR jurisdiction_uf ~ '^[A-Z]{2}$'),
|
|
135
|
+
jurisdiction_city_ibge text CHECK (jurisdiction_city_ibge IS NULL OR jurisdiction_city_ibge ~ '^[0-9]{7}$'),
|
|
136
|
+
-- A jurisdição precisa ser COMPLETA para o seu nível. Uma regra "estadual"
|
|
137
|
+
-- sem UF não é uma regra: é um marcador que casaria com o país inteiro.
|
|
138
|
+
CHECK (
|
|
139
|
+
(jurisdiction_scope = 'federal' AND jurisdiction_uf IS NULL AND jurisdiction_city_ibge IS NULL) OR
|
|
140
|
+
(jurisdiction_scope = 'state' AND jurisdiction_uf IS NOT NULL AND jurisdiction_city_ibge IS NULL) OR
|
|
141
|
+
(jurisdiction_scope = 'municipal' AND jurisdiction_uf IS NOT NULL AND jurisdiction_city_ibge IS NOT NULL)
|
|
142
|
+
),
|
|
143
|
+
|
|
144
|
+
-- ── vigência e versão ───────────────────────────────────────────────────
|
|
145
|
+
version int NOT NULL DEFAULT 1 CHECK (version > 0),
|
|
146
|
+
valid_from date NOT NULL,
|
|
147
|
+
valid_until date,
|
|
148
|
+
CHECK (valid_until IS NULL OR valid_until >= valid_from),
|
|
149
|
+
|
|
150
|
+
status text NOT NULL DEFAULT 'pending_validation'
|
|
151
|
+
CHECK (status IN ('not_configured', 'pending_validation', 'validated', 'expired', 'disabled')),
|
|
152
|
+
|
|
153
|
+
-- ── conteúdo ────────────────────────────────────────────────────────────
|
|
154
|
+
-- AST declarativa. Interpretada por código tipado, NUNCA avaliada como
|
|
155
|
+
-- expressão: nada aqui vira `eval`, SQL ou função.
|
|
156
|
+
conditions jsonb NOT NULL DEFAULT '{"op":"always"}'::jsonb,
|
|
157
|
+
due_rule jsonb,
|
|
158
|
+
|
|
159
|
+
-- ── procedência ─────────────────────────────────────────────────────────
|
|
160
|
+
source_reference text,
|
|
161
|
+
validated_by uuid REFERENCES "user"(id),
|
|
162
|
+
validated_at timestamptz,
|
|
163
|
+
note text,
|
|
164
|
+
|
|
165
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
166
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
167
|
+
deleted_at timestamptz,
|
|
168
|
+
|
|
169
|
+
CONSTRAINT fiscal_rule_tenant_id_uq UNIQUE (tenant_id, id),
|
|
170
|
+
-- Uma regra `validated` sem quem validou é uma afirmação sem autor.
|
|
171
|
+
CHECK ((status <> 'validated') OR (validated_at IS NOT NULL AND validated_by IS NOT NULL)),
|
|
172
|
+
-- `not_configured` é o estado honesto de quem não tem vencimento definido.
|
|
173
|
+
-- Uma regra validada SEM vencimento produziria obrigação sem data.
|
|
174
|
+
CHECK ((status <> 'validated') OR due_rule IS NOT NULL)
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
-- Versões da mesma regra não se sobrepõem: uma só vigente por jurisdição.
|
|
178
|
+
-- Mesmo padrão que `accounting_regime` já provou para o regime do cliente.
|
|
179
|
+
CREATE UNIQUE INDEX IF NOT EXISTS fiscal_rule_current_uq
|
|
180
|
+
ON fiscal_rule (
|
|
181
|
+
coalesce(tenant_id::text, '-'), item_type_id, jurisdiction_scope,
|
|
182
|
+
coalesce(jurisdiction_uf, '-'), coalesce(jurisdiction_city_ibge, '-')
|
|
183
|
+
)
|
|
184
|
+
WHERE valid_until IS NULL AND deleted_at IS NULL;
|
|
185
|
+
|
|
186
|
+
CREATE UNIQUE INDEX IF NOT EXISTS fiscal_rule_version_uq
|
|
187
|
+
ON fiscal_rule (
|
|
188
|
+
coalesce(tenant_id::text, '-'), item_type_id, jurisdiction_scope,
|
|
189
|
+
coalesce(jurisdiction_uf, '-'), coalesce(jurisdiction_city_ibge, '-'), version
|
|
190
|
+
)
|
|
191
|
+
WHERE deleted_at IS NULL;
|
|
192
|
+
|
|
193
|
+
-- Pré-filtragem: a engine busca por jurisdição + vigência antes de avaliar
|
|
194
|
+
-- condição nenhuma. Sem este índice, avaliar a carteira exigiria varrer o
|
|
195
|
+
-- catálogo inteiro por cliente.
|
|
196
|
+
CREATE INDEX IF NOT EXISTS fiscal_rule_lookup_idx
|
|
197
|
+
ON fiscal_rule (jurisdiction_scope, jurisdiction_uf, jurisdiction_city_ibge, valid_from)
|
|
198
|
+
WHERE deleted_at IS NULL;
|
|
199
|
+
CREATE INDEX IF NOT EXISTS fiscal_rule_item_idx
|
|
200
|
+
ON fiscal_rule (item_type_id, valid_from) WHERE deleted_at IS NULL;
|
|
201
|
+
|
|
202
|
+
ALTER TABLE fiscal_rule ENABLE ROW LEVEL SECURITY;
|
|
203
|
+
ALTER TABLE fiscal_rule FORCE ROW LEVEL SECURITY;
|
|
204
|
+
|
|
205
|
+
-- Leitura: a regra global e as do próprio escritório.
|
|
206
|
+
-- Escrita: SOMENTE as do próprio escritório.
|
|
207
|
+
--
|
|
208
|
+
-- O `WITH CHECK` sem o ramo `IS NULL` é a lição da 0045: com ele, qualquer
|
|
209
|
+
-- tenant forjaria regra fiscal GLOBAL — que vale para todos os escritórios do
|
|
210
|
+
-- país. É a escalada mais cara que este schema comporta.
|
|
211
|
+
CREATE POLICY fiscal_rule_visibility ON fiscal_rule
|
|
212
|
+
USING (tenant_id IS NULL OR tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
|
|
213
|
+
WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
|
|
214
|
+
|
|
215
|
+
DO $$
|
|
216
|
+
DECLARE owner_role text;
|
|
217
|
+
BEGIN
|
|
218
|
+
SELECT tableowner INTO owner_role FROM pg_tables
|
|
219
|
+
WHERE schemaname = 'public' AND tablename = 'fiscal_rule';
|
|
220
|
+
IF owner_role IS NULL THEN
|
|
221
|
+
RAISE EXCEPTION 'fiscal_rule sem dono identificável — abortando';
|
|
222
|
+
END IF;
|
|
223
|
+
-- FORCE RLS vale para o dono; o provisionamento do catálogo global roda sem
|
|
224
|
+
-- tenant no contexto e precisa de política nominal. Padrão da 0042/0045.
|
|
225
|
+
EXECUTE format(
|
|
226
|
+
'CREATE POLICY fiscal_rule_system_provisioning ON fiscal_rule FOR ALL TO %I USING (true) WITH CHECK (true)',
|
|
227
|
+
owner_role);
|
|
228
|
+
END $$;
|
|
229
|
+
|
|
230
|
+
COMMENT ON POLICY fiscal_rule_visibility ON fiscal_rule IS
|
|
231
|
+
'Leitura: regra global + as do tenant. Escrita: SOMENTE do tenant. O ramo IS NULL fora do WITH CHECK impede forjar regra fiscal nacional (lição do P0-A, Fase 4).';
|
|
232
|
+
|
|
233
|
+
-- ── 4 · Política operacional do escritório ─────────────────────────────────
|
|
234
|
+
--
|
|
235
|
+
-- Separa REGRA LEGAL de PROCEDIMENTO DO ESCRITÓRIO.
|
|
236
|
+
--
|
|
237
|
+
-- O prazo legal do DAS é do país. O prazo em que o escritório quer os
|
|
238
|
+
-- documentos do cliente na mão é dele. Enquanto os dois moram no mesmo campo,
|
|
239
|
+
-- ajustar o procedimento interno reescreve a regra legal — em silêncio, e para
|
|
240
|
+
-- todo mundo que enxerga aquela linha.
|
|
241
|
+
|
|
242
|
+
CREATE TABLE IF NOT EXISTS tenant_rule_policy (
|
|
243
|
+
id uuid PRIMARY KEY,
|
|
244
|
+
tenant_id uuid NOT NULL REFERENCES tenant(id),
|
|
245
|
+
item_type_id uuid NOT NULL REFERENCES recurring_item_type(id),
|
|
246
|
+
-- Dias ANTES do vencimento legal em que o escritório quer a coisa pronta.
|
|
247
|
+
-- Não altera o vencimento legal: acrescenta um prazo interno ao lado dele.
|
|
248
|
+
internal_lead_days int CHECK (internal_lead_days IS NULL OR internal_lead_days BETWEEN 0 AND 90),
|
|
249
|
+
enabled boolean NOT NULL DEFAULT true,
|
|
250
|
+
note text,
|
|
251
|
+
updated_by uuid REFERENCES "user"(id),
|
|
252
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
253
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
254
|
+
CONSTRAINT tenant_rule_policy_tenant_id_uq UNIQUE (tenant_id, id)
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
CREATE UNIQUE INDEX IF NOT EXISTS tenant_rule_policy_uq
|
|
258
|
+
ON tenant_rule_policy (tenant_id, item_type_id);
|
|
259
|
+
|
|
260
|
+
ALTER TABLE tenant_rule_policy ENABLE ROW LEVEL SECURITY;
|
|
261
|
+
ALTER TABLE tenant_rule_policy FORCE ROW LEVEL SECURITY;
|
|
262
|
+
CREATE POLICY tenant_rule_policy_tenant_isolation ON tenant_rule_policy
|
|
263
|
+
USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
|
|
264
|
+
WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
|
|
265
|
+
|
|
266
|
+
COMMENT ON TABLE tenant_rule_policy IS
|
|
267
|
+
'Procedimento do escritório, ao LADO da regra legal e nunca por cima dela. Prazo interno não é prazo legal.';
|
|
268
|
+
|
|
269
|
+
-- ── 5 · Preservação histórica ──────────────────────────────────────────────
|
|
270
|
+
--
|
|
271
|
+
-- A instância já congela `label` e `due_date`. Falta saber QUAL versão da
|
|
272
|
+
-- regra produziu aquela data — sem isso, uma V2 publicada amanhã faz a
|
|
273
|
+
-- obrigação de ontem parecer ter nascido de uma regra que não existia.
|
|
274
|
+
|
|
275
|
+
ALTER TABLE recurring_instance ADD COLUMN IF NOT EXISTS fiscal_rule_id uuid REFERENCES fiscal_rule(id);
|
|
276
|
+
ALTER TABLE recurring_instance ADD COLUMN IF NOT EXISTS fiscal_rule_version int;
|
|
277
|
+
|
|
278
|
+
COMMENT ON COLUMN recurring_instance.fiscal_rule_id IS
|
|
279
|
+
'Versão EXATA da regra que gerou esta obrigação. Publicar uma V2 não altera o que já foi gerado sob a V1.';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
-- 0050 — o vocabulário do perfil fiscal é lido pelo runtime, nunca escrito.
|
|
2
|
+
--
|
|
3
|
+
-- Esta migration existe separada da 0049 por uma razão de processo, e vale
|
|
4
|
+
-- registrar: eu havia acrescentado este bloco ao fim da 0049 depois de ela já
|
|
5
|
+
-- ter sido aplicada. O runner recusou — migrations são imutáveis, e a checagem
|
|
6
|
+
-- de hash existe exatamente para impedir que o que rodou num ambiente seja
|
|
7
|
+
-- diferente do que roda no próximo.
|
|
8
|
+
--
|
|
9
|
+
-- ── A barreira ─────────────────────────────────────────────────────────────
|
|
10
|
+
--
|
|
11
|
+
-- `fiscal_attribute_definition` é catálogo GLOBAL, sem `tenant_id`. Não há RLS
|
|
12
|
+
-- que o proteja, porque não há linha de ninguém: a barreira só pode ser o
|
|
13
|
+
-- privilégio.
|
|
14
|
+
--
|
|
15
|
+
-- O runtime lê o vocabulário a cada avaliação de aplicabilidade e nunca o
|
|
16
|
+
-- escreve. Um tenant capaz de inserir atributo aqui poderia criar um atributo
|
|
17
|
+
-- que nenhuma regra usa (inócuo) ou — pior — apagar um que as regras de todos
|
|
18
|
+
-- os escritórios usam, derrubando a avaliação do país inteiro por FK.
|
|
19
|
+
--
|
|
20
|
+
-- Repetido em `migrate.ts` no mesmo bloco de `role` e `audit_log`: o GRANT da
|
|
21
|
+
-- allowlist é amplo por construção e devolveria a escrita no deploy seguinte.
|
|
22
|
+
-- Foi o defeito que a Fase 3 encontrou ao separar GRANT de hardening.
|
|
23
|
+
|
|
24
|
+
DO $$
|
|
25
|
+
BEGIN
|
|
26
|
+
IF EXISTS (SELECT FROM pg_roles WHERE rolname = 'baruk_app') THEN
|
|
27
|
+
EXECUTE 'REVOKE INSERT, UPDATE, DELETE ON fiscal_attribute_definition FROM baruk_app';
|
|
28
|
+
END IF;
|
|
29
|
+
END $$;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
-- 0051 — os 17 seeds viram regras, SEM ganhar precisão que não têm.
|
|
2
|
+
--
|
|
3
|
+
-- ═══ O risco que esta migration precisa não correr ═════════════════════════
|
|
4
|
+
--
|
|
5
|
+
-- É tentador aproveitar a estrutura nova para "melhorar" o seed: dar UF ao ISS,
|
|
6
|
+
-- marcar as federais como validadas, escrever condição de folha nos itens
|
|
7
|
+
-- trabalhistas. Cada uma dessas seria legislação inventada por quem não tem
|
|
8
|
+
-- competência para inventá-la — e escrita num lugar em que passaria a parecer
|
|
9
|
+
-- fonte.
|
|
10
|
+
--
|
|
11
|
+
-- Então esta migration converte o que existe, e **nada mais**:
|
|
12
|
+
--
|
|
13
|
+
-- • toda regra nasce `pending_validation`. Nenhuma vira `validated`: ninguém
|
|
14
|
+
-- conferiu nada ainda, e o CHECK do banco exige autor para validar;
|
|
15
|
+
-- • toda condição nasce `{"op":"always"}` — a aplicabilidade que o seed de
|
|
16
|
+
-- fato tem hoje é por regime, e o regime já é filtrado por
|
|
17
|
+
-- `applies_to_regime` no tipo de item. Não invento condição de folha;
|
|
18
|
+
-- • os dois itens de esfera local viram `not_configured`, porque **não se
|
|
19
|
+
-- sabe de qual UF nem de qual município eles são**. Dar-lhes uma
|
|
20
|
+
-- jurisdição seria escolher um estado por conta própria.
|
|
21
|
+
--
|
|
22
|
+
-- `valid_from` é a data desta migration: é honesto dizer "esta regra existe no
|
|
23
|
+
-- sistema desde hoje", e desonesto dizer que ela vale desde sempre.
|
|
24
|
+
|
|
25
|
+
DO $$
|
|
26
|
+
DECLARE
|
|
27
|
+
r record;
|
|
28
|
+
hoje date := CURRENT_DATE;
|
|
29
|
+
BEGIN
|
|
30
|
+
FOR r IN
|
|
31
|
+
SELECT id, key, sphere, due_rule, note
|
|
32
|
+
FROM recurring_item_type
|
|
33
|
+
WHERE tenant_id IS NULL AND deleted_at IS NULL
|
|
34
|
+
LOOP
|
|
35
|
+
-- Idempotente: reexecutar não duplica.
|
|
36
|
+
IF EXISTS (SELECT 1 FROM fiscal_rule WHERE item_type_id = r.id AND tenant_id IS NULL) THEN
|
|
37
|
+
CONTINUE;
|
|
38
|
+
END IF;
|
|
39
|
+
|
|
40
|
+
IF r.sphere IN ('estadual', 'municipal') THEN
|
|
41
|
+
-- Marcador honesto: o item existe, a regra não. Sem UF, sem município e
|
|
42
|
+
-- sem vencimento — o motor devolve RULE_NOT_CONFIGURED e ninguém gera
|
|
43
|
+
-- obrigação local por engano.
|
|
44
|
+
--
|
|
45
|
+
-- Fica como `federal` no campo de escopo apenas porque o CHECK exige
|
|
46
|
+
-- jurisdição completa para state/municipal, e completá-la seria inventar.
|
|
47
|
+
-- O `status` é quem carrega a verdade: não configurada.
|
|
48
|
+
INSERT INTO fiscal_rule (
|
|
49
|
+
id, tenant_id, item_type_id, jurisdiction_scope, version, valid_from,
|
|
50
|
+
status, conditions, due_rule, note
|
|
51
|
+
) VALUES (
|
|
52
|
+
gen_random_uuid(), NULL, r.id, 'federal', 1, hoje,
|
|
53
|
+
'not_configured', '{"op":"always"}'::jsonb, NULL,
|
|
54
|
+
coalesce(r.note || ' ', '') ||
|
|
55
|
+
'[N1] Item de esfera ' || r.sphere || ' sem jurisdição definida. ' ||
|
|
56
|
+
'Cadastre uma regra por UF (ou por município) antes de gerar.'
|
|
57
|
+
);
|
|
58
|
+
ELSE
|
|
59
|
+
INSERT INTO fiscal_rule (
|
|
60
|
+
id, tenant_id, item_type_id, jurisdiction_scope, version, valid_from,
|
|
61
|
+
status, conditions, due_rule, note
|
|
62
|
+
) VALUES (
|
|
63
|
+
gen_random_uuid(), NULL, r.id, 'federal', 1, hoje,
|
|
64
|
+
'pending_validation', '{"op":"always"}'::jsonb, r.due_rule,
|
|
65
|
+
coalesce(r.note, '')
|
|
66
|
+
);
|
|
67
|
+
END IF;
|
|
68
|
+
END LOOP;
|
|
69
|
+
END $$;
|
|
70
|
+
|
|
71
|
+
COMMENT ON TABLE fiscal_rule IS
|
|
72
|
+
'Regra fiscal versionada por jurisdição. As 17 primeiras vieram do seed inicial e nascem PENDING_VALIDATION ou NOT_CONFIGURED — nenhuma foi conferida por pessoa, e nenhuma ganhou jurisdição ou prazo que o seed não tinha.';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
-- 0052 — fechar o DELETE da regra fiscal GLOBAL.
|
|
2
|
+
--
|
|
3
|
+
-- ═══ O defeito, encontrado pelo teste antes do deploy ══════════════════════
|
|
4
|
+
--
|
|
5
|
+
-- A 0049 criou uma política única para `fiscal_rule`:
|
|
6
|
+
--
|
|
7
|
+
-- USING (tenant_id IS NULL OR tenant_id = current_tenant)
|
|
8
|
+
-- WITH CHECK (tenant_id = current_tenant)
|
|
9
|
+
--
|
|
10
|
+
-- Esse par fecha INSERT e UPDATE — e **não fecha DELETE**. `WITH CHECK` só
|
|
11
|
+
-- governa a linha que passa a existir; quem decide o que um DELETE alcança é o
|
|
12
|
+
-- `USING`. Com o ramo `IS NULL` ali, qualquer escritório apagava regra fiscal
|
|
13
|
+
-- nacional: as regras de todos os outros escritórios do país.
|
|
14
|
+
--
|
|
15
|
+
-- É a mesma forma do P0-A da Fase 4 — o ramo `IS NULL` no lugar errado — com
|
|
16
|
+
-- um agravante: lá a barreira de privilégio segurava, porque `role` teve
|
|
17
|
+
-- INSERT/UPDATE/DELETE revogados do runtime. Aqui não dá para revogar: o
|
|
18
|
+
-- escritório precisa escrever as PRÓPRIAS regras. A separação tem de vir da
|
|
19
|
+
-- política.
|
|
20
|
+
--
|
|
21
|
+
-- ═══ A correção ════════════════════════════════════════════════════════════
|
|
22
|
+
--
|
|
23
|
+
-- Uma política por comando, em vez de uma para tudo. Ler o catálogo nacional é
|
|
24
|
+
-- direito de todos; alterá-lo, de ninguém que opere como escritório.
|
|
25
|
+
|
|
26
|
+
DROP POLICY IF EXISTS fiscal_rule_visibility ON fiscal_rule;
|
|
27
|
+
|
|
28
|
+
CREATE POLICY fiscal_rule_read ON fiscal_rule
|
|
29
|
+
FOR SELECT
|
|
30
|
+
USING (tenant_id IS NULL OR tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
|
|
31
|
+
|
|
32
|
+
CREATE POLICY fiscal_rule_insert ON fiscal_rule
|
|
33
|
+
FOR INSERT
|
|
34
|
+
WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
|
|
35
|
+
|
|
36
|
+
CREATE POLICY fiscal_rule_update ON fiscal_rule
|
|
37
|
+
FOR UPDATE
|
|
38
|
+
USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
|
|
39
|
+
WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
|
|
40
|
+
|
|
41
|
+
CREATE POLICY fiscal_rule_delete ON fiscal_rule
|
|
42
|
+
FOR DELETE
|
|
43
|
+
USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
|
|
44
|
+
|
|
45
|
+
COMMENT ON POLICY fiscal_rule_read ON fiscal_rule IS
|
|
46
|
+
'Leitura: catálogo nacional + regras do próprio escritório.';
|
|
47
|
+
COMMENT ON POLICY fiscal_rule_delete ON fiscal_rule IS
|
|
48
|
+
'Apagar SOMENTE as próprias. A política única da 0049 deixava o DELETE alcançar a regra global, porque WITH CHECK não governa DELETE — só o USING governa.';
|
|
49
|
+
|
|
50
|
+
-- ── A mesma pergunta, feita às policies vizinhas ───────────────────────────
|
|
51
|
+
--
|
|
52
|
+
-- `role` e `role_permission` têm o mesmo formato desde a 0045 e, no papel,
|
|
53
|
+
-- o mesmo buraco no DELETE. Lá a segunda barreira segura: a 0045 revogou
|
|
54
|
+
-- INSERT, UPDATE e DELETE do runtime, e `migrate.ts` repete o REVOKE a cada
|
|
55
|
+
-- deploy. Fica registrado aqui porque a próxima pessoa a ler aquela policy vai
|
|
56
|
+
-- fazer esta pergunta — e a resposta é "o privilégio segura", não "a política
|
|
57
|
+
-- está certa".
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
-- 0053 — `valid_from` descreve a REGRA, não o dia em que a linha foi criada.
|
|
2
|
+
--
|
|
3
|
+
-- ═══ O erro da 0051, e por que ele parecia certo ═══════════════════════════
|
|
4
|
+
--
|
|
5
|
+
-- A 0051 gravou `valid_from = CURRENT_DATE` nas regras do seed, com a
|
|
6
|
+
-- justificativa de que é honesto dizer "esta regra existe no sistema desde
|
|
7
|
+
-- hoje". A frase é verdadeira e a modelagem estava errada: `valid_from` não
|
|
8
|
+
-- descreve quando o REGISTRO nasceu — descreve desde quando a REGRA vale.
|
|
9
|
+
--
|
|
10
|
+
-- O efeito apareceu no primeiro teste: um escritório que abre a competência de
|
|
11
|
+
-- agosto em setembro — que é o normal da profissão, não a exceção — não
|
|
12
|
+
-- recebia obrigação nenhuma, porque nenhuma regra era candidata para agosto.
|
|
13
|
+
--
|
|
14
|
+
-- ═══ Por que uma data antiga é mais honesta que hoje ══════════════════════
|
|
15
|
+
--
|
|
16
|
+
-- Não se sabe desde quando cada obrigação do seed está em vigor; várias são
|
|
17
|
+
-- anteriores ao produto. As duas opções eram:
|
|
18
|
+
--
|
|
19
|
+
-- hoje → afirma que a regra não valia em agosto. É falso.
|
|
20
|
+
-- limite baixo → afirma que ela vale desde antes de o sistema existir, sem
|
|
21
|
+
-- dizer desde quando. É verdadeiro e incompleto.
|
|
22
|
+
--
|
|
23
|
+
-- A segunda é a correta: `2000-01-01` aqui não é uma data legal, é a ausência
|
|
24
|
+
-- de início declarado. Declarar o início de verdade faz parte de validar a
|
|
25
|
+
-- regra, e as regras do seed continuam `pending_validation` justamente porque
|
|
26
|
+
-- ninguém as validou.
|
|
27
|
+
--
|
|
28
|
+
-- Nada disso relaxa a barreira: uma regra pendente não gera obrigação. O que
|
|
29
|
+
-- muda é que ela passa a ser CANDIDATA e a aparecer na prévia com o motivo
|
|
30
|
+
-- certo — "aguarda conferência" em vez de "não existe regra", que mandaria o
|
|
31
|
+
-- escritório procurar o que já está lá.
|
|
32
|
+
|
|
33
|
+
UPDATE fiscal_rule
|
|
34
|
+
SET valid_from = DATE '2000-01-01',
|
|
35
|
+
note = coalesce(note || ' ', '') ||
|
|
36
|
+
'[N1] Início de vigência não declarado: a data é um limite inferior, não uma afirmação legal.',
|
|
37
|
+
updated_at = now()
|
|
38
|
+
WHERE tenant_id IS NULL
|
|
39
|
+
AND deleted_at IS NULL
|
|
40
|
+
AND valid_from > DATE '2000-01-01'
|
|
41
|
+
AND status IN ('pending_validation', 'not_configured');
|
|
42
|
+
|
|
43
|
+
COMMENT ON COLUMN fiscal_rule.valid_from IS
|
|
44
|
+
'Desde quando a REGRA vale — não quando a linha foi criada. Regra sem início declarado usa um limite inferior antigo, e declarar o início real faz parte de validá-la.';
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
-- 0054 — a identidade do escritório vira dado do produto, e a regra fiscal
|
|
2
|
+
-- passa a citar a fonte.
|
|
3
|
+
--
|
|
4
|
+
-- ═══ Por que estas duas coisas na mesma migration ══════════════════════════
|
|
5
|
+
--
|
|
6
|
+
-- As duas nascem do mesmo achado da auditoria C360: o produto tinha caminho de
|
|
7
|
+
-- LEITURA e não tinha caminho de ESCRITA. A marca do escritório vivia em
|
|
8
|
+
-- `tenant.settings` e só era gravável por `UPDATE` manual no banco; a regra
|
|
9
|
+
-- fiscal tinha `source_reference` em texto livre e nenhum lugar canônico para o
|
|
10
|
+
-- endereço da norma. Nos dois casos o efeito é o mesmo: um escritório novo
|
|
11
|
+
-- exigia desenvolvedor.
|
|
12
|
+
--
|
|
13
|
+
-- Nenhuma tabela nova. Nenhum GRANT novo: `tenant` e `fiscal_rule` já estão na
|
|
14
|
+
-- allowlist do runtime, e GRANT em Postgres é por tabela — coluna nova nasce
|
|
15
|
+
-- alcançável pelo mesmo privilégio.
|
|
16
|
+
|
|
17
|
+
-- ── 1 · Identidade do escritório ───────────────────────────────────────────
|
|
18
|
+
--
|
|
19
|
+
-- `display_name` e `legal_name` já existiam. Faltavam o documento e o fuso, e
|
|
20
|
+
-- os dois têm consumidor concreto e próximo:
|
|
21
|
+
--
|
|
22
|
+
-- documento → o escritório é ele mesmo um contribuinte. A onda de ingestão
|
|
23
|
+
-- fiscal precisa do CNPJ dele para o certificado da própria casa
|
|
24
|
+
-- (`digital_certificate.client_id IS NULL` já prevê esse caso).
|
|
25
|
+
-- fuso → todo vencimento hoje é calculado em data, sem hora. No dia em
|
|
26
|
+
-- que houver corte por horário (janela de envio, D-0 às 18h), a
|
|
27
|
+
-- ausência do fuso vira erro de um dia para metade do país.
|
|
28
|
+
--
|
|
29
|
+
-- Só dígitos, como em `client.document_number`: máscara é apresentação, e duas
|
|
30
|
+
-- representações do mesmo documento não se encontram em nenhuma busca.
|
|
31
|
+
ALTER TABLE tenant ADD COLUMN IF NOT EXISTS document_number text;
|
|
32
|
+
ALTER TABLE tenant ADD COLUMN IF NOT EXISTS timezone text NOT NULL DEFAULT 'America/Sao_Paulo';
|
|
33
|
+
|
|
34
|
+
DO $$
|
|
35
|
+
BEGIN
|
|
36
|
+
IF NOT EXISTS (SELECT FROM pg_constraint WHERE conname = 'tenant_document_format') THEN
|
|
37
|
+
ALTER TABLE tenant ADD CONSTRAINT tenant_document_format
|
|
38
|
+
CHECK (document_number IS NULL OR document_number ~ '^[0-9]{11}$' OR document_number ~ '^[0-9]{14}$');
|
|
39
|
+
END IF;
|
|
40
|
+
|
|
41
|
+
-- Fuso: a lista dos fusos BRASILEIROS, não um texto livre.
|
|
42
|
+
--
|
|
43
|
+
-- `CHECK` não enxerga `pg_timezone_names`, então a alternativa seria aceitar
|
|
44
|
+
-- qualquer string e descobrir o erro na primeira conversão. O produto é
|
|
45
|
+
-- nacional; enumerar os dezesseis fusos do país é a restrição honesta e
|
|
46
|
+
-- fecha a porta para `'GMT-3'`, `'Brasilia'` e afins, que parecem certos e
|
|
47
|
+
-- não são identificadores IANA.
|
|
48
|
+
IF NOT EXISTS (SELECT FROM pg_constraint WHERE conname = 'tenant_timezone_br') THEN
|
|
49
|
+
ALTER TABLE tenant ADD CONSTRAINT tenant_timezone_br
|
|
50
|
+
CHECK (timezone IN (
|
|
51
|
+
'America/Sao_Paulo','America/Bahia','America/Fortaleza','America/Recife',
|
|
52
|
+
'America/Belem','America/Araguaina','America/Maceio','America/Campo_Grande',
|
|
53
|
+
'America/Cuiaba','America/Santarem','America/Manaus','America/Porto_Velho',
|
|
54
|
+
'America/Boa_Vista','America/Rio_Branco','America/Eirunepe','America/Noronha'
|
|
55
|
+
));
|
|
56
|
+
END IF;
|
|
57
|
+
END $$;
|
|
58
|
+
|
|
59
|
+
COMMENT ON COLUMN tenant.document_number IS
|
|
60
|
+
'CNPJ (14) ou CPF (11) do próprio escritório, só dígitos. O escritório é contribuinte de si mesmo — a ingestão fiscal precisa deste número para o certificado da casa.';
|
|
61
|
+
COMMENT ON COLUMN tenant.timezone IS
|
|
62
|
+
'Fuso IANA do escritório. Restrito aos fusos brasileiros: o produto é nacional e um texto livre aceitaria identificadores que não existem.';
|
|
63
|
+
|
|
64
|
+
-- ── 2 · A regra fiscal cita a fonte ────────────────────────────────────────
|
|
65
|
+
--
|
|
66
|
+
-- `source_reference` continua sendo a referência humana ("IN RFB 2.005/2021,
|
|
67
|
+
-- art. 3º"). `source_url` é o ENDEREÇO oficial, separado de propósito: uma URL
|
|
68
|
+
-- misturada com texto livre não pode ser verificada, aberta na tela nem
|
|
69
|
+
-- comparada entre versões.
|
|
70
|
+
--
|
|
71
|
+
-- Só https. A regra fiscal aparece numa tela de conferência e o endereço vira
|
|
72
|
+
-- link; `javascript:` e `data:` num campo editável seriam XSS armazenado com a
|
|
73
|
+
-- assinatura do escritório.
|
|
74
|
+
--
|
|
75
|
+
-- Isto NÃO valida a regra. Citar a fonte é o insumo da conferência humana, e
|
|
76
|
+
-- `status` continua exigindo `validated_by` e `validated_at` para sair de
|
|
77
|
+
-- `pending_validation`.
|
|
78
|
+
ALTER TABLE fiscal_rule ADD COLUMN IF NOT EXISTS source_url text;
|
|
79
|
+
|
|
80
|
+
DO $$
|
|
81
|
+
BEGIN
|
|
82
|
+
IF NOT EXISTS (SELECT FROM pg_constraint WHERE conname = 'fiscal_rule_source_url_https') THEN
|
|
83
|
+
ALTER TABLE fiscal_rule ADD CONSTRAINT fiscal_rule_source_url_https
|
|
84
|
+
CHECK (source_url IS NULL OR source_url ~ '^https://[^\s"''<>]+$');
|
|
85
|
+
END IF;
|
|
86
|
+
END $$;
|
|
87
|
+
|
|
88
|
+
COMMENT ON COLUMN fiscal_rule.source_url IS
|
|
89
|
+
'Endereço oficial da norma, https. Insumo da conferência humana — citar a fonte não valida a regra.';
|