@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,516 @@
|
|
|
1
|
+
-- 0058 — o motor operacional: quem pode rodar, quando, sozinho, e o que falta.
|
|
2
|
+
--
|
|
3
|
+
-- ═══ O que a Fase 1 provou, e o que ela deliberadamente não respondeu ══════
|
|
4
|
+
--
|
|
5
|
+
-- A 0056/0057 provaram que sabemos INGERIR: documento canônico, evento fora de
|
|
6
|
+
-- ordem preservado, cursor que não avança sobre dado perdido. Tudo isso é
|
|
7
|
+
-- exercido por uma chamada de função, com um ator humano ou um seed atrás.
|
|
8
|
+
--
|
|
9
|
+
-- Esta migration cria o que falta para a mesma ingestão rodar SOZINHA, todos os
|
|
10
|
+
-- dias, sem que ninguém aperte nada — e, sobretudo, para que ela NÃO rode quando
|
|
11
|
+
-- não deve. Cinco perguntas que a Fase 1 não tinha onde responder:
|
|
12
|
+
--
|
|
13
|
+
-- 1. Duas execuções simultâneas sobre o mesmo cliente: o que impede?
|
|
14
|
+
-- 2. O worker morreu segurando a vez: quem devolve?
|
|
15
|
+
-- 3. O provedor caiu para TODOS os clientes: como não martelamos 200 vezes?
|
|
16
|
+
-- 4. Falta certificado: quem é avisado, e uma vez ou a cada tentativa?
|
|
17
|
+
-- 5. Este município tem coleta automática? — e a resposta honesta na maioria
|
|
18
|
+
-- dos casos é "não sabemos", que precisa ser dizível.
|
|
19
|
+
--
|
|
20
|
+
-- ═══ Nenhuma destas tabelas habilita nada ══════════════════════════════════
|
|
21
|
+
--
|
|
22
|
+
-- A elegibilidade default-deny da 0055 continua sendo o portão. O que nasce aqui
|
|
23
|
+
-- é a MECÂNICA de operar quem já passou por ele. Tabela vazia, como na 0055,
|
|
24
|
+
-- já é a barreira: sem política, a frequência é `disabled`; sem lease, ninguém
|
|
25
|
+
-- roda; sem linha de cobertura, a resposta é `UNKNOWN`, nunca "coberto".
|
|
26
|
+
|
|
27
|
+
-- ── 1 · A vez de rodar ─────────────────────────────────────────────────────
|
|
28
|
+
--
|
|
29
|
+
-- ═══ Por que uma linha, e não um advisory lock ═════════════════════════════
|
|
30
|
+
--
|
|
31
|
+
-- `pg_advisory_lock` morreria com a conexão — o que resolve o item 2 de graça, e
|
|
32
|
+
-- é tentador. Mas ele é INVISÍVEL: ninguém consulta "quem está segurando o
|
|
33
|
+
-- cliente X?" numa tela, e um worker travado apareceria como um cliente que
|
|
34
|
+
-- simplesmente não sincroniza, sem nenhum lugar onde olhar. Uma linha com
|
|
35
|
+
-- `holder`, `heartbeat_at` e `expires_at` responde à mesma pergunta e ainda
|
|
36
|
+
-- deixa rastro de quantas vezes a vez precisou ser tomada de volta.
|
|
37
|
+
--
|
|
38
|
+
-- ═══ A exclusão mútua é do ÍNDICE, não do TypeScript ═══════════════════════
|
|
39
|
+
--
|
|
40
|
+
-- A tomada é um `INSERT ... ON CONFLICT DO UPDATE ... WHERE expirou`. O
|
|
41
|
+
-- PostgreSQL trava a linha conflitante: a segunda transação ESPERA a primeira
|
|
42
|
+
-- confirmar e então reavalia o `WHERE`, que já será falso. Não existe janela
|
|
43
|
+
-- entre "verifiquei que está livre" e "tomei" porque não existem dois momentos.
|
|
44
|
+
-- Um `SELECT ... IF livre THEN UPDATE` teria essa janela, e ela é exatamente a
|
|
45
|
+
-- corrida que deixa dois workers consultando o mesmo CNPJ contra a Receita.
|
|
46
|
+
--
|
|
47
|
+
-- ═══ Por que o fluxo entra na chave ════════════════════════════════════════
|
|
48
|
+
--
|
|
49
|
+
-- A vez é por (escritório, cliente, fonte, ambiente, tipo de documento) porque o
|
|
50
|
+
-- cursor da 0056 tem essa mesma granularidade. Travar por cliente inteiro
|
|
51
|
+
-- impediria a NFS-e de andar enquanto a NF-e roda, sem nenhum ganho: são cursores
|
|
52
|
+
-- diferentes contra provedores diferentes.
|
|
53
|
+
CREATE TABLE IF NOT EXISTS fiscal_sync_lease (
|
|
54
|
+
id uuid PRIMARY KEY,
|
|
55
|
+
tenant_id uuid NOT NULL REFERENCES tenant(id),
|
|
56
|
+
client_id uuid NOT NULL,
|
|
57
|
+
source text NOT NULL,
|
|
58
|
+
environment text NOT NULL
|
|
59
|
+
CHECK (environment IN ('synthetic', 'homologation', 'production')),
|
|
60
|
+
document_type text NOT NULL CHECK (document_type IN ('nfse', 'nfe', 'cte', 'nfce')),
|
|
61
|
+
|
|
62
|
+
-- Quem está com a vez. Identidade do PROCESSO — nome do worker mais um
|
|
63
|
+
-- sufixo aleatório da instância. Nunca hostname de produção, nunca segredo:
|
|
64
|
+
-- esta coluna aparece em tela e em trilha.
|
|
65
|
+
holder text NOT NULL CHECK (holder <> '' AND length(holder) <= 120),
|
|
66
|
+
-- O run que está segurando, quando já existe. Sem FK: o run é gravado no fim
|
|
67
|
+
-- e a vez é tomada no começo — exigir a linha aqui inverteria a ordem.
|
|
68
|
+
run_id uuid,
|
|
69
|
+
|
|
70
|
+
acquired_at timestamptz NOT NULL DEFAULT now(),
|
|
71
|
+
-- Sinal de vida. Um lote longo o renova; um processo morto para de renovar, e
|
|
72
|
+
-- é a distância entre este instante e agora que autoriza a retomada.
|
|
73
|
+
heartbeat_at timestamptz NOT NULL DEFAULT now(),
|
|
74
|
+
-- Prazo. Passado ele, qualquer worker pode tomar a vez — e é ISTO que impede
|
|
75
|
+
-- o bloqueio eterno do item 2. Não existe lease sem prazo neste schema.
|
|
76
|
+
expires_at timestamptz NOT NULL,
|
|
77
|
+
released_at timestamptz,
|
|
78
|
+
-- Quantas vezes esta vez foi tomada de um titular que ainda não a devolvera.
|
|
79
|
+
-- Zero é o normal; um número que cresce é um worker que morre no meio, e essa
|
|
80
|
+
-- é uma pergunta operacional que nenhum log agregado responde.
|
|
81
|
+
recovered_count int NOT NULL DEFAULT 0 CHECK (recovered_count >= 0),
|
|
82
|
+
|
|
83
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
84
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
85
|
+
|
|
86
|
+
CONSTRAINT fiscal_sync_lease_tenant_id_uq UNIQUE (tenant_id, id),
|
|
87
|
+
FOREIGN KEY (tenant_id, client_id) REFERENCES client (tenant_id, id),
|
|
88
|
+
-- Prazo anterior à tomada seria um lease nascido morto: livre para qualquer um
|
|
89
|
+
-- no instante seguinte, o que anula a exclusão em vez de expressá-la.
|
|
90
|
+
CONSTRAINT fiscal_sync_lease_expiry_after_acquire CHECK (expires_at > acquired_at)
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
CREATE UNIQUE INDEX IF NOT EXISTS fiscal_sync_lease_stream_uq
|
|
94
|
+
ON fiscal_sync_lease (tenant_id, client_id, source, environment, document_type);
|
|
95
|
+
CREATE INDEX IF NOT EXISTS fiscal_sync_lease_live_idx
|
|
96
|
+
ON fiscal_sync_lease (tenant_id, expires_at) WHERE released_at IS NULL;
|
|
97
|
+
|
|
98
|
+
ALTER TABLE fiscal_sync_lease ENABLE ROW LEVEL SECURITY;
|
|
99
|
+
ALTER TABLE fiscal_sync_lease FORCE ROW LEVEL SECURITY;
|
|
100
|
+
CREATE POLICY fiscal_sync_lease_tenant_isolation ON fiscal_sync_lease
|
|
101
|
+
USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
|
|
102
|
+
WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
|
|
103
|
+
|
|
104
|
+
COMMENT ON COLUMN fiscal_sync_lease.expires_at IS
|
|
105
|
+
'Prazo da vez. Sem ele, um worker morto bloquearia o cliente para sempre — o modo de falha que este schema recusa a expressar (a coluna é NOT NULL).';
|
|
106
|
+
|
|
107
|
+
-- ── 2 · Com que frequência, e a resposta padrão é "nenhuma" ────────────────
|
|
108
|
+
--
|
|
109
|
+
-- Elegibilidade responde QUEM pode. Esta tabela responde QUANDO — e são
|
|
110
|
+
-- perguntas independentes: um cliente elegível com política ausente não roda,
|
|
111
|
+
-- e ligar a política de um cliente inelegível não o faz rodar.
|
|
112
|
+
--
|
|
113
|
+
-- Duas rédeas para o mesmo portão seriam um defeito; duas rédeas para portões
|
|
114
|
+
-- diferentes são o desenho. A prova é o caso que importa: no dia em que alguém
|
|
115
|
+
-- inscrever a carteira inteira de uma vez, a frequência ainda será `disabled`
|
|
116
|
+
-- para todos, e a inscrição sozinha não dispara nenhuma consulta ao ambiente
|
|
117
|
+
-- nacional.
|
|
118
|
+
CREATE TABLE IF NOT EXISTS fiscal_sync_policy (
|
|
119
|
+
id uuid PRIMARY KEY,
|
|
120
|
+
tenant_id uuid NOT NULL REFERENCES tenant(id),
|
|
121
|
+
-- NULL = padrão do escritório para aquele tipo. A linha do cliente vence.
|
|
122
|
+
client_id uuid,
|
|
123
|
+
document_type text NOT NULL CHECK (document_type IN ('nfse', 'nfe', 'cte', 'nfce')),
|
|
124
|
+
|
|
125
|
+
-- `disabled` é o default da COLUNA além de ser o significado da ausência de
|
|
126
|
+
-- linha. Redundante de propósito: um INSERT que esqueça a frequência não pode
|
|
127
|
+
-- resultar em automação ligada.
|
|
128
|
+
frequency text NOT NULL DEFAULT 'disabled'
|
|
129
|
+
CHECK (frequency IN ('disabled', 'hourly', 'daily', 'business_day')),
|
|
130
|
+
-- Hora local (America/Sao_Paulo) em que a janela diária abre. Buscar nota de
|
|
131
|
+
-- madrugada não é mais rápido e some do horário em que há alguém para olhar.
|
|
132
|
+
window_hour_local int NOT NULL DEFAULT 8 CHECK (window_hour_local BETWEEN 0 AND 23),
|
|
133
|
+
|
|
134
|
+
enabled_at timestamptz,
|
|
135
|
+
enabled_by uuid REFERENCES "user"(id),
|
|
136
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
137
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
138
|
+
|
|
139
|
+
CONSTRAINT fiscal_sync_policy_tenant_id_uq UNIQUE (tenant_id, id),
|
|
140
|
+
FOREIGN KEY (tenant_id, client_id) REFERENCES client (tenant_id, id),
|
|
141
|
+
-- Frequência ligada sem registro de quem ligou e quando é automação sem dono.
|
|
142
|
+
CONSTRAINT fiscal_sync_policy_enabled_has_author
|
|
143
|
+
CHECK (frequency = 'disabled' OR (enabled_at IS NOT NULL))
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
CREATE UNIQUE INDEX IF NOT EXISTS fiscal_sync_policy_client_uq
|
|
147
|
+
ON fiscal_sync_policy (tenant_id, client_id, document_type) WHERE client_id IS NOT NULL;
|
|
148
|
+
CREATE UNIQUE INDEX IF NOT EXISTS fiscal_sync_policy_default_uq
|
|
149
|
+
ON fiscal_sync_policy (tenant_id, document_type) WHERE client_id IS NULL;
|
|
150
|
+
|
|
151
|
+
ALTER TABLE fiscal_sync_policy ENABLE ROW LEVEL SECURITY;
|
|
152
|
+
ALTER TABLE fiscal_sync_policy FORCE ROW LEVEL SECURITY;
|
|
153
|
+
CREATE POLICY fiscal_sync_policy_tenant_isolation ON fiscal_sync_policy
|
|
154
|
+
USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
|
|
155
|
+
WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
|
|
156
|
+
|
|
157
|
+
-- ── 3 · O provedor caiu para todos ─────────────────────────────────────────
|
|
158
|
+
--
|
|
159
|
+
-- O `next_attempt_after` da 0056 é POR CLIENTE, e resolve o caso de um cliente
|
|
160
|
+
-- cuja consulta falhou. Ele não resolve o caso oposto e mais provável: o
|
|
161
|
+
-- ambiente nacional fora do ar às 8h. Com cem clientes elegíveis, o recuo por
|
|
162
|
+
-- cliente produz cem requisições contra um serviço que já respondeu que está
|
|
163
|
+
-- indisponível — precisamente o padrão de consumo que o cStat 656 pune, e cujo
|
|
164
|
+
-- bloqueio recai sobre o CNPJ de cem terceiros.
|
|
165
|
+
--
|
|
166
|
+
-- Um estado por (escritório, fonte, ambiente) faz o segundo cliente já nascer
|
|
167
|
+
-- barrado pela falha do primeiro. `half_open` existe para que a reabertura seja
|
|
168
|
+
-- um cliente de prova, e não a carteira inteira de uma vez.
|
|
169
|
+
CREATE TABLE IF NOT EXISTS fiscal_provider_circuit (
|
|
170
|
+
id uuid PRIMARY KEY,
|
|
171
|
+
tenant_id uuid NOT NULL REFERENCES tenant(id),
|
|
172
|
+
source text NOT NULL,
|
|
173
|
+
environment text NOT NULL
|
|
174
|
+
CHECK (environment IN ('synthetic', 'homologation', 'production')),
|
|
175
|
+
|
|
176
|
+
state text NOT NULL DEFAULT 'closed'
|
|
177
|
+
CHECK (state IN ('closed', 'open', 'half_open')),
|
|
178
|
+
consecutive_failures int NOT NULL DEFAULT 0 CHECK (consecutive_failures >= 0),
|
|
179
|
+
last_error_class text,
|
|
180
|
+
opened_at timestamptz,
|
|
181
|
+
open_until timestamptz,
|
|
182
|
+
last_failure_at timestamptz,
|
|
183
|
+
last_success_at timestamptz,
|
|
184
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
185
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
186
|
+
|
|
187
|
+
CONSTRAINT fiscal_provider_circuit_tenant_id_uq UNIQUE (tenant_id, id),
|
|
188
|
+
-- Aberto sem prazo é fechado para sempre. O banco recusa a linha em vez de
|
|
189
|
+
-- confiar que o TypeScript sempre preencherá a data.
|
|
190
|
+
CONSTRAINT fiscal_provider_circuit_open_has_deadline
|
|
191
|
+
CHECK (state <> 'open' OR open_until IS NOT NULL)
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
CREATE UNIQUE INDEX IF NOT EXISTS fiscal_provider_circuit_uq
|
|
195
|
+
ON fiscal_provider_circuit (tenant_id, source, environment);
|
|
196
|
+
|
|
197
|
+
ALTER TABLE fiscal_provider_circuit ENABLE ROW LEVEL SECURITY;
|
|
198
|
+
ALTER TABLE fiscal_provider_circuit FORCE ROW LEVEL SECURITY;
|
|
199
|
+
CREATE POLICY fiscal_provider_circuit_tenant_isolation ON fiscal_provider_circuit
|
|
200
|
+
USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
|
|
201
|
+
WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
|
|
202
|
+
|
|
203
|
+
-- ── 4 · O que só uma pessoa resolve ────────────────────────────────────────
|
|
204
|
+
--
|
|
205
|
+
-- Certificado vencido não é falha transitória: nenhuma quantidade de tentativas
|
|
206
|
+
-- renova um A1. Mas transformar cada tentativa numa tarefa produziria uma fila
|
|
207
|
+
-- com trinta linhas idênticas até o fim do mês — e uma fila que ninguém
|
|
208
|
+
-- consegue ler é indistinguível de uma fila vazia.
|
|
209
|
+
--
|
|
210
|
+
-- Por isso a chave: UMA linha aberta por (escritório, cliente, motivo, fonte,
|
|
211
|
+
-- ambiente), com contador. A trigésima ocorrência incrementa `occurrences` e
|
|
212
|
+
-- move `last_seen_at`; ela não cria a trigésima tarefa.
|
|
213
|
+
CREATE TABLE IF NOT EXISTS fiscal_intervention (
|
|
214
|
+
id uuid PRIMARY KEY,
|
|
215
|
+
tenant_id uuid NOT NULL REFERENCES tenant(id),
|
|
216
|
+
-- NULL quando o problema é do PROVEDOR e não de um cliente específico.
|
|
217
|
+
client_id uuid,
|
|
218
|
+
kind text NOT NULL
|
|
219
|
+
CHECK (kind IN ('auth_required', 'certificate_expired', 'certificate_invalid',
|
|
220
|
+
'provider_access_denied', 'unsupported_source',
|
|
221
|
+
'repeated_failure')),
|
|
222
|
+
source text,
|
|
223
|
+
environment text CHECK (environment IS NULL OR
|
|
224
|
+
environment IN ('synthetic', 'homologation', 'production')),
|
|
225
|
+
document_type text CHECK (document_type IS NULL OR
|
|
226
|
+
document_type IN ('nfse', 'nfe', 'cte', 'nfce')),
|
|
227
|
+
|
|
228
|
+
state text NOT NULL DEFAULT 'open'
|
|
229
|
+
CHECK (state IN ('open', 'acknowledged', 'resolved')),
|
|
230
|
+
-- Classe e contexto. NUNCA a resposta do provedor: um erro de TLS mútuo cita
|
|
231
|
+
-- caminho de arquivo e sujeito do certificado, e esta coluna vai para a tela.
|
|
232
|
+
detail text NOT NULL,
|
|
233
|
+
occurrences int NOT NULL DEFAULT 1 CHECK (occurrences > 0),
|
|
234
|
+
|
|
235
|
+
first_seen_at timestamptz NOT NULL DEFAULT now(),
|
|
236
|
+
last_seen_at timestamptz NOT NULL DEFAULT now(),
|
|
237
|
+
resolved_at timestamptz,
|
|
238
|
+
resolved_by uuid REFERENCES "user"(id),
|
|
239
|
+
|
|
240
|
+
CONSTRAINT fiscal_intervention_tenant_id_uq UNIQUE (tenant_id, id),
|
|
241
|
+
FOREIGN KEY (tenant_id, client_id) REFERENCES client (tenant_id, id),
|
|
242
|
+
-- Resolvido sem data seria uma fila que esvazia sem deixar quando.
|
|
243
|
+
CONSTRAINT fiscal_intervention_resolved_coherent
|
|
244
|
+
CHECK ((state = 'resolved') = (resolved_at IS NOT NULL))
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
-- A deduplicação é do índice. `coalesce` porque NULL não colide com NULL em
|
|
248
|
+
-- índice único, e sem isto cada tentativa criaria uma linha nova para um
|
|
249
|
+
-- problema de provedor (client_id NULL) — a fila ilegível que a tabela evita.
|
|
250
|
+
CREATE UNIQUE INDEX IF NOT EXISTS fiscal_intervention_open_uq
|
|
251
|
+
ON fiscal_intervention (
|
|
252
|
+
tenant_id,
|
|
253
|
+
coalesce(client_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
254
|
+
kind,
|
|
255
|
+
coalesce(source, ''),
|
|
256
|
+
coalesce(environment, '')
|
|
257
|
+
) WHERE state <> 'resolved';
|
|
258
|
+
CREATE INDEX IF NOT EXISTS fiscal_intervention_open_idx
|
|
259
|
+
ON fiscal_intervention (tenant_id, state, last_seen_at DESC);
|
|
260
|
+
|
|
261
|
+
ALTER TABLE fiscal_intervention ENABLE ROW LEVEL SECURITY;
|
|
262
|
+
ALTER TABLE fiscal_intervention FORCE ROW LEVEL SECURITY;
|
|
263
|
+
CREATE POLICY fiscal_intervention_tenant_isolation ON fiscal_intervention
|
|
264
|
+
USING (
|
|
265
|
+
tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid
|
|
266
|
+
AND (
|
|
267
|
+
NULLIF(current_setting('app.current_client', true), '') IS NULL
|
|
268
|
+
OR client_id = NULLIF(current_setting('app.current_client', true), '')::uuid
|
|
269
|
+
)
|
|
270
|
+
)
|
|
271
|
+
WITH CHECK (
|
|
272
|
+
tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid
|
|
273
|
+
AND (
|
|
274
|
+
NULLIF(current_setting('app.current_client', true), '') IS NULL
|
|
275
|
+
OR client_id = NULLIF(current_setting('app.current_client', true), '')::uuid
|
|
276
|
+
)
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
-- ── 5 · Cobertura: o que sabemos, e o tamanho do que não sabemos ───────────
|
|
280
|
+
--
|
|
281
|
+
-- ═══ A afirmação perigosa ══════════════════════════════════════════════════
|
|
282
|
+
--
|
|
283
|
+
-- "O ADN cobre a NFS-e do Brasil inteiro" é falsa, e é a frase que um produto
|
|
284
|
+
-- nacional diz sem perceber. A NFS-e nacional depende de ADESÃO do município ao
|
|
285
|
+
-- convênio; os que não aderiram continuam emitindo pelo próprio portal, com
|
|
286
|
+
-- login próprio e nenhum NSU. Vender coleta automática para um cliente cuja
|
|
287
|
+
-- prefeitura não aderiu é prometer trabalho que ninguém vai fazer.
|
|
288
|
+
--
|
|
289
|
+
-- ═══ Por que a evidência é uma RESTRIÇÃO, e não uma convenção ══════════════
|
|
290
|
+
--
|
|
291
|
+
-- `fiscal_coverage_claim_needs_evidence` torna INGRAVÁVEL qualquer status que
|
|
292
|
+
-- não seja `UNKNOWN` sem data de verificação e sem fonte. Não há caminho pelo
|
|
293
|
+
-- qual alguém — ou um agente com pressa — declare "coberto" e siga adiante:
|
|
294
|
+
-- afirmar cobertura passa a custar uma referência oficial, no banco, ou o INSERT
|
|
295
|
+
-- falha. `UNKNOWN` é o único estado gratuito, e é o correto por padrão.
|
|
296
|
+
--
|
|
297
|
+
-- ═══ Catálogo GLOBAL, sem tenant e sem RLS ═════════════════════════════════
|
|
298
|
+
--
|
|
299
|
+
-- A adesão de um município ao ADN não é um fato de escritório: é o mesmo para
|
|
300
|
+
-- todos. Uma cópia por tenant divergiria na primeira atualização. Segue o padrão
|
|
301
|
+
-- de `fiscal_attribute_definition` e `platform_capability` — sem `tenant_id`,
|
|
302
|
+
-- logo sem RLS que o proteja, logo o runtime SÓ LÊ (o REVOKE está em
|
|
303
|
+
-- `migrate.ts`, junto dos irmãos).
|
|
304
|
+
CREATE TABLE IF NOT EXISTS fiscal_coverage (
|
|
305
|
+
id uuid PRIMARY KEY,
|
|
306
|
+
document_type text NOT NULL CHECK (document_type IN ('nfse', 'nfe', 'cte', 'nfce')),
|
|
307
|
+
jurisdiction_scope text NOT NULL
|
|
308
|
+
CHECK (jurisdiction_scope IN ('national', 'state', 'municipal')),
|
|
309
|
+
-- UF (2 letras) ou código IBGE do município (7 dígitos). NULL = a regra
|
|
310
|
+
-- PADRÃO daquele escopo, que é o que responde por toda jurisdição não medida.
|
|
311
|
+
jurisdiction_code text CHECK (jurisdiction_code IS NULL OR
|
|
312
|
+
jurisdiction_code ~ '^([A-Z]{2}|[0-9]{7})$'),
|
|
313
|
+
provider text NOT NULL,
|
|
314
|
+
|
|
315
|
+
coverage_status text NOT NULL
|
|
316
|
+
CHECK (coverage_status IN ('SUPPORTED_OFFICIAL', 'SUPPORTED_PROVIDER',
|
|
317
|
+
'SUPPORTED_MANUAL_FALLBACK', 'UNKNOWN',
|
|
318
|
+
'UNSUPPORTED')),
|
|
319
|
+
automation_method text CHECK (automation_method IS NULL OR
|
|
320
|
+
automation_method IN ('dfe_nsu_soap', 'dfe_nsu_rest',
|
|
321
|
+
'provider_api', 'portal_manual',
|
|
322
|
+
'manual_upload')),
|
|
323
|
+
credential_requirement text NOT NULL DEFAULT 'unknown'
|
|
324
|
+
CHECK (credential_requirement IN ('none', 'icp_brasil_mtls',
|
|
325
|
+
'municipal_portal_login',
|
|
326
|
+
'provider_api_key', 'unknown')),
|
|
327
|
+
-- Ambientes em que o caminho existe. Vazio = nenhum exercível hoje.
|
|
328
|
+
environments text[] NOT NULL DEFAULT '{}',
|
|
329
|
+
fallback_type text CHECK (fallback_type IS NULL OR
|
|
330
|
+
fallback_type IN ('manual_upload', 'client_delivery',
|
|
331
|
+
'issuer_export', 'none')),
|
|
332
|
+
last_verified_at date,
|
|
333
|
+
source_reference text,
|
|
334
|
+
notes text,
|
|
335
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
336
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
337
|
+
|
|
338
|
+
CONSTRAINT fiscal_coverage_claim_needs_evidence
|
|
339
|
+
CHECK (coverage_status = 'UNKNOWN'
|
|
340
|
+
OR (last_verified_at IS NOT NULL AND source_reference IS NOT NULL))
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
CREATE UNIQUE INDEX IF NOT EXISTS fiscal_coverage_key_uq
|
|
344
|
+
ON fiscal_coverage (document_type, jurisdiction_scope, coalesce(jurisdiction_code, ''), provider);
|
|
345
|
+
|
|
346
|
+
COMMENT ON CONSTRAINT fiscal_coverage_claim_needs_evidence ON fiscal_coverage IS
|
|
347
|
+
'Afirmar cobertura custa fonte e data. UNKNOWN é o único status gratuito — e o correto quando ninguém mediu.';
|
|
348
|
+
|
|
349
|
+
-- Semeado AQUI, e não num seed posterior, porque um banco limpo sem estas linhas
|
|
350
|
+
-- responderia "não sabemos" até que alguém rodasse outro comando — e a diferença
|
|
351
|
+
-- entre "medimos e não há" e "ninguém rodou o seed" é invisível na tela.
|
|
352
|
+
INSERT INTO fiscal_coverage (
|
|
353
|
+
id, document_type, jurisdiction_scope, jurisdiction_code, provider, coverage_status,
|
|
354
|
+
automation_method, credential_requirement, environments, fallback_type,
|
|
355
|
+
last_verified_at, source_reference, notes
|
|
356
|
+
) VALUES
|
|
357
|
+
-- NF-e: distribuição nacional confirmada. E a limitação que decide produto:
|
|
358
|
+
-- o DF-e NÃO devolve as notas que a própria empresa emitiu.
|
|
359
|
+
(gen_random_uuid(), 'nfe', 'national', NULL, 'sefaz_dfe', 'SUPPORTED_OFFICIAL',
|
|
360
|
+
'dfe_nsu_soap', 'icp_brasil_mtls', ARRAY['homologation', 'production'], 'issuer_export',
|
|
361
|
+
DATE '2026-09-07', 'NT 2014.002 v1.02c (ago/2020) — WS NFeDistribuicaoDFe, nfeDistDFeInteresse',
|
|
362
|
+
'Documentos EMITIDOS pela própria empresa não são distribuídos: para esses, a origem é o emissor do cliente. Espera obrigatória de 1h após cStat 137; consultar antes devolve cStat 656 e bloqueia o CNPJ do contribuinte.'),
|
|
363
|
+
|
|
364
|
+
-- CT-e: mesma mecânica, mesma norma irmã. Forma medida na pesquisa da Onda B;
|
|
365
|
+
-- a distribuição em si nunca foi exercida — por isso o `notes` diz isso.
|
|
366
|
+
(gen_random_uuid(), 'cte', 'national', NULL, 'sefaz_dfe', 'SUPPORTED_OFFICIAL',
|
|
367
|
+
'dfe_nsu_soap', 'icp_brasil_mtls', ARRAY['homologation', 'production'], 'issuer_export',
|
|
368
|
+
DATE '2026-09-07', 'NT 2015.002 — WS CTeDistribuicaoDFe',
|
|
369
|
+
'Forma de distribuição idêntica à da NF-e (distNSU/consNSU, ultNSU/maxNSU, docZip). Nenhuma chamada autenticada foi feita; adapter não implementado.'),
|
|
370
|
+
|
|
371
|
+
-- NFS-e nacional: a API existe e está documentada. O que ela alcança é outra
|
|
372
|
+
-- pergunta — respondida pela linha municipal abaixo.
|
|
373
|
+
(gen_random_uuid(), 'nfse', 'national', NULL, 'nfse_adn', 'SUPPORTED_OFFICIAL',
|
|
374
|
+
'dfe_nsu_rest', 'icp_brasil_mtls', ARRAY['homologation', 'production'], 'client_delivery',
|
|
375
|
+
DATE '2026-09-07', 'Manual dos Contribuintes — API do ADN v1.0 (12/02/2026), gov.br/nfse',
|
|
376
|
+
'Alcança apenas municípios que aderiram ao convênio da NFS-e nacional. O host exige TLS mútuo com certificado ICP-Brasil, inclusive para servir a documentação.'),
|
|
377
|
+
|
|
378
|
+
-- A linha que impede a afirmação perigosa. É ela que qualquer pergunta
|
|
379
|
+
-- municipal sem medição encontra — e ela responde `UNKNOWN`, não "coberto".
|
|
380
|
+
(gen_random_uuid(), 'nfse', 'municipal', NULL, 'municipio', 'UNKNOWN',
|
|
381
|
+
NULL, 'unknown', ARRAY[]::text[], 'manual_upload',
|
|
382
|
+
NULL, NULL,
|
|
383
|
+
'Adesão do município ao ADN não verificada. Enquanto não for, a coleta da NFS-e daquele município é desconhecida — nunca automática por herança da linha nacional.'),
|
|
384
|
+
|
|
385
|
+
-- NFC-e não é distribuída ao consumidor por DF-e. Não sabemos de caminho, e
|
|
386
|
+
-- dizê-lo é mais útil do que inventar um.
|
|
387
|
+
(gen_random_uuid(), 'nfce', 'national', NULL, 'sefaz_dfe', 'UNKNOWN',
|
|
388
|
+
NULL, 'unknown', ARRAY[]::text[], 'issuer_export',
|
|
389
|
+
NULL, NULL,
|
|
390
|
+
'Nenhuma pesquisa oficial desta onda cobriu a distribuição de NFC-e. Status permanece desconhecido até alguém medir.')
|
|
391
|
+
ON CONFLICT DO NOTHING;
|
|
392
|
+
|
|
393
|
+
-- ── 6 · O que o cursor não guardava sobre a EXECUÇÃO ───────────────────────
|
|
394
|
+
--
|
|
395
|
+
-- A 0056 modelou a posição no provedor (`last_nsu`, `max_nsu`) e a espera
|
|
396
|
+
-- imposta por ele (`next_attempt_after`). Faltava o outro eixo: a cadência que
|
|
397
|
+
-- NÓS escolhemos. São coisas distintas e a distinção importa — `next_attempt_after`
|
|
398
|
+
-- é o provedor dizendo "não me procure antes disso" e desobedecê-lo bloqueia o
|
|
399
|
+
-- CNPJ do cliente; `next_eligible_run_at` é a política do escritório dizendo
|
|
400
|
+
-- "uma vez por dia basta" e antecipá-lo é só desperdício.
|
|
401
|
+
--
|
|
402
|
+
-- O gate efetivo é o MAIOR dos dois. Colapsá-los numa coluna só faria uma
|
|
403
|
+
-- política diária apagar uma espera obrigatória de uma hora.
|
|
404
|
+
ALTER TABLE fiscal_sync_cursor ADD COLUMN IF NOT EXISTS last_started_at timestamptz;
|
|
405
|
+
ALTER TABLE fiscal_sync_cursor ADD COLUMN IF NOT EXISTS last_completed_at timestamptz;
|
|
406
|
+
ALTER TABLE fiscal_sync_cursor ADD COLUMN IF NOT EXISTS next_eligible_run_at timestamptz;
|
|
407
|
+
|
|
408
|
+
COMMENT ON COLUMN fiscal_sync_cursor.next_eligible_run_at IS
|
|
409
|
+
'Cadência NOSSA (política). Distinta de next_attempt_after, que é espera do PROVEDOR: desobedecer aquela bloqueia o CNPJ do cliente; antecipar esta é só desperdício. O gate é o maior dos dois.';
|
|
410
|
+
|
|
411
|
+
-- ── 7 · A trilha do lote ganha identidade de execução ──────────────────────
|
|
412
|
+
--
|
|
413
|
+
-- `fiscal_sync_run` (0056) já registra contadores e cursor. Faltava dizer QUEM
|
|
414
|
+
-- executou (qual worker, em qual versão) e o que aconteceu com os EVENTOS — que
|
|
415
|
+
-- foi justamente o eixo que a Fase 1 · R1 teve de corrigir no pipeline e não
|
|
416
|
+
-- chegou a refletir na trilha.
|
|
417
|
+
ALTER TABLE fiscal_sync_run ADD COLUMN IF NOT EXISTS job_name text;
|
|
418
|
+
ALTER TABLE fiscal_sync_run ADD COLUMN IF NOT EXISTS job_version int;
|
|
419
|
+
ALTER TABLE fiscal_sync_run ADD COLUMN IF NOT EXISTS lease_id uuid;
|
|
420
|
+
ALTER TABLE fiscal_sync_run ADD COLUMN IF NOT EXISTS events_pending int NOT NULL DEFAULT 0;
|
|
421
|
+
ALTER TABLE fiscal_sync_run ADD COLUMN IF NOT EXISTS events_applied int NOT NULL DEFAULT 0;
|
|
422
|
+
ALTER TABLE fiscal_sync_run ADD COLUMN IF NOT EXISTS events_reconciled int NOT NULL DEFAULT 0;
|
|
423
|
+
-- Estado de retentativa DESTA execução, para que a decisão fique auditável sem
|
|
424
|
+
-- ter de reconstruí-la a partir da classe do erro.
|
|
425
|
+
ALTER TABLE fiscal_sync_run ADD COLUMN IF NOT EXISTS retryable boolean;
|
|
426
|
+
ALTER TABLE fiscal_sync_run ADD COLUMN IF NOT EXISTS retry_after timestamptz;
|
|
427
|
+
ALTER TABLE fiscal_sync_run ADD COLUMN IF NOT EXISTS attempt int;
|
|
428
|
+
|
|
429
|
+
DO $$
|
|
430
|
+
BEGIN
|
|
431
|
+
IF NOT EXISTS (SELECT FROM pg_constraint WHERE conname = 'fiscal_sync_run_event_counts_nonneg') THEN
|
|
432
|
+
ALTER TABLE fiscal_sync_run ADD CONSTRAINT fiscal_sync_run_event_counts_nonneg
|
|
433
|
+
CHECK (events_pending >= 0 AND events_applied >= 0 AND events_reconciled >= 0);
|
|
434
|
+
END IF;
|
|
435
|
+
IF NOT EXISTS (SELECT FROM pg_constraint WHERE conname = 'fiscal_sync_run_blocked_has_no_events') THEN
|
|
436
|
+
-- A mesma invariante do `..._blocked_is_inert`, estendida aos eventos: um
|
|
437
|
+
-- lote que não rodou não pode ter mexido em evento nenhum. O banco recusa a
|
|
438
|
+
-- linha incoerente em vez de confiar na ordem das atribuições.
|
|
439
|
+
ALTER TABLE fiscal_sync_run ADD CONSTRAINT fiscal_sync_run_blocked_has_no_events
|
|
440
|
+
CHECK (outcome NOT IN ('blocked_eligibility', 'blocked_credential', 'blocked_contract', 'throttled')
|
|
441
|
+
OR (events_pending = 0 AND events_applied = 0 AND events_reconciled = 0));
|
|
442
|
+
END IF;
|
|
443
|
+
END $$;
|
|
444
|
+
|
|
445
|
+
CREATE INDEX IF NOT EXISTS fiscal_sync_run_job_idx
|
|
446
|
+
ON fiscal_sync_run (tenant_id, job_name, started_at DESC);
|
|
447
|
+
|
|
448
|
+
-- ── 8 · A capacidade que o worker desta fase exige ─────────────────────────
|
|
449
|
+
--
|
|
450
|
+
-- Declarada na MESMA migration que cria o schema que a sustenta (expand →
|
|
451
|
+
-- declare → deploy → contract). Enquanto ela não existir no banco, o worker fica
|
|
452
|
+
-- INCOMPATIBLE pelo Gate B0 e não escreve nada — que é a janela de skew
|
|
453
|
+
-- resolvida sem coordenação de deploy.
|
|
454
|
+
INSERT INTO platform_capability (key, label, contract_version, declared_by_migration) VALUES
|
|
455
|
+
('office.fiscal-sync-worker.v1',
|
|
456
|
+
'Motor operacional de sincronização fiscal: lease, política, circuito, fila humana e cobertura',
|
|
457
|
+
1, '0058')
|
|
458
|
+
ON CONFLICT (key) DO NOTHING;
|
|
459
|
+
|
|
460
|
+
-- ── 9 · Reprocessar o que ficou preservado ─────────────────────────────────
|
|
461
|
+
--
|
|
462
|
+
-- A caixa de entrada da 0057 guarda duas coisas: evento à espera do alvo, e
|
|
463
|
+
-- item que o parser não conseguiu ler. O segundo caso tem um desfecho que a
|
|
464
|
+
-- 0057 não previu — e que é exatamente o motivo de o item ter sido guardado:
|
|
465
|
+
-- **o parser melhora**. Um XML ilegível hoje é legível depois de um deploy, e
|
|
466
|
+
-- reprocessá-lo pode produzir um DOCUMENTO, não um evento.
|
|
467
|
+
--
|
|
468
|
+
-- As restrições da 0057 tornam esse desfecho ingravável: `applied` exige
|
|
469
|
+
-- `applied_event_id`, e qualquer estado diferente de `invalid` exige
|
|
470
|
+
-- `target_access_key` e `event_type` — campos que um documento não tem.
|
|
471
|
+
--
|
|
472
|
+
-- O item ficaria preso em `invalid` para sempre, com o documento já gravado ao
|
|
473
|
+
-- lado dele: um registro dizendo "não deu para ler" sobre bytes que foram lidos.
|
|
474
|
+
ALTER TABLE fiscal_ingestion_inbox ADD COLUMN IF NOT EXISTS applied_document_id uuid;
|
|
475
|
+
ALTER TABLE fiscal_ingestion_inbox ADD COLUMN IF NOT EXISTS reprocessed_at timestamptz;
|
|
476
|
+
ALTER TABLE fiscal_ingestion_inbox ADD COLUMN IF NOT EXISTS reprocess_count int NOT NULL DEFAULT 0;
|
|
477
|
+
|
|
478
|
+
DO $$
|
|
479
|
+
BEGIN
|
|
480
|
+
IF NOT EXISTS (
|
|
481
|
+
SELECT FROM pg_constraint WHERE conname = 'fiscal_ingestion_inbox_applied_document_fk'
|
|
482
|
+
) THEN
|
|
483
|
+
ALTER TABLE fiscal_ingestion_inbox
|
|
484
|
+
ADD CONSTRAINT fiscal_ingestion_inbox_applied_document_fk
|
|
485
|
+
FOREIGN KEY (tenant_id, applied_document_id) REFERENCES fiscal_document (tenant_id, id);
|
|
486
|
+
END IF;
|
|
487
|
+
END $$;
|
|
488
|
+
|
|
489
|
+
ALTER TABLE fiscal_ingestion_inbox
|
|
490
|
+
DROP CONSTRAINT IF EXISTS fiscal_ingestion_inbox_applied_has_event;
|
|
491
|
+
ALTER TABLE fiscal_ingestion_inbox
|
|
492
|
+
ADD CONSTRAINT fiscal_ingestion_inbox_applied_has_destination
|
|
493
|
+
-- Aplicado exige DESTINO: um evento sobre um documento, ou o documento em si.
|
|
494
|
+
-- A restrição continua fechando o caso que importa — `applied` sem nada do
|
|
495
|
+
-- outro lado —, e passa a admitir o segundo destino legítimo.
|
|
496
|
+
CHECK (state <> 'applied'
|
|
497
|
+
OR (applied_at IS NOT NULL
|
|
498
|
+
AND (applied_event_id IS NOT NULL OR applied_document_id IS NOT NULL)));
|
|
499
|
+
|
|
500
|
+
ALTER TABLE fiscal_ingestion_inbox
|
|
501
|
+
DROP CONSTRAINT IF EXISTS fiscal_ingestion_inbox_event_is_addressable;
|
|
502
|
+
ALTER TABLE fiscal_ingestion_inbox
|
|
503
|
+
ADD CONSTRAINT fiscal_ingestion_inbox_event_is_addressable
|
|
504
|
+
-- Item à espera de alvo continua obrigado a dizer QUAL alvo e QUE evento — é
|
|
505
|
+
-- essa exigência que impede um item sem destino de se disfarçar de pendente.
|
|
506
|
+
-- O que passa a ser dispensado é o item já resolvido como documento.
|
|
507
|
+
CHECK (state <> 'pending_target'
|
|
508
|
+
OR (target_access_key IS NOT NULL AND event_type IS NOT NULL));
|
|
509
|
+
|
|
510
|
+
ALTER TABLE fiscal_ingestion_inbox
|
|
511
|
+
DROP CONSTRAINT IF EXISTS fiscal_ingestion_inbox_reprocess_nonneg;
|
|
512
|
+
ALTER TABLE fiscal_ingestion_inbox
|
|
513
|
+
ADD CONSTRAINT fiscal_ingestion_inbox_reprocess_nonneg CHECK (reprocess_count >= 0);
|
|
514
|
+
|
|
515
|
+
COMMENT ON COLUMN fiscal_ingestion_inbox.applied_document_id IS
|
|
516
|
+
'Documento produzido pelo REPROCESSAMENTO de um item antes ilegível. O parser melhora; os bytes guardados existem justamente para essa segunda chance.';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
-- 0059 — qual parser leu este lote.
|
|
2
|
+
--
|
|
3
|
+
-- ═══ A pergunta que a Fase 3 tornou respondível ════════════════════════════
|
|
4
|
+
--
|
|
5
|
+
-- O leiaute da NFS-e nacional mudou duas vezes entre dez/2025 e fev/2026 (XSD
|
|
6
|
+
-- v1.00 → v1.01, e três anexos revisados no intervalo). Vai mudar de novo: a
|
|
7
|
+
-- reforma tributária já colocou o bloco `IBSCBS` no schema vigente e ele ainda
|
|
8
|
+
-- vai ganhar regra de apuração.
|
|
9
|
+
--
|
|
10
|
+
-- Quando isso acontecer, a pergunta operacional é uma só: **quais lotes foram
|
|
11
|
+
-- lidos pelo parser antigo?** Sem esta coluna a resposta exige cruzar a data da
|
|
12
|
+
-- linha com o `git log` do deploy — uma reconstrução que erra na primeira janela
|
|
13
|
+
-- de rollback, porque a data do commit não é a data em que ele estava servindo.
|
|
14
|
+
--
|
|
15
|
+
-- `source` não serve para isso: ele identifica a FONTE (`nfse_adn`), e a fonte é
|
|
16
|
+
-- a mesma antes e depois da mudança de contrato. Sobrecarregá-lo com a versão
|
|
17
|
+
-- quebraria o casamento de adapter no reprocessamento, que compara `source` por
|
|
18
|
+
-- igualdade para escolher quem sabe ler os bytes guardados.
|
|
19
|
+
--
|
|
20
|
+
-- ═══ Por que a coluna é anulável, e não tem default ════════════════════════
|
|
21
|
+
--
|
|
22
|
+
-- `NULL` é a resposta honesta para as linhas que existiam antes desta migration
|
|
23
|
+
-- e para os adapters sintéticos, que não implementam contrato externo nenhum. Um
|
|
24
|
+
-- default como `'nfse-adn:v1'` gravaria uma afirmação falsa em linha nenhuma
|
|
25
|
+
-- para tornar a coluna "mais completa" — e uma coluna que mente é pior que uma
|
|
26
|
+
-- coluna vazia, porque a vazia não engana quem investiga.
|
|
27
|
+
|
|
28
|
+
ALTER TABLE fiscal_sync_run ADD COLUMN IF NOT EXISTS contract_version text;
|
|
29
|
+
|
|
30
|
+
COMMENT ON COLUMN fiscal_sync_run.contract_version IS
|
|
31
|
+
'Versão do contrato do provedor que o adapter implementava neste lote (ex.: nfse-adn:v1). NULL para adapter sintético ou linha anterior à 0059.';
|
|
32
|
+
|
|
33
|
+
-- ── Segurança de skew ──────────────────────────────────────────────────────
|
|
34
|
+
--
|
|
35
|
+
-- A aplicação que grava esta coluna não pode rodar contra um banco que ainda não
|
|
36
|
+
-- a tem: o `INSERT` falharia no meio do ciclo, depois do lease tomado e
|
|
37
|
+
-- possivelmente depois da requisição ao provedor — o pior lugar para falhar,
|
|
38
|
+
-- porque o consumo já foi gasto e o cursor não avançou.
|
|
39
|
+
--
|
|
40
|
+
-- O Gate B0 resolve isso sem coordenação de deploy, e o mecanismo correto é uma
|
|
41
|
+
-- CAPACIDADE NOVA — não um `contract_version` maior.
|
|
42
|
+
--
|
|
43
|
+
-- ═══ Por que não bumpar `contract_version` ═════════════════════════════════
|
|
44
|
+
--
|
|
45
|
+
-- `platform_capability.contract_version` é a versão do FORMATO DO REGISTRO, e é
|
|
46
|
+
-- global: `probePlatformCapabilities` recusa a tabela inteira quando as linhas
|
|
47
|
+
-- divergem ("registro com versões de contrato divergentes" → `absent`). Subir a
|
|
48
|
+
-- versão de uma linha só derrubaria TODOS os workers — cobrança e faturamento
|
|
49
|
+
-- inclusive — por causa de uma coluna do motor fiscal.
|
|
50
|
+
--
|
|
51
|
+
-- A dimensão certa é a chave. Capacidade é comparada por CONTINÊNCIA: o worker
|
|
52
|
+
-- exige um conjunto, o banco declara outro, e basta que o declarado contenha o
|
|
53
|
+
-- exigido. É por isso que "schema novo com worker velho" funciona de graça — e
|
|
54
|
+
-- é a mesma propriedade que faz "worker novo com schema velho" fechar sozinho.
|
|
55
|
+
INSERT INTO platform_capability (key, label, contract_version, declared_by_migration) VALUES
|
|
56
|
+
('office.fiscal-contract-version.v1',
|
|
57
|
+
'A execução fiscal registra a versão do contrato do provedor que a processou',
|
|
58
|
+
1, '0059')
|
|
59
|
+
ON CONFLICT (key) DO NOTHING;
|