@baruk/core 0.4.2 → 0.6.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.
Files changed (576) hide show
  1. package/.baruk-provenance.json +4 -4
  2. package/CHANGELOG.md +42 -0
  3. package/dist/audit/repository.d.ts +33 -1
  4. package/dist/audit/repository.d.ts.map +1 -1
  5. package/dist/audit/repository.js +41 -7
  6. package/dist/audit/repository.js.map +1 -1
  7. package/dist/auth/service.d.ts +21 -0
  8. package/dist/auth/service.d.ts.map +1 -1
  9. package/dist/auth/service.js +46 -1
  10. package/dist/auth/service.js.map +1 -1
  11. package/dist/billing/ar-repository.d.ts +41 -1
  12. package/dist/billing/ar-repository.d.ts.map +1 -1
  13. package/dist/billing/ar-repository.js +82 -6
  14. package/dist/billing/ar-repository.js.map +1 -1
  15. package/dist/billing/provider-account.d.ts +38 -0
  16. package/dist/billing/provider-account.d.ts.map +1 -1
  17. package/dist/billing/provider-account.js +58 -2
  18. package/dist/billing/provider-account.js.map +1 -1
  19. package/dist/billing/repository.d.ts +44 -0
  20. package/dist/billing/repository.d.ts.map +1 -1
  21. package/dist/billing/repository.js +49 -5
  22. package/dist/billing/repository.js.map +1 -1
  23. package/dist/cjs/audit/repository.js +41 -7
  24. package/dist/cjs/audit/repository.js.map +1 -1
  25. package/dist/cjs/auth/service.js +46 -1
  26. package/dist/cjs/auth/service.js.map +1 -1
  27. package/dist/cjs/billing/ar-repository.js +82 -6
  28. package/dist/cjs/billing/ar-repository.js.map +1 -1
  29. package/dist/cjs/billing/provider-account.js +60 -1
  30. package/dist/cjs/billing/provider-account.js.map +1 -1
  31. package/dist/cjs/billing/repository.js +51 -5
  32. package/dist/cjs/billing/repository.js.map +1 -1
  33. package/dist/cjs/clients/ibge.js +117 -0
  34. package/dist/cjs/clients/ibge.js.map +1 -0
  35. package/dist/cjs/clients/registry/http.js +157 -0
  36. package/dist/cjs/clients/registry/http.js.map +1 -0
  37. package/dist/cjs/clients/registry/ibge-municipios.js +28 -0
  38. package/dist/cjs/clients/registry/ibge-municipios.js.map +1 -0
  39. package/dist/cjs/clients/registry/ibge.js +63 -0
  40. package/dist/cjs/clients/registry/ibge.js.map +1 -0
  41. package/dist/cjs/clients/registry/lookup.js +191 -0
  42. package/dist/cjs/clients/registry/lookup.js.map +1 -0
  43. package/dist/cjs/clients/registry/provider.js +34 -0
  44. package/dist/cjs/clients/registry/provider.js.map +1 -0
  45. package/dist/cjs/clients/registry/resolve.js +47 -0
  46. package/dist/cjs/clients/registry/resolve.js.map +1 -0
  47. package/dist/cjs/clients/registry/serpro.js +149 -0
  48. package/dist/cjs/clients/registry/serpro.js.map +1 -0
  49. package/dist/cjs/clients/registry/service.js +230 -0
  50. package/dist/cjs/clients/registry/service.js.map +1 -0
  51. package/dist/cjs/clients/registry/synthetic.js +165 -0
  52. package/dist/cjs/clients/registry/synthetic.js.map +1 -0
  53. package/dist/cjs/clients/repository.js +190 -9
  54. package/dist/cjs/clients/repository.js.map +1 -1
  55. package/dist/cjs/collection/branding.js +190 -0
  56. package/dist/cjs/collection/branding.js.map +1 -1
  57. package/dist/cjs/collection/clearance-store.js +74 -0
  58. package/dist/cjs/collection/clearance-store.js.map +1 -0
  59. package/dist/cjs/collection/communication-runtime.js +65 -2
  60. package/dist/cjs/collection/communication-runtime.js.map +1 -1
  61. package/dist/cjs/collection/engine.js +20 -0
  62. package/dist/cjs/collection/engine.js.map +1 -1
  63. package/dist/cjs/collection/policy.js +29 -1
  64. package/dist/cjs/collection/policy.js.map +1 -1
  65. package/dist/cjs/communication/clearance.js +18 -0
  66. package/dist/cjs/communication/clearance.js.map +1 -0
  67. package/dist/cjs/communication/dry-run-scenario.js +5 -0
  68. package/dist/cjs/communication/dry-run-scenario.js.map +1 -1
  69. package/dist/cjs/communication/engine.js +12 -0
  70. package/dist/cjs/communication/engine.js.map +1 -1
  71. package/dist/cjs/communication/policy.js.map +1 -1
  72. package/dist/cjs/competence/business-days.js +155 -0
  73. package/dist/cjs/competence/business-days.js.map +1 -0
  74. package/dist/cjs/competence/catalog-validation.js +174 -0
  75. package/dist/cjs/competence/catalog-validation.js.map +1 -0
  76. package/dist/cjs/competence/obligation-preview.js +190 -0
  77. package/dist/cjs/competence/obligation-preview.js.map +1 -0
  78. package/dist/cjs/competence/repository.js +330 -17
  79. package/dist/cjs/competence/repository.js.map +1 -1
  80. package/dist/cjs/dashboard/pilot-panel.js +90 -0
  81. package/dist/cjs/dashboard/pilot-panel.js.map +1 -0
  82. package/dist/cjs/dashboard/repository.js +11 -1
  83. package/dist/cjs/dashboard/repository.js.map +1 -1
  84. package/dist/cjs/db/client.js +17 -11
  85. package/dist/cjs/db/client.js.map +1 -1
  86. package/dist/cjs/db/provision-e2e-identity.js +116 -0
  87. package/dist/cjs/db/provision-e2e-identity.js.map +1 -0
  88. package/dist/cjs/fiscal/applicability.js +164 -0
  89. package/dist/cjs/fiscal/applicability.js.map +1 -0
  90. package/dist/cjs/fiscal/collection/authorization.js +186 -0
  91. package/dist/cjs/fiscal/collection/authorization.js.map +1 -0
  92. package/dist/cjs/fiscal/collection/competence.js +97 -0
  93. package/dist/cjs/fiscal/collection/competence.js.map +1 -0
  94. package/dist/cjs/fiscal/collection/first-contact.js +204 -0
  95. package/dist/cjs/fiscal/collection/first-contact.js.map +1 -0
  96. package/dist/cjs/fiscal/collection/plan.js +122 -0
  97. package/dist/cjs/fiscal/collection/plan.js.map +1 -0
  98. package/dist/cjs/fiscal/conditions.js +198 -0
  99. package/dist/cjs/fiscal/conditions.js.map +1 -0
  100. package/dist/cjs/fiscal/contracts/nfse-adn-contract.js +390 -0
  101. package/dist/cjs/fiscal/contracts/nfse-adn-contract.js.map +1 -0
  102. package/dist/cjs/fiscal/contracts/nfse-adn-endpoint.js +108 -0
  103. package/dist/cjs/fiscal/contracts/nfse-adn-endpoint.js.map +1 -0
  104. package/dist/cjs/fiscal/contracts/nfse-adn-envelope.js +220 -0
  105. package/dist/cjs/fiscal/contracts/nfse-adn-envelope.js.map +1 -0
  106. package/dist/cjs/fiscal/coverage/model.js +137 -0
  107. package/dist/cjs/fiscal/coverage/model.js.map +1 -0
  108. package/dist/cjs/fiscal/coverage/requests.js +99 -0
  109. package/dist/cjs/fiscal/coverage/requests.js.map +1 -0
  110. package/dist/cjs/fiscal/credentials/inspect.js +182 -0
  111. package/dist/cjs/fiscal/credentials/inspect.js.map +1 -0
  112. package/dist/cjs/fiscal/credentials/material.js +168 -0
  113. package/dist/cjs/fiscal/credentials/material.js.map +1 -0
  114. package/dist/cjs/fiscal/credentials/onboarding.js +266 -0
  115. package/dist/cjs/fiscal/credentials/onboarding.js.map +1 -0
  116. package/dist/cjs/fiscal/credentials/readiness.js +190 -0
  117. package/dist/cjs/fiscal/credentials/readiness.js.map +1 -0
  118. package/dist/cjs/fiscal/eligibility.js +199 -0
  119. package/dist/cjs/fiscal/eligibility.js.map +1 -0
  120. package/dist/cjs/fiscal/enrollment.js +134 -0
  121. package/dist/cjs/fiscal/enrollment.js.map +1 -0
  122. package/dist/cjs/fiscal/ingestion/__fixtures__/nfse.js +128 -0
  123. package/dist/cjs/fiscal/ingestion/__fixtures__/nfse.js.map +1 -0
  124. package/dist/cjs/fiscal/ingestion/adn-transport.js +229 -0
  125. package/dist/cjs/fiscal/ingestion/adn-transport.js.map +1 -0
  126. package/dist/cjs/fiscal/ingestion/direction.js +36 -0
  127. package/dist/cjs/fiscal/ingestion/direction.js.map +1 -0
  128. package/dist/cjs/fiscal/ingestion/nfse-adn.js +228 -0
  129. package/dist/cjs/fiscal/ingestion/nfse-adn.js.map +1 -0
  130. package/dist/cjs/fiscal/ingestion/pipeline.js +704 -0
  131. package/dist/cjs/fiscal/ingestion/pipeline.js.map +1 -0
  132. package/dist/cjs/fiscal/ingestion/provider.js +51 -0
  133. package/dist/cjs/fiscal/ingestion/provider.js.map +1 -0
  134. package/dist/cjs/fiscal/ingestion/repository.js +132 -0
  135. package/dist/cjs/fiscal/ingestion/repository.js.map +1 -0
  136. package/dist/cjs/fiscal/ingestion/synthetic-provider.js +103 -0
  137. package/dist/cjs/fiscal/ingestion/synthetic-provider.js.map +1 -0
  138. package/dist/cjs/fiscal/ingestion/synthetic-transport-types.js +13 -0
  139. package/dist/cjs/fiscal/ingestion/synthetic-transport-types.js.map +1 -0
  140. package/dist/cjs/fiscal/ingestion/synthetic-transport.js +61 -0
  141. package/dist/cjs/fiscal/ingestion/synthetic-transport.js.map +1 -0
  142. package/dist/cjs/fiscal/ingestion/xml-reader.js +121 -0
  143. package/dist/cjs/fiscal/ingestion/xml-reader.js.map +1 -0
  144. package/dist/cjs/fiscal/repository.js +470 -0
  145. package/dist/cjs/fiscal/repository.js.map +1 -0
  146. package/dist/cjs/fiscal/sync/circuit.js +124 -0
  147. package/dist/cjs/fiscal/sync/circuit.js.map +1 -0
  148. package/dist/cjs/fiscal/sync/intervention.js +140 -0
  149. package/dist/cjs/fiscal/sync/intervention.js.map +1 -0
  150. package/dist/cjs/fiscal/sync/lease.js +146 -0
  151. package/dist/cjs/fiscal/sync/lease.js.map +1 -0
  152. package/dist/cjs/fiscal/sync/observability.js +244 -0
  153. package/dist/cjs/fiscal/sync/observability.js.map +1 -0
  154. package/dist/cjs/fiscal/sync/orchestrator.js +490 -0
  155. package/dist/cjs/fiscal/sync/orchestrator.js.map +1 -0
  156. package/dist/cjs/fiscal/sync/policy.js +143 -0
  157. package/dist/cjs/fiscal/sync/policy.js.map +1 -0
  158. package/dist/cjs/fiscal/sync/reprocess.js +241 -0
  159. package/dist/cjs/fiscal/sync/reprocess.js.map +1 -0
  160. package/dist/cjs/fiscal/sync/retry.js +183 -0
  161. package/dist/cjs/fiscal/sync/retry.js.map +1 -0
  162. package/dist/cjs/fiscal/sync/secret-boundary.js +123 -0
  163. package/dist/cjs/fiscal/sync/secret-boundary.js.map +1 -0
  164. package/dist/cjs/fiscal/transport/https.js +309 -0
  165. package/dist/cjs/fiscal/transport/https.js.map +1 -0
  166. package/dist/cjs/fiscal/transport/synthetic-tls.js +380 -0
  167. package/dist/cjs/fiscal/transport/synthetic-tls.js.map +1 -0
  168. package/dist/cjs/fiscal/transport/types.js +93 -0
  169. package/dist/cjs/fiscal/transport/types.js.map +1 -0
  170. package/dist/cjs/import/br-documents.js +85 -0
  171. package/dist/cjs/import/br-documents.js.map +1 -0
  172. package/dist/cjs/import/clients-import.js +347 -40
  173. package/dist/cjs/import/clients-import.js.map +1 -1
  174. package/dist/cjs/index.js +461 -73
  175. package/dist/cjs/index.js.map +1 -1
  176. package/dist/cjs/legal/documents.js +164 -19
  177. package/dist/cjs/legal/documents.js.map +1 -1
  178. package/dist/cjs/legal/index.js +3 -1
  179. package/dist/cjs/legal/index.js.map +1 -1
  180. package/dist/cjs/legal/types.js.map +1 -1
  181. package/dist/cjs/onboarding/persistence.js +4 -4
  182. package/dist/cjs/onboarding/persistence.js.map +1 -1
  183. package/dist/cjs/pilot/go-live-checklist.js +212 -0
  184. package/dist/cjs/pilot/go-live-checklist.js.map +1 -0
  185. package/dist/cjs/pilot/milestone.js +79 -0
  186. package/dist/cjs/pilot/milestone.js.map +1 -0
  187. package/dist/cjs/platform/capabilities.js +173 -0
  188. package/dist/cjs/platform/capabilities.js.map +1 -0
  189. package/dist/cjs/platform/job-contract.js +411 -0
  190. package/dist/cjs/platform/job-contract.js.map +1 -0
  191. package/dist/cjs/rbac/catalog.js +110 -0
  192. package/dist/cjs/rbac/catalog.js.map +1 -1
  193. package/dist/cjs/subscription-billing/index.js +9 -1
  194. package/dist/cjs/subscription-billing/index.js.map +1 -1
  195. package/dist/cjs/subscription-billing/platform-exemption.js +121 -0
  196. package/dist/cjs/subscription-billing/platform-exemption.js.map +1 -0
  197. package/dist/cjs/subscription-billing/service.js +246 -78
  198. package/dist/cjs/subscription-billing/service.js.map +1 -1
  199. package/dist/cjs/tenancy/features.js +131 -0
  200. package/dist/cjs/tenancy/features.js.map +1 -0
  201. package/dist/cjs/tenancy/settings.js +224 -0
  202. package/dist/cjs/tenancy/settings.js.map +1 -0
  203. package/dist/cjs/users/repository.js +24 -1
  204. package/dist/cjs/users/repository.js.map +1 -1
  205. package/dist/cjs/vault/repository.js +92 -5
  206. package/dist/cjs/vault/repository.js.map +1 -1
  207. package/dist/clients/ibge.d.ts +17 -0
  208. package/dist/clients/ibge.d.ts.map +1 -0
  209. package/dist/clients/ibge.js +112 -0
  210. package/dist/clients/ibge.js.map +1 -0
  211. package/dist/clients/registry/http.d.ts +61 -0
  212. package/dist/clients/registry/http.d.ts.map +1 -0
  213. package/dist/clients/registry/http.js +151 -0
  214. package/dist/clients/registry/http.js.map +1 -0
  215. package/dist/clients/registry/ibge-municipios.d.ts +25 -0
  216. package/dist/clients/registry/ibge-municipios.d.ts.map +1 -0
  217. package/dist/clients/registry/ibge-municipios.js +25 -0
  218. package/dist/clients/registry/ibge-municipios.js.map +1 -0
  219. package/dist/clients/registry/ibge.d.ts +62 -0
  220. package/dist/clients/registry/ibge.d.ts.map +1 -0
  221. package/dist/clients/registry/ibge.js +57 -0
  222. package/dist/clients/registry/ibge.js.map +1 -0
  223. package/dist/clients/registry/lookup.d.ts +94 -0
  224. package/dist/clients/registry/lookup.d.ts.map +1 -0
  225. package/dist/clients/registry/lookup.js +184 -0
  226. package/dist/clients/registry/lookup.js.map +1 -0
  227. package/dist/clients/registry/provider.d.ts +124 -0
  228. package/dist/clients/registry/provider.d.ts.map +1 -0
  229. package/dist/clients/registry/provider.js +39 -0
  230. package/dist/clients/registry/provider.js.map +1 -0
  231. package/dist/clients/registry/resolve.d.ts +36 -0
  232. package/dist/clients/registry/resolve.d.ts.map +1 -0
  233. package/dist/clients/registry/resolve.js +44 -0
  234. package/dist/clients/registry/resolve.js.map +1 -0
  235. package/dist/clients/registry/serpro.d.ts +57 -0
  236. package/dist/clients/registry/serpro.d.ts.map +1 -0
  237. package/dist/clients/registry/serpro.js +143 -0
  238. package/dist/clients/registry/serpro.js.map +1 -0
  239. package/dist/clients/registry/service.d.ts +59 -0
  240. package/dist/clients/registry/service.d.ts.map +1 -0
  241. package/dist/clients/registry/service.js +225 -0
  242. package/dist/clients/registry/service.js.map +1 -0
  243. package/dist/clients/registry/synthetic.d.ts +30 -0
  244. package/dist/clients/registry/synthetic.d.ts.map +1 -0
  245. package/dist/clients/registry/synthetic.js +161 -0
  246. package/dist/clients/registry/synthetic.js.map +1 -0
  247. package/dist/clients/repository.d.ts +158 -0
  248. package/dist/clients/repository.d.ts.map +1 -1
  249. package/dist/clients/repository.js +186 -9
  250. package/dist/clients/repository.js.map +1 -1
  251. package/dist/collection/branding.d.ts +65 -0
  252. package/dist/collection/branding.d.ts.map +1 -1
  253. package/dist/collection/branding.js +186 -0
  254. package/dist/collection/branding.js.map +1 -1
  255. package/dist/collection/clearance-store.d.ts +39 -0
  256. package/dist/collection/clearance-store.d.ts.map +1 -0
  257. package/dist/collection/clearance-store.js +69 -0
  258. package/dist/collection/clearance-store.js.map +1 -0
  259. package/dist/collection/communication-runtime.d.ts +37 -0
  260. package/dist/collection/communication-runtime.d.ts.map +1 -1
  261. package/dist/collection/communication-runtime.js +63 -1
  262. package/dist/collection/communication-runtime.js.map +1 -1
  263. package/dist/collection/engine.d.ts +23 -0
  264. package/dist/collection/engine.d.ts.map +1 -1
  265. package/dist/collection/engine.js +20 -0
  266. package/dist/collection/engine.js.map +1 -1
  267. package/dist/collection/policy.d.ts +25 -0
  268. package/dist/collection/policy.d.ts.map +1 -1
  269. package/dist/collection/policy.js +27 -0
  270. package/dist/collection/policy.js.map +1 -1
  271. package/dist/communication/clearance.d.ts +53 -0
  272. package/dist/communication/clearance.d.ts.map +1 -0
  273. package/dist/communication/clearance.js +15 -0
  274. package/dist/communication/clearance.js.map +1 -0
  275. package/dist/communication/dry-run-scenario.d.ts.map +1 -1
  276. package/dist/communication/dry-run-scenario.js +5 -0
  277. package/dist/communication/dry-run-scenario.js.map +1 -1
  278. package/dist/communication/engine.d.ts +10 -0
  279. package/dist/communication/engine.d.ts.map +1 -1
  280. package/dist/communication/engine.js +12 -0
  281. package/dist/communication/engine.js.map +1 -1
  282. package/dist/communication/policy.d.ts +16 -0
  283. package/dist/communication/policy.d.ts.map +1 -1
  284. package/dist/communication/policy.js.map +1 -1
  285. package/dist/competence/business-days.d.ts +51 -0
  286. package/dist/competence/business-days.d.ts.map +1 -0
  287. package/dist/competence/business-days.js +145 -0
  288. package/dist/competence/business-days.js.map +1 -0
  289. package/dist/competence/catalog-validation.d.ts +86 -0
  290. package/dist/competence/catalog-validation.d.ts.map +1 -0
  291. package/dist/competence/catalog-validation.js +168 -0
  292. package/dist/competence/catalog-validation.js.map +1 -0
  293. package/dist/competence/obligation-preview.d.ts +103 -0
  294. package/dist/competence/obligation-preview.d.ts.map +1 -0
  295. package/dist/competence/obligation-preview.js +184 -0
  296. package/dist/competence/obligation-preview.js.map +1 -0
  297. package/dist/competence/repository.d.ts +139 -1
  298. package/dist/competence/repository.d.ts.map +1 -1
  299. package/dist/competence/repository.js +325 -17
  300. package/dist/competence/repository.js.map +1 -1
  301. package/dist/dashboard/pilot-panel.d.ts +100 -0
  302. package/dist/dashboard/pilot-panel.d.ts.map +1 -0
  303. package/dist/dashboard/pilot-panel.js +87 -0
  304. package/dist/dashboard/pilot-panel.js.map +1 -0
  305. package/dist/dashboard/repository.d.ts.map +1 -1
  306. package/dist/dashboard/repository.js +11 -1
  307. package/dist/dashboard/repository.js.map +1 -1
  308. package/dist/db/client.d.ts +45 -0
  309. package/dist/db/client.d.ts.map +1 -1
  310. package/dist/db/client.js +16 -11
  311. package/dist/db/client.js.map +1 -1
  312. package/dist/db/migrate.d.ts +36 -1
  313. package/dist/db/migrate.d.ts.map +1 -1
  314. package/dist/db/migrate.js +199 -32
  315. package/dist/db/migrate.js.map +1 -1
  316. package/dist/db/provision-e2e-identity.d.ts +2 -0
  317. package/dist/db/provision-e2e-identity.d.ts.map +1 -0
  318. package/dist/db/provision-e2e-identity.js +114 -0
  319. package/dist/db/provision-e2e-identity.js.map +1 -0
  320. package/dist/fiscal/applicability.d.ts +73 -0
  321. package/dist/fiscal/applicability.d.ts.map +1 -0
  322. package/dist/fiscal/applicability.js +160 -0
  323. package/dist/fiscal/applicability.js.map +1 -0
  324. package/dist/fiscal/collection/authorization.d.ts +152 -0
  325. package/dist/fiscal/collection/authorization.d.ts.map +1 -0
  326. package/dist/fiscal/collection/authorization.js +178 -0
  327. package/dist/fiscal/collection/authorization.js.map +1 -0
  328. package/dist/fiscal/collection/competence.d.ts +66 -0
  329. package/dist/fiscal/collection/competence.d.ts.map +1 -0
  330. package/dist/fiscal/collection/competence.js +90 -0
  331. package/dist/fiscal/collection/competence.js.map +1 -0
  332. package/dist/fiscal/collection/first-contact.d.ts +103 -0
  333. package/dist/fiscal/collection/first-contact.d.ts.map +1 -0
  334. package/dist/fiscal/collection/first-contact.js +201 -0
  335. package/dist/fiscal/collection/first-contact.js.map +1 -0
  336. package/dist/fiscal/collection/plan.d.ts +74 -0
  337. package/dist/fiscal/collection/plan.d.ts.map +1 -0
  338. package/dist/fiscal/collection/plan.js +119 -0
  339. package/dist/fiscal/collection/plan.js.map +1 -0
  340. package/dist/fiscal/conditions.d.ts +99 -0
  341. package/dist/fiscal/conditions.d.ts.map +1 -0
  342. package/dist/fiscal/conditions.js +190 -0
  343. package/dist/fiscal/conditions.js.map +1 -0
  344. package/dist/fiscal/contracts/nfse-adn-contract.d.ts +315 -0
  345. package/dist/fiscal/contracts/nfse-adn-contract.d.ts.map +1 -0
  346. package/dist/fiscal/contracts/nfse-adn-contract.js +386 -0
  347. package/dist/fiscal/contracts/nfse-adn-contract.js.map +1 -0
  348. package/dist/fiscal/contracts/nfse-adn-endpoint.d.ts +13 -0
  349. package/dist/fiscal/contracts/nfse-adn-endpoint.d.ts.map +1 -0
  350. package/dist/fiscal/contracts/nfse-adn-endpoint.js +103 -0
  351. package/dist/fiscal/contracts/nfse-adn-endpoint.js.map +1 -0
  352. package/dist/fiscal/contracts/nfse-adn-envelope.d.ts +77 -0
  353. package/dist/fiscal/contracts/nfse-adn-envelope.d.ts.map +1 -0
  354. package/dist/fiscal/contracts/nfse-adn-envelope.js +215 -0
  355. package/dist/fiscal/contracts/nfse-adn-envelope.js.map +1 -0
  356. package/dist/fiscal/coverage/model.d.ts +114 -0
  357. package/dist/fiscal/coverage/model.d.ts.map +1 -0
  358. package/dist/fiscal/coverage/model.js +130 -0
  359. package/dist/fiscal/coverage/model.js.map +1 -0
  360. package/dist/fiscal/coverage/requests.d.ts +70 -0
  361. package/dist/fiscal/coverage/requests.d.ts.map +1 -0
  362. package/dist/fiscal/coverage/requests.js +95 -0
  363. package/dist/fiscal/coverage/requests.js.map +1 -0
  364. package/dist/fiscal/credentials/inspect.d.ts +114 -0
  365. package/dist/fiscal/credentials/inspect.d.ts.map +1 -0
  366. package/dist/fiscal/credentials/inspect.js +178 -0
  367. package/dist/fiscal/credentials/inspect.js.map +1 -0
  368. package/dist/fiscal/credentials/material.d.ts +81 -0
  369. package/dist/fiscal/credentials/material.d.ts.map +1 -0
  370. package/dist/fiscal/credentials/material.js +164 -0
  371. package/dist/fiscal/credentials/material.js.map +1 -0
  372. package/dist/fiscal/credentials/onboarding.d.ts +119 -0
  373. package/dist/fiscal/credentials/onboarding.d.ts.map +1 -0
  374. package/dist/fiscal/credentials/onboarding.js +259 -0
  375. package/dist/fiscal/credentials/onboarding.js.map +1 -0
  376. package/dist/fiscal/credentials/readiness.d.ts +119 -0
  377. package/dist/fiscal/credentials/readiness.d.ts.map +1 -0
  378. package/dist/fiscal/credentials/readiness.js +186 -0
  379. package/dist/fiscal/credentials/readiness.js.map +1 -0
  380. package/dist/fiscal/eligibility.d.ts +106 -0
  381. package/dist/fiscal/eligibility.d.ts.map +1 -0
  382. package/dist/fiscal/eligibility.js +194 -0
  383. package/dist/fiscal/eligibility.js.map +1 -0
  384. package/dist/fiscal/enrollment.d.ts +91 -0
  385. package/dist/fiscal/enrollment.d.ts.map +1 -0
  386. package/dist/fiscal/enrollment.js +126 -0
  387. package/dist/fiscal/enrollment.js.map +1 -0
  388. package/dist/fiscal/ingestion/__fixtures__/nfse.d.ts +57 -0
  389. package/dist/fiscal/ingestion/__fixtures__/nfse.d.ts.map +1 -0
  390. package/dist/fiscal/ingestion/__fixtures__/nfse.js +121 -0
  391. package/dist/fiscal/ingestion/__fixtures__/nfse.js.map +1 -0
  392. package/dist/fiscal/ingestion/adn-transport.d.ts +123 -0
  393. package/dist/fiscal/ingestion/adn-transport.d.ts.map +1 -0
  394. package/dist/fiscal/ingestion/adn-transport.js +220 -0
  395. package/dist/fiscal/ingestion/adn-transport.js.map +1 -0
  396. package/dist/fiscal/ingestion/direction.d.ts +60 -0
  397. package/dist/fiscal/ingestion/direction.d.ts.map +1 -0
  398. package/dist/fiscal/ingestion/direction.js +32 -0
  399. package/dist/fiscal/ingestion/direction.js.map +1 -0
  400. package/dist/fiscal/ingestion/nfse-adn.d.ts +91 -0
  401. package/dist/fiscal/ingestion/nfse-adn.d.ts.map +1 -0
  402. package/dist/fiscal/ingestion/nfse-adn.js +222 -0
  403. package/dist/fiscal/ingestion/nfse-adn.js.map +1 -0
  404. package/dist/fiscal/ingestion/pipeline.d.ts +168 -0
  405. package/dist/fiscal/ingestion/pipeline.d.ts.map +1 -0
  406. package/dist/fiscal/ingestion/pipeline.js +699 -0
  407. package/dist/fiscal/ingestion/pipeline.js.map +1 -0
  408. package/dist/fiscal/ingestion/provider.d.ts +206 -0
  409. package/dist/fiscal/ingestion/provider.d.ts.map +1 -0
  410. package/dist/fiscal/ingestion/provider.js +47 -0
  411. package/dist/fiscal/ingestion/provider.js.map +1 -0
  412. package/dist/fiscal/ingestion/repository.d.ts +82 -0
  413. package/dist/fiscal/ingestion/repository.d.ts.map +1 -0
  414. package/dist/fiscal/ingestion/repository.js +127 -0
  415. package/dist/fiscal/ingestion/repository.js.map +1 -0
  416. package/dist/fiscal/ingestion/synthetic-provider.d.ts +92 -0
  417. package/dist/fiscal/ingestion/synthetic-provider.d.ts.map +1 -0
  418. package/dist/fiscal/ingestion/synthetic-provider.js +99 -0
  419. package/dist/fiscal/ingestion/synthetic-provider.js.map +1 -0
  420. package/dist/fiscal/ingestion/synthetic-transport-types.d.ts +19 -0
  421. package/dist/fiscal/ingestion/synthetic-transport-types.d.ts.map +1 -0
  422. package/dist/fiscal/ingestion/synthetic-transport-types.js +9 -0
  423. package/dist/fiscal/ingestion/synthetic-transport-types.js.map +1 -0
  424. package/dist/fiscal/ingestion/synthetic-transport.d.ts +55 -0
  425. package/dist/fiscal/ingestion/synthetic-transport.d.ts.map +1 -0
  426. package/dist/fiscal/ingestion/synthetic-transport.js +57 -0
  427. package/dist/fiscal/ingestion/synthetic-transport.js.map +1 -0
  428. package/dist/fiscal/ingestion/xml-reader.d.ts +55 -0
  429. package/dist/fiscal/ingestion/xml-reader.d.ts.map +1 -0
  430. package/dist/fiscal/ingestion/xml-reader.js +112 -0
  431. package/dist/fiscal/ingestion/xml-reader.js.map +1 -0
  432. package/dist/fiscal/repository.d.ts +220 -0
  433. package/dist/fiscal/repository.d.ts.map +1 -0
  434. package/dist/fiscal/repository.js +449 -0
  435. package/dist/fiscal/repository.js.map +1 -0
  436. package/dist/fiscal/sync/circuit.d.ts +86 -0
  437. package/dist/fiscal/sync/circuit.d.ts.map +1 -0
  438. package/dist/fiscal/sync/circuit.js +118 -0
  439. package/dist/fiscal/sync/circuit.js.map +1 -0
  440. package/dist/fiscal/sync/intervention.d.ts +81 -0
  441. package/dist/fiscal/sync/intervention.d.ts.map +1 -0
  442. package/dist/fiscal/sync/intervention.js +135 -0
  443. package/dist/fiscal/sync/intervention.js.map +1 -0
  444. package/dist/fiscal/sync/lease.d.ts +118 -0
  445. package/dist/fiscal/sync/lease.d.ts.map +1 -0
  446. package/dist/fiscal/sync/lease.js +139 -0
  447. package/dist/fiscal/sync/lease.js.map +1 -0
  448. package/dist/fiscal/sync/observability.d.ts +148 -0
  449. package/dist/fiscal/sync/observability.d.ts.map +1 -0
  450. package/dist/fiscal/sync/observability.js +239 -0
  451. package/dist/fiscal/sync/observability.js.map +1 -0
  452. package/dist/fiscal/sync/orchestrator.d.ts +132 -0
  453. package/dist/fiscal/sync/orchestrator.d.ts.map +1 -0
  454. package/dist/fiscal/sync/orchestrator.js +487 -0
  455. package/dist/fiscal/sync/orchestrator.js.map +1 -0
  456. package/dist/fiscal/sync/policy.d.ts +87 -0
  457. package/dist/fiscal/sync/policy.d.ts.map +1 -0
  458. package/dist/fiscal/sync/policy.js +136 -0
  459. package/dist/fiscal/sync/policy.js.map +1 -0
  460. package/dist/fiscal/sync/reprocess.d.ts +76 -0
  461. package/dist/fiscal/sync/reprocess.d.ts.map +1 -0
  462. package/dist/fiscal/sync/reprocess.js +237 -0
  463. package/dist/fiscal/sync/reprocess.js.map +1 -0
  464. package/dist/fiscal/sync/retry.d.ts +82 -0
  465. package/dist/fiscal/sync/retry.d.ts.map +1 -0
  466. package/dist/fiscal/sync/retry.js +176 -0
  467. package/dist/fiscal/sync/retry.js.map +1 -0
  468. package/dist/fiscal/sync/secret-boundary.d.ts +169 -0
  469. package/dist/fiscal/sync/secret-boundary.d.ts.map +1 -0
  470. package/dist/fiscal/sync/secret-boundary.js +116 -0
  471. package/dist/fiscal/sync/secret-boundary.js.map +1 -0
  472. package/dist/fiscal/transport/https.d.ts +86 -0
  473. package/dist/fiscal/transport/https.d.ts.map +1 -0
  474. package/dist/fiscal/transport/https.js +303 -0
  475. package/dist/fiscal/transport/https.js.map +1 -0
  476. package/dist/fiscal/transport/synthetic-tls.d.ts +99 -0
  477. package/dist/fiscal/transport/synthetic-tls.d.ts.map +1 -0
  478. package/dist/fiscal/transport/synthetic-tls.js +374 -0
  479. package/dist/fiscal/transport/synthetic-tls.js.map +1 -0
  480. package/dist/fiscal/transport/types.d.ts +134 -0
  481. package/dist/fiscal/transport/types.d.ts.map +1 -0
  482. package/dist/fiscal/transport/types.js +88 -0
  483. package/dist/fiscal/transport/types.js.map +1 -0
  484. package/dist/import/br-documents.d.ts +26 -0
  485. package/dist/import/br-documents.d.ts.map +1 -0
  486. package/dist/import/br-documents.js +78 -0
  487. package/dist/import/br-documents.js.map +1 -0
  488. package/dist/import/clients-import.d.ts +122 -4
  489. package/dist/import/clients-import.d.ts.map +1 -1
  490. package/dist/import/clients-import.js +341 -41
  491. package/dist/import/clients-import.js.map +1 -1
  492. package/dist/index.d.ts +145 -13
  493. package/dist/index.d.ts.map +1 -1
  494. package/dist/index.js +120 -7
  495. package/dist/index.js.map +1 -1
  496. package/dist/legal/documents.d.ts +12 -1
  497. package/dist/legal/documents.d.ts.map +1 -1
  498. package/dist/legal/documents.js +162 -18
  499. package/dist/legal/documents.js.map +1 -1
  500. package/dist/legal/index.d.ts +2 -2
  501. package/dist/legal/index.d.ts.map +1 -1
  502. package/dist/legal/index.js +1 -1
  503. package/dist/legal/index.js.map +1 -1
  504. package/dist/legal/types.d.ts +114 -2
  505. package/dist/legal/types.d.ts.map +1 -1
  506. package/dist/legal/types.js.map +1 -1
  507. package/dist/onboarding/persistence.d.ts +10 -1
  508. package/dist/onboarding/persistence.d.ts.map +1 -1
  509. package/dist/onboarding/persistence.js +4 -4
  510. package/dist/onboarding/persistence.js.map +1 -1
  511. package/dist/pilot/go-live-checklist.d.ts +41 -0
  512. package/dist/pilot/go-live-checklist.d.ts.map +1 -0
  513. package/dist/pilot/go-live-checklist.js +209 -0
  514. package/dist/pilot/go-live-checklist.js.map +1 -0
  515. package/dist/pilot/milestone.d.ts +56 -0
  516. package/dist/pilot/milestone.d.ts.map +1 -0
  517. package/dist/pilot/milestone.js +74 -0
  518. package/dist/pilot/milestone.js.map +1 -0
  519. package/dist/platform/capabilities.d.ts +88 -0
  520. package/dist/platform/capabilities.d.ts.map +1 -0
  521. package/dist/platform/capabilities.js +169 -0
  522. package/dist/platform/capabilities.js.map +1 -0
  523. package/dist/platform/job-contract.d.ts +232 -0
  524. package/dist/platform/job-contract.d.ts.map +1 -0
  525. package/dist/platform/job-contract.js +403 -0
  526. package/dist/platform/job-contract.js.map +1 -0
  527. package/dist/rbac/catalog.d.ts.map +1 -1
  528. package/dist/rbac/catalog.js +110 -0
  529. package/dist/rbac/catalog.js.map +1 -1
  530. package/dist/subscription-billing/index.d.ts +8 -5
  531. package/dist/subscription-billing/index.d.ts.map +1 -1
  532. package/dist/subscription-billing/index.js +4 -3
  533. package/dist/subscription-billing/index.js.map +1 -1
  534. package/dist/subscription-billing/platform-exemption.d.ts +89 -0
  535. package/dist/subscription-billing/platform-exemption.d.ts.map +1 -0
  536. package/dist/subscription-billing/platform-exemption.js +113 -0
  537. package/dist/subscription-billing/platform-exemption.js.map +1 -0
  538. package/dist/subscription-billing/service.d.ts +74 -0
  539. package/dist/subscription-billing/service.d.ts.map +1 -1
  540. package/dist/subscription-billing/service.js +182 -16
  541. package/dist/subscription-billing/service.js.map +1 -1
  542. package/dist/tenancy/features.d.ts +69 -0
  543. package/dist/tenancy/features.d.ts.map +1 -0
  544. package/dist/tenancy/features.js +123 -0
  545. package/dist/tenancy/features.js.map +1 -0
  546. package/dist/tenancy/settings.d.ts +63 -0
  547. package/dist/tenancy/settings.d.ts.map +1 -0
  548. package/dist/tenancy/settings.js +218 -0
  549. package/dist/tenancy/settings.js.map +1 -0
  550. package/dist/users/repository.d.ts +16 -1
  551. package/dist/users/repository.d.ts.map +1 -1
  552. package/dist/users/repository.js +24 -1
  553. package/dist/users/repository.js.map +1 -1
  554. package/dist/vault/repository.d.ts +69 -1
  555. package/dist/vault/repository.d.ts.map +1 -1
  556. package/dist/vault/repository.js +88 -5
  557. package/dist/vault/repository.js.map +1 -1
  558. package/package.json +10 -2
  559. package/src/db/migrations/0045_role_write_scope.sql +122 -0
  560. package/src/db/migrations/0046_pilot_activation_model.sql +184 -0
  561. package/src/db/migrations/0047_recipient_clearance.sql +89 -0
  562. package/src/db/migrations/0048_item_type_note.sql +16 -0
  563. package/src/db/migrations/0049_national_fiscal_engine.sql +279 -0
  564. package/src/db/migrations/0050_fiscal_vocabulary_readonly.sql +29 -0
  565. package/src/db/migrations/0051_seed_rules_from_catalog.sql +72 -0
  566. package/src/db/migrations/0052_fiscal_rule_delete_scope.sql +57 -0
  567. package/src/db/migrations/0053_rule_validity_lower_bound.sql +44 -0
  568. package/src/db/migrations/0054_tenant_profile_and_rule_source.sql +89 -0
  569. package/src/db/migrations/0055_job_capability_contract.sql +300 -0
  570. package/src/db/migrations/0056_fiscal_document_ingestion.sql +337 -0
  571. package/src/db/migrations/0057_fiscal_pending_event_inbox.sql +228 -0
  572. package/src/db/migrations/0058_fiscal_operational_engine.sql +516 -0
  573. package/src/db/migrations/0059_fiscal_contract_version.sql +59 -0
  574. package/src/db/migrations/0060_fiscal_credential_vault_and_run_authorization.sql +321 -0
  575. package/src/db/migrations/0061_client_official_registry_provenance.sql +193 -0
  576. package/src/db/migrations/0062_first_contact_and_capability_truth.sql +119 -0
@@ -0,0 +1,337 @@
1
+ -- 0056 — o documento fiscal como registro canônico, e o cursor que o busca.
2
+ --
3
+ -- ═══ O problema operacional, na frase de quem o tem ════════════════════════
4
+ --
5
+ -- A proprietária da SC precisa chegar mais cedo ou sair mais tarde para "fazer
6
+ -- baixa de nota fiscal de cliente". Essa expressão não descreve UMA rotina: ela
7
+ -- cobre buscar NFS-e emitidas no portal do município, buscar NF-e destinadas ao
8
+ -- cliente no ambiente nacional, salvar XML, conferir contra o que o cliente
9
+ -- mandou, e classificar por competência. Fluxos diferentes, sistemas
10
+ -- diferentes, e só o último é trabalho de contador.
11
+ --
12
+ -- Esta migration cria onde o resultado dessa busca cabe — e nada além disso.
13
+ -- Capturar documento NÃO é escriturar: apuração, obrigação e fechamento
14
+ -- continuam em outros domínios, e é deliberado que esta tabela não os toque.
15
+ --
16
+ -- ═══ Por que um modelo só para NF-e, NFS-e e CT-e ══════════════════════════
17
+ --
18
+ -- A pesquisa oficial desta onda mediu que os três compartilham a MESMA forma de
19
+ -- distribuição:
20
+ --
21
+ -- NF-e NT 2014.002 v1.02c (ago/2020) — WS `NFeDistribuicaoDFe`,
22
+ -- `distNSU`/`consNSU`/`consChNFe`, lote de até 50, `ultNSU`/`maxNSU`,
23
+ -- `docZip` gZip+base64.
24
+ -- CT-e NT 2015.002 — WS `CTeDistribuicaoDFe`, `distNSU`/`consNSU`,
25
+ -- `ultNSU`/`maxNSU`, `docZip`. Mesma semântica, outro documento.
26
+ -- NFS-e Manual dos Contribuintes do ADN v1.0 (12/02/2026) —
27
+ -- `GET /DFe/{NSU}` e `GET /NFSe/{ChaveAcesso}/Eventos`. REST em vez de
28
+ -- SOAP, e o MESMO conceito de NSU.
29
+ --
30
+ -- Um cursor NSU sobre um ator identificado por documento serve aos três. Modelar
31
+ -- só NFS-e agora criaria retrabalho garantido, e modelar cada um em tabela
32
+ -- própria triplicaria dedupe, cursor e trilha. Um registro canônico com
33
+ -- `document_type`, e o específico no payload normalizado.
34
+
35
+ -- ── 1 · O documento fiscal ─────────────────────────────────────────────────
36
+ --
37
+ -- RAW e NORMALIZED coexistem, e nenhum dos dois é derivável do outro em
38
+ -- produção: o XML original é o que tem valor probatório e o que a Receita
39
+ -- reconhece; o normalizado é o que a tela lê sem reparsear XML a cada render.
40
+ -- Normalizar NUNCA destrói o original — `raw_blob_id` aponta para `blob`, que
41
+ -- já carrega sha256, tamanho e chave de storage particionada por
42
+ -- {tenant}/{client}/{yyyy}/{mm}.
43
+ --
44
+ -- `normalized` é jsonb e não colunas por campo. Isso não é preguiça de
45
+ -- modelagem: a reforma tributária acrescenta IBS e CBS aos leiautes, e um
46
+ -- schema rígido demais hoje descartaria em silêncio campos que ainda não
47
+ -- existem. O que é consultado vira coluna; o resto sobrevive no jsonb e no XML.
48
+ CREATE TABLE IF NOT EXISTS fiscal_document (
49
+ id uuid PRIMARY KEY,
50
+ tenant_id uuid NOT NULL REFERENCES tenant(id),
51
+ client_id uuid NOT NULL,
52
+
53
+ document_type text NOT NULL CHECK (document_type IN ('nfse', 'nfe', 'cte', 'nfce')),
54
+ -- `received` é o caso que a NT 2014.002 privilegia: "Documentos emitidos pela
55
+ -- própria empresa NÃO estarão disponíveis para consulta" no DF-e da NF-e. Já
56
+ -- o ADN da NFS-e devolve documentos em que o contribuinte é emitente,
57
+ -- tomador OU intermediário. A coluna existe porque a resposta é diferente por
58
+ -- documento, e tratar tudo como "nota do cliente" perderia a distinção que
59
+ -- decide o que se faz com ela.
60
+ direction text NOT NULL CHECK (direction IN ('issued', 'received', 'intermediary')),
61
+
62
+ source text NOT NULL,
63
+ -- Sem fallback entre ambientes, nos dois sentidos (Gate B0 §4). Documento de
64
+ -- homologação nunca vira documento real por omissão de coluna.
65
+ source_environment text NOT NULL
66
+ CHECK (source_environment IN ('synthetic', 'homologation', 'production')),
67
+ -- Posição do cursor que entregou este documento. Texto e não bigint: o NSU da
68
+ -- NT 2014.002 tem até 15 dígitos e o do ADN não promete ser numérico.
69
+ source_nsu text,
70
+
71
+ access_key text,
72
+ document_number text,
73
+ series text,
74
+ issuer_document text,
75
+ issuer_name text,
76
+ recipient_document text,
77
+ recipient_name text,
78
+ issued_at timestamptz,
79
+ -- Competência CONTÁBIL, separada da emissão de propósito: nota emitida em
80
+ -- 02/10 pode pertencer à competência 2026-09, e assumir que as duas são a
81
+ -- mesma coisa é como o fechamento mensal sai errado.
82
+ competence_label text CHECK (competence_label IS NULL OR competence_label ~ '^\d{4}-\d{2}$'),
83
+ gross_amount numeric(14,2),
84
+ currency text NOT NULL DEFAULT 'BRL',
85
+
86
+ -- Cancelada não é apagada. A nota existiu, foi contabilizada por alguém, e
87
+ -- some do painel só se alguém decidir escondê-la — nunca por efeito colateral
88
+ -- de um evento.
89
+ status text NOT NULL DEFAULT 'active'
90
+ CHECK (status IN ('active', 'cancelled', 'replaced', 'denied')),
91
+ -- `summary` é o `resNFe`: o que o ambiente nacional entrega ao destinatário
92
+ -- ANTES da manifestação. `full` é o `procNFe`. A distinção é do leiaute, não
93
+ -- nossa, e o produto precisa dizer qual tem — um resumo apresentado como
94
+ -- documento completo é uma mentira sobre o que o escritório possui.
95
+ content_kind text NOT NULL DEFAULT 'summary' CHECK (content_kind IN ('summary', 'full')),
96
+
97
+ raw_blob_id uuid,
98
+ raw_sha256 text CHECK (raw_sha256 IS NULL OR raw_sha256 ~ '^[a-f0-9]{64}$'),
99
+ normalized jsonb NOT NULL DEFAULT '{}'::jsonb,
100
+
101
+ -- Identidade de deduplicação. Calculada pelo adapter conforme o tipo: chave de
102
+ -- acesso quando existe, senão o par (fonte, identificador do provedor), senão
103
+ -- o hash do conteúdo. Nunca o NSU sozinho — o mesmo documento pode reaparecer
104
+ -- sob outro NSU num replay, e é exatamente esse o caso que a dedupe existe
105
+ -- para absorver.
106
+ dedupe_key text NOT NULL,
107
+
108
+ ingested_at timestamptz NOT NULL DEFAULT now(),
109
+ updated_at timestamptz NOT NULL DEFAULT now(),
110
+
111
+ CONSTRAINT fiscal_document_tenant_id_uq UNIQUE (tenant_id, id),
112
+ FOREIGN KEY (tenant_id, client_id) REFERENCES client (tenant_id, id),
113
+ FOREIGN KEY (tenant_id, raw_blob_id) REFERENCES blob (tenant_id, id),
114
+ -- Um documento marcado `full` sem original guardado é uma afirmação sem
115
+ -- lastro. `summary` pode não ter blob: o resumo cabe no normalizado.
116
+ CONSTRAINT fiscal_document_full_has_raw
117
+ CHECK (content_kind <> 'full' OR raw_blob_id IS NOT NULL)
118
+ );
119
+
120
+ -- A dedupe é por (tenant, cliente, chave). Cliente entra porque a MESMA nota
121
+ -- pode ser destinada a um cliente e emitida por outro dentro do mesmo
122
+ -- escritório — são dois fatos contábeis distintos sobre o mesmo XML.
123
+ CREATE UNIQUE INDEX IF NOT EXISTS fiscal_document_dedupe_uq
124
+ ON fiscal_document (tenant_id, client_id, dedupe_key);
125
+
126
+ CREATE INDEX IF NOT EXISTS fiscal_document_client_idx
127
+ ON fiscal_document (tenant_id, client_id, issued_at DESC);
128
+ CREATE INDEX IF NOT EXISTS fiscal_document_competence_idx
129
+ ON fiscal_document (tenant_id, competence_label, document_type);
130
+
131
+ ALTER TABLE fiscal_document ENABLE ROW LEVEL SECURITY;
132
+ ALTER TABLE fiscal_document FORCE ROW LEVEL SECURITY;
133
+ -- Duas fronteiras, não uma. O ramo aberto de `app.current_client` é o mesmo
134
+ -- padrão das demais tabelas client-scoped: staff vê o escritório inteiro, o
135
+ -- portal vê só o próprio cliente.
136
+ CREATE POLICY fiscal_document_tenant_isolation ON fiscal_document
137
+ USING (
138
+ tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid
139
+ AND (
140
+ NULLIF(current_setting('app.current_client', true), '') IS NULL
141
+ OR client_id = NULLIF(current_setting('app.current_client', true), '')::uuid
142
+ )
143
+ )
144
+ WITH CHECK (
145
+ tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid
146
+ AND (
147
+ NULLIF(current_setting('app.current_client', true), '') IS NULL
148
+ OR client_id = NULLIF(current_setting('app.current_client', true), '')::uuid
149
+ )
150
+ );
151
+
152
+ COMMENT ON COLUMN fiscal_document.competence_label IS
153
+ 'Competência CONTÁBIL (YYYY-MM), separada de issued_at: emissão e competência divergem, e assumir que são a mesma coisa erra o fechamento.';
154
+ COMMENT ON COLUMN fiscal_document.content_kind IS
155
+ 'summary = resNFe (o que o ambiente nacional entrega antes da manifestação); full = procNFe. Apresentar resumo como documento completo é mentir sobre o que o escritório possui.';
156
+
157
+ -- ── 2 · Eventos do documento ───────────────────────────────────────────────
158
+ --
159
+ -- Cancelamento, substituição e correção são EVENTOS, e o documento continua
160
+ -- existindo. A NT 2014.002 é explícita: o cancelamento é entregue mesmo sem
161
+ -- manifestação do destinatário, justamente porque ele muda o que a nota
162
+ -- significa sem apagá-la.
163
+ CREATE TABLE IF NOT EXISTS fiscal_document_event (
164
+ id uuid PRIMARY KEY,
165
+ tenant_id uuid NOT NULL REFERENCES tenant(id),
166
+ fiscal_document_id uuid NOT NULL,
167
+ event_type text NOT NULL
168
+ CHECK (event_type IN ('cancellation', 'replacement', 'correction',
169
+ 'manifestation', 'other')),
170
+ event_code text,
171
+ occurred_at timestamptz,
172
+ source_nsu text,
173
+ raw_blob_id uuid,
174
+ payload jsonb NOT NULL DEFAULT '{}'::jsonb,
175
+ dedupe_key text NOT NULL,
176
+ ingested_at timestamptz NOT NULL DEFAULT now(),
177
+ CONSTRAINT fiscal_document_event_tenant_id_uq UNIQUE (tenant_id, id),
178
+ FOREIGN KEY (tenant_id, fiscal_document_id) REFERENCES fiscal_document (tenant_id, id),
179
+ FOREIGN KEY (tenant_id, raw_blob_id) REFERENCES blob (tenant_id, id)
180
+ );
181
+
182
+ CREATE UNIQUE INDEX IF NOT EXISTS fiscal_document_event_dedupe_uq
183
+ ON fiscal_document_event (tenant_id, fiscal_document_id, dedupe_key);
184
+
185
+ ALTER TABLE fiscal_document_event ENABLE ROW LEVEL SECURITY;
186
+ ALTER TABLE fiscal_document_event FORCE ROW LEVEL SECURITY;
187
+ CREATE POLICY fiscal_document_event_tenant_isolation ON fiscal_document_event
188
+ USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
189
+ WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
190
+
191
+ -- ── 3 · O cursor de sincronização ──────────────────────────────────────────
192
+ --
193
+ -- Por (tenant, cliente, fonte, ambiente), e não por job: o NSU do ambiente
194
+ -- nacional é POR CNPJ consultado. O `job_cursor` do Gate B0 é por tenant e não
195
+ -- tem essa granularidade — reaproveitá-lo colapsaria a posição de todos os
196
+ -- clientes numa só e o primeiro cliente sincronizado apagaria a posição dos
197
+ -- outros.
198
+ --
199
+ -- ═══ `next_attempt_after` é a defesa contra o cStat 656 ════════════════════
200
+ --
201
+ -- A NT 2014.002 §3.5 é explícita: "A empresa deverá aguardar um tempo mínimo de
202
+ -- UMA HORA para efetuar uma nova solicitação de distribuição caso receba a
203
+ -- indicação que não existem mais documentos" (`ultNSU` == `maxNSU`, cStat 137).
204
+ -- Consultar antes disso é "consumo indevido" e o ambiente nacional rejeita com
205
+ -- **cStat 656**, bloqueando o CNPJ.
206
+ --
207
+ -- E o CNPJ bloqueado é o DO CLIENTE, não o nosso. Um laço mal escrito aqui
208
+ -- produz dano num terceiro que não pediu nada — por isso a espera é uma coluna
209
+ -- do banco e não um `sleep` no processo. Processo morre; linha não.
210
+ CREATE TABLE IF NOT EXISTS fiscal_sync_cursor (
211
+ id uuid PRIMARY KEY,
212
+ tenant_id uuid NOT NULL REFERENCES tenant(id),
213
+ client_id uuid NOT NULL,
214
+ source text NOT NULL,
215
+ environment text NOT NULL
216
+ CHECK (environment IN ('synthetic', 'homologation', 'production')),
217
+ -- Última posição CONFIRMADAMENTE persistida. Nunca adiantada antes do commit
218
+ -- dos documentos daquele lote.
219
+ last_nsu text,
220
+ -- Maior NSU que o provedor declarou existir. `last_nsu = max_nsu` é a
221
+ -- condição que arma a espera de uma hora.
222
+ max_nsu text,
223
+ last_success_at timestamptz,
224
+ -- Antes deste instante, nenhuma requisição sai. É o que impede o 656.
225
+ next_attempt_after timestamptz,
226
+ consecutive_failures int NOT NULL DEFAULT 0 CHECK (consecutive_failures >= 0),
227
+ last_error_class text,
228
+ contract_version int NOT NULL DEFAULT 1,
229
+ created_at timestamptz NOT NULL DEFAULT now(),
230
+ updated_at timestamptz NOT NULL DEFAULT now(),
231
+ CONSTRAINT fiscal_sync_cursor_tenant_id_uq UNIQUE (tenant_id, id),
232
+ FOREIGN KEY (tenant_id, client_id) REFERENCES client (tenant_id, id)
233
+ );
234
+
235
+ CREATE UNIQUE INDEX IF NOT EXISTS fiscal_sync_cursor_uq
236
+ ON fiscal_sync_cursor (tenant_id, client_id, source, environment);
237
+
238
+ ALTER TABLE fiscal_sync_cursor ENABLE ROW LEVEL SECURITY;
239
+ ALTER TABLE fiscal_sync_cursor FORCE ROW LEVEL SECURITY;
240
+ CREATE POLICY fiscal_sync_cursor_tenant_isolation ON fiscal_sync_cursor
241
+ USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
242
+ WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
243
+
244
+ COMMENT ON COLUMN fiscal_sync_cursor.next_attempt_after IS
245
+ 'Defesa contra o cStat 656 da NT 2014.002: uma hora de espera depois de cStat 137. O CNPJ que o 656 bloqueia é o do CLIENTE — por isso a espera é linha de banco, não sleep de processo.';
246
+
247
+ -- ── 4 · A trilha do lote ───────────────────────────────────────────────────
248
+ --
249
+ -- `job_run` (Gate B0) responde "o worker pôde rodar?". Esta responde "o que a
250
+ -- busca trouxe?" — são perguntas diferentes e uma não substitui a outra.
251
+ --
252
+ -- Sem XML e sem PII: contadores, classe de erro e as duas pontas do cursor.
253
+ CREATE TABLE IF NOT EXISTS fiscal_sync_run (
254
+ id uuid PRIMARY KEY,
255
+ tenant_id uuid NOT NULL REFERENCES tenant(id),
256
+ client_id uuid NOT NULL,
257
+ source text NOT NULL,
258
+ environment text NOT NULL
259
+ CHECK (environment IN ('synthetic', 'homologation', 'production')),
260
+ outcome text NOT NULL
261
+ CHECK (outcome IN ('completed', 'no_documents', 'throttled',
262
+ 'blocked_eligibility', 'blocked_credential',
263
+ 'blocked_contract', 'provider_error', 'failed')),
264
+ documents_fetched int NOT NULL DEFAULT 0 CHECK (documents_fetched >= 0),
265
+ documents_new int NOT NULL DEFAULT 0 CHECK (documents_new >= 0),
266
+ documents_duplicate int NOT NULL DEFAULT 0 CHECK (documents_duplicate >= 0),
267
+ documents_failed int NOT NULL DEFAULT 0 CHECK (documents_failed >= 0),
268
+ cursor_before text,
269
+ cursor_after text,
270
+ error_class text,
271
+ detail text,
272
+ started_at timestamptz NOT NULL DEFAULT now(),
273
+ finished_at timestamptz,
274
+ duration_ms int,
275
+ CONSTRAINT fiscal_sync_run_tenant_id_uq UNIQUE (tenant_id, id),
276
+ FOREIGN KEY (tenant_id, client_id) REFERENCES client (tenant_id, id),
277
+ -- A mesma invariante do `job_run`, aplicada ao dado: um lote bloqueado não
278
+ -- trouxe documento e não moveu cursor. O banco recusa a linha incoerente em
279
+ -- vez de confiar na ordem das operações do TypeScript.
280
+ CONSTRAINT fiscal_sync_run_blocked_is_inert
281
+ CHECK (outcome NOT IN ('blocked_eligibility', 'blocked_credential', 'blocked_contract', 'throttled')
282
+ OR (documents_fetched = 0 AND documents_new = 0 AND cursor_after IS NOT DISTINCT FROM cursor_before))
283
+ );
284
+
285
+ CREATE INDEX IF NOT EXISTS fiscal_sync_run_idx
286
+ ON fiscal_sync_run (tenant_id, client_id, started_at DESC);
287
+
288
+ ALTER TABLE fiscal_sync_run ENABLE ROW LEVEL SECURITY;
289
+ ALTER TABLE fiscal_sync_run FORCE ROW LEVEL SECURITY;
290
+ CREATE POLICY fiscal_sync_run_tenant_isolation ON fiscal_sync_run
291
+ USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
292
+ WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
293
+
294
+ -- ── 5 · A capacidade que o worker fiscal vai exigir ────────────────────────
295
+ --
296
+ -- Declarada AQUI, na mesma migration que cria o schema que a sustenta — é a
297
+ -- regra expand → declare → deploy → contract do doc 142. Antes disso o worker
298
+ -- fica INCOMPATIBLE e não escreve; depois passa sozinho.
299
+ INSERT INTO platform_capability (key, label, contract_version, declared_by_migration) VALUES
300
+ ('office.fiscal-ingestion.v1',
301
+ 'Ingestão de documentos fiscais: registro canônico, eventos, cursor e trilha', 1, '0056')
302
+ ON CONFLICT (key) DO NOTHING;
303
+
304
+ -- ── 6 · O terceiro ambiente ────────────────────────────────────────────────
305
+ --
306
+ -- A 0055 modelou dois: `synthetic` e `production`. A pesquisa desta onda mostrou
307
+ -- que faltava um, e que ele é o mais importante do meio do caminho: a
308
+ -- **produção restrita** do ADN (`adn.producaorestrita.nfse.gov.br`) e a
309
+ -- homologação da SEFAZ não são nem laboratório nem produção. São ambientes
310
+ -- OFICIAIS, que respondem como o real, com documento que não vale nada.
311
+ --
312
+ -- Sem essa distinção, ligar homologação exigiria declarar o escritório em
313
+ -- `production` — e aí um adapter apontado para produção de verdade passaria a
314
+ -- elegibilidade que alguém abriu para ensaiar. É o fallback silencioso que o
315
+ -- Gate B0 proibiu, entrando pela porta do vocabulário.
316
+ ALTER TABLE tenant_fiscal_automation DROP CONSTRAINT IF EXISTS tenant_fiscal_automation_mode_check;
317
+ ALTER TABLE tenant_fiscal_automation ADD CONSTRAINT tenant_fiscal_automation_mode_check
318
+ CHECK (mode IN ('disabled', 'synthetic', 'homologation', 'production'));
319
+
320
+ COMMENT ON COLUMN tenant_fiscal_automation.mode IS
321
+ 'disabled (ausência de linha também) | synthetic (fixture) | homologation (ambiente OFICIAL de teste, documento sem valor) | production. Sem fallback entre eles, em nenhuma direção.';
322
+
323
+ -- ── 6 · Retirada de `client_fiscal_credential` ─────────────────────────────
324
+ --
325
+ -- Criada ontem, na 0055, e é invenção: o domínio de certificado já existe em
326
+ -- `digital_certificate` (vertical contábil, migration 1002), COM consumidores
327
+ -- vivos — a rota de compliance e o painel do piloto. Duas tabelas para o mesmo
328
+ -- fato dariam duas respostas para "este cliente tem certificado?", e elas
329
+ -- divergiriam na primeira vez que alguém atualizasse só uma.
330
+ --
331
+ -- A remoção é segura e foi medida antes: em produção a tabela tem
332
+ -- `n_tup_ins = 0` — nenhuma inserção desde a criação, não apenas nenhuma linha
333
+ -- viva. Nada é perdido porque nada foi escrito.
334
+ --
335
+ -- A prontidão passa a sair de `digital_certificate`, que ganha o que lhe
336
+ -- faltava para responder à pergunta (ver a migration 1004 da vertical).
337
+ DROP TABLE IF EXISTS client_fiscal_credential;
@@ -0,0 +1,228 @@
1
+ -- 0057 — a caixa de entrada da ingestão fiscal, e a direção que deixa de ser adivinhada.
2
+ --
3
+ -- ═══ O defeito que esta migration existe para fechar ═══════════════════════
4
+ --
5
+ -- A 0056 modelou `fiscal_document_event` como evento DE um documento: a coluna
6
+ -- `fiscal_document_id` é `NOT NULL` e tem chave estrangeira. Está certo — um
7
+ -- cancelamento é, por definição, o cancelamento de uma nota.
8
+ --
9
+ -- O que faltava é o que fazer com o evento que chega ANTES da nota. E ele
10
+ -- chega: o ambiente nacional ordena a distribuição por RECEPÇÃO, não por
11
+ -- emissão, e a NT 2014.002 não promete o contrário em lugar nenhum. Um evento
12
+ -- de NSU 100 sobre um documento que só será entregue no NSU 101 é o caso
13
+ -- normal, não a exceção.
14
+ --
15
+ -- O motor tratava esse evento como "duplicado", contava o item como absorvido e
16
+ -- **avançava o cursor**. O NSU 100 ficava commitado sem que nada de 100
17
+ -- existisse em lugar algum — e quando o documento 101 chegasse, o cancelamento
18
+ -- dele já teria sido perdido para sempre. Uma nota cancelada permaneceria
19
+ -- válida no fechamento, e a única forma de descobrir seria a fiscalização.
20
+ --
21
+ -- ═══ A invariante, escrita de uma vez ══════════════════════════════════════
22
+ --
23
+ -- NENHUM NSU É COMMITADO SEM QUE A INFORMAÇÃO CORRESPONDENTE ESTEJA
24
+ -- DURAVELMENTE PRESERVADA.
25
+ --
26
+ -- "Duravelmente preservada" tem quatro formas, e todas são linha de banco:
27
+ -- documento gravado, evento aplicado, evento GUARDADO À ESPERA do alvo, ou
28
+ -- item ilegível guardado como tal. Não encontrar o alvo não é nenhuma delas.
29
+ --
30
+ -- ═══ Por que uma caixa de entrada, e não `fiscal_document_event` nullable ══
31
+ --
32
+ -- Tornar `fiscal_document_id` nulável custaria mais do que parece:
33
+ --
34
+ -- · a deduplicação é `UNIQUE (tenant_id, fiscal_document_id, dedupe_key)`, e
35
+ -- em índice único NULL não colide com NULL — o mesmo evento pendente
36
+ -- entraria quantas vezes fosse reapresentado, que é exatamente a
37
+ -- propriedade que a dedupe existe para dar;
38
+ -- · a tabela não tem `client_id`, e sem ele não há como provar que o evento
39
+ -- do Cliente A não reconcilia com o documento do Cliente B — a RLS dela é
40
+ -- de tenant apenas, porque o documento-alvo já carregava a fronteira de
41
+ -- cliente;
42
+ -- · e a invariante "todo evento pertence a um documento", que hoje o banco
43
+ -- garante, viraria disciplina de código.
44
+ --
45
+ -- Então a espera vive em tabela própria. Ela NÃO é um domínio paralelo: nada
46
+ -- aqui é consultado como documento fiscal, nada aqui aparece na tela de
47
+ -- documentos, e a vida de uma linha daqui termina virando
48
+ -- `fiscal_document_event`. É uma caixa de entrada, e caixa de entrada é a
49
+ -- estrutura certa para "chegou, é válido, ainda não dá para arquivar".
50
+ --
51
+ -- ═══ Por que o RAW mora aqui dentro, em `bytea` ════════════════════════════
52
+ --
53
+ -- O documento fiscal guarda o XML no `blob` (storage externo, sha256, chave
54
+ -- particionada) e está certo: ele é grande, é permanente e tem valor
55
+ -- probatório. A caixa de entrada é o oposto — pequena, transitória e a única
56
+ -- coisa que separa o cursor de perder informação.
57
+ --
58
+ -- Se a preservação dependesse do storage, existiria uma janela real: gravar o
59
+ -- objeto, o processo morrer, e o cursor commitar depois com base num `PUT` que
60
+ -- não aconteceu. Guardando os bytes na MESMA transação que move o cursor, a
61
+ -- janela não existe — ou as duas coisas acontecem, ou nenhuma. É a diferença
62
+ -- entre uma invariante e uma intenção.
63
+ --
64
+ -- Quando o evento é reconciliado, o `fiscal_document_event` resultante segue o
65
+ -- caminho normal do `blob`; a linha desta caixa mantém os bytes originais como
66
+ -- histórico, e é isso que permite reprocessar um evento aplicado por engano sem
67
+ -- pedir nada ao provedor de novo.
68
+
69
+ -- ── 1 · A caixa de entrada ─────────────────────────────────────────────────
70
+ CREATE TABLE IF NOT EXISTS fiscal_ingestion_inbox (
71
+ id uuid PRIMARY KEY,
72
+ tenant_id uuid NOT NULL REFERENCES tenant(id),
73
+ -- Presente e NOT NULL de propósito: é a fronteira que impede o evento de um
74
+ -- cliente de reconciliar com o documento de outro dentro do mesmo escritório.
75
+ client_id uuid NOT NULL,
76
+
77
+ source text NOT NULL,
78
+ source_environment text NOT NULL
79
+ CHECK (source_environment IN ('synthetic', 'homologation', 'production')),
80
+ -- O NSU que entregou o item. NOT NULL aqui, ao contrário de
81
+ -- `fiscal_document.source_nsu`: uma linha desta tabela SÓ existe por causa de
82
+ -- um item de cursor, e sem o NSU não há como responder "o que havia em 100?".
83
+ source_nsu text NOT NULL,
84
+
85
+ -- Os três estados, e o que cada um significa:
86
+ --
87
+ -- pending_target evento válido cujo documento-alvo ainda não chegou.
88
+ -- applied já virou `fiscal_document_event`. Fica como histórico.
89
+ -- invalid o item não pôde ser interpretado. Os bytes ficam para
90
+ -- recuperação manual (`consNSU`) em vez de sumirem.
91
+ --
92
+ -- Não há estado `duplicate`: item repetido não cria linha nenhuma — o índice
93
+ -- único abaixo o absorve, e a repetição é um DESFECHO da ingestão, não um
94
+ -- estado do dado. Chamar de duplicata o evento sem alvo foi o defeito
95
+ -- original, e o vocabulário não vai voltar a permiti-lo.
96
+ state text NOT NULL DEFAULT 'pending_target'
97
+ CHECK (state IN ('pending_target', 'applied', 'invalid')),
98
+
99
+ document_type text CHECK (document_type IS NULL
100
+ OR document_type IN ('nfse', 'nfe', 'cte', 'nfce')),
101
+ event_type text CHECK (event_type IS NULL
102
+ OR event_type IN ('cancellation', 'replacement', 'correction',
103
+ 'manifestation', 'other')),
104
+ event_code text,
105
+ -- `nSeqEvento` do leiaute. Entra na ordem de aplicação quando dois eventos
106
+ -- do mesmo tipo caem na mesma caixa.
107
+ event_sequence text,
108
+ occurred_at timestamptz,
109
+ -- Chave do documento que este evento altera. É por ela que a reconciliação
110
+ -- encontra o que estava esperando.
111
+ target_access_key text,
112
+
113
+ raw bytea NOT NULL CHECK (octet_length(raw) <= 8388608),
114
+ raw_sha256 text NOT NULL CHECK (raw_sha256 ~ '^[a-f0-9]{64}$'),
115
+ normalized jsonb NOT NULL DEFAULT '{}'::jsonb,
116
+
117
+ -- Classe do motivo, nunca a mensagem do provedor nem trecho de XML.
118
+ reject_reason text,
119
+
120
+ received_at timestamptz NOT NULL DEFAULT now(),
121
+ applied_at timestamptz,
122
+ applied_event_id uuid,
123
+
124
+ dedupe_key text NOT NULL,
125
+
126
+ CONSTRAINT fiscal_ingestion_inbox_tenant_id_uq UNIQUE (tenant_id, id),
127
+ FOREIGN KEY (tenant_id, client_id) REFERENCES client (tenant_id, id),
128
+ FOREIGN KEY (tenant_id, applied_event_id) REFERENCES fiscal_document_event (tenant_id, id),
129
+
130
+ -- Um evento sem chave-alvo não teria como ser reconciliado nunca: seria
131
+ -- guardado para sempre sem chance de sair. O banco recusa em vez de deixar a
132
+ -- caixa acumular linhas inertes.
133
+ CONSTRAINT fiscal_ingestion_inbox_event_is_addressable
134
+ CHECK (state = 'invalid' OR (target_access_key IS NOT NULL AND event_type IS NOT NULL)),
135
+ -- Aplicado sem o evento resultante seria uma afirmação sem lastro — a mesma
136
+ -- classe do `full` sem RAW da 0056.
137
+ CONSTRAINT fiscal_ingestion_inbox_applied_has_event
138
+ CHECK (state <> 'applied' OR (applied_at IS NOT NULL AND applied_event_id IS NOT NULL)),
139
+ -- E inválido sem motivo seria um item descartado em silêncio com aparência
140
+ -- de registro.
141
+ CONSTRAINT fiscal_ingestion_inbox_invalid_has_reason
142
+ CHECK (state <> 'invalid' OR reject_reason IS NOT NULL)
143
+ );
144
+
145
+ -- ═══ A deduplicação é do BANCO ═════════════════════════════════════════════
146
+ --
147
+ -- Por (tenant, cliente, chave de dedupe), sem NULL em coluna nenhuma do índice.
148
+ -- É o que faz o replay do mesmo lote, o retry depois de crash e o evento
149
+ -- reapresentado depois do documento existir convergirem para uma linha só.
150
+ CREATE UNIQUE INDEX IF NOT EXISTS fiscal_ingestion_inbox_dedupe_uq
151
+ ON fiscal_ingestion_inbox (tenant_id, client_id, dedupe_key);
152
+
153
+ -- A busca da reconciliação: "quem estava esperando por esta chave?". Parcial
154
+ -- porque só o que está pendente interessa a essa pergunta.
155
+ CREATE INDEX IF NOT EXISTS fiscal_ingestion_inbox_pending_idx
156
+ ON fiscal_ingestion_inbox (tenant_id, client_id, target_access_key)
157
+ WHERE state = 'pending_target';
158
+
159
+ CREATE INDEX IF NOT EXISTS fiscal_ingestion_inbox_age_idx
160
+ ON fiscal_ingestion_inbox (tenant_id, state, received_at);
161
+
162
+ ALTER TABLE fiscal_ingestion_inbox ENABLE ROW LEVEL SECURITY;
163
+ ALTER TABLE fiscal_ingestion_inbox FORCE ROW LEVEL SECURITY;
164
+ -- Duas fronteiras, como em `fiscal_document`: tenant sempre, cliente quando o
165
+ -- portal define `app.current_client`.
166
+ CREATE POLICY fiscal_ingestion_inbox_tenant_isolation ON fiscal_ingestion_inbox
167
+ USING (
168
+ tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid
169
+ AND (
170
+ NULLIF(current_setting('app.current_client', true), '') IS NULL
171
+ OR client_id = NULLIF(current_setting('app.current_client', true), '')::uuid
172
+ )
173
+ )
174
+ WITH CHECK (
175
+ tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid
176
+ AND (
177
+ NULLIF(current_setting('app.current_client', true), '') IS NULL
178
+ OR client_id = NULLIF(current_setting('app.current_client', true), '')::uuid
179
+ )
180
+ );
181
+
182
+ COMMENT ON TABLE fiscal_ingestion_inbox IS
183
+ 'Itens de NSU que o modelo canônico ainda não pode representar: evento cujo documento-alvo não chegou, e item ilegível. Existe para que o cursor nunca avance sobre informação não preservada.';
184
+ COMMENT ON COLUMN fiscal_ingestion_inbox.raw IS
185
+ 'Bytes originais do item, na MESMA transação que move o cursor. Externalizá-los abriria a janela entre o PUT no storage e o commit do cursor.';
186
+ COMMENT ON COLUMN fiscal_ingestion_inbox.state IS
187
+ 'pending_target | applied | invalid. Não existe `duplicate`: item repetido não cria linha — chamar de duplicata o evento sem alvo foi o defeito que esta tabela corrige.';
188
+
189
+ -- ── 2 · Direção deixa de ser adivinhada ────────────────────────────────────
190
+ --
191
+ -- Até aqui o parser da NFS-e devolvia `direction: 'issued'` fixo, com um
192
+ -- comentário dizendo que a ingestão resolveria — e a ingestão não resolvia.
193
+ -- Toda nota capturada nascia "emitida pelo cliente", inclusive as em que ele é
194
+ -- o tomador. Num escritório contábil isso não é um rótulo errado: é a diferença
195
+ -- entre receita e despesa.
196
+ --
197
+ -- A direção passa a sair do CONFRONTO entre o documento do ator consultado e os
198
+ -- participantes declarados no leiaute (emitente, tomador, intermediário). E
199
+ -- quando o confronto não decide, o resultado é `unknown` — explícito, visível
200
+ -- na tela e filtrável — em vez de `issued` por omissão.
201
+ --
202
+ -- `unknown` não é um estado de erro: o ADN distribui ao contribuinte documentos
203
+ -- em que ele figura em qualquer dos três papéis, mas nada impede um leiaute
204
+ -- futuro, ou um documento parcial, de não trazer o participante que decidiria.
205
+ -- Um rótulo honesto de "não deu para dizer" é a única resposta que não mente.
206
+ ALTER TABLE fiscal_document DROP CONSTRAINT IF EXISTS fiscal_document_direction_check;
207
+ ALTER TABLE fiscal_document ADD CONSTRAINT fiscal_document_direction_check
208
+ CHECK (direction IN ('issued', 'received', 'intermediary', 'unknown'));
209
+
210
+ COMMENT ON COLUMN fiscal_document.direction IS
211
+ 'issued | received | intermediary | unknown. Resultado do confronto entre o documento do ator consultado e os participantes do leiaute. `unknown` quando o confronto não decide — nunca `issued` por omissão.';
212
+
213
+ -- ── 3 · A capacidade da nova forma ─────────────────────────────────────────
214
+ --
215
+ -- Chave NOVA, não `contract_version` maior: o registro é lido inteiro e versões
216
+ -- divergentes na mesma tabela o tornam ilegível (`probePlatformCapabilities`).
217
+ -- A regra da 0055 é que o registro só CRESCE, e compatibilidade é continência
218
+ -- de conjunto — mudança incompatível ganha `.v2`.
219
+ --
220
+ -- E ela é incompatível de verdade: um worker que escrevesse pela forma nova num
221
+ -- schema sem esta tabela veria o INSERT falhar, o item ficar não preservado e o
222
+ -- cursor parar. Fail-closed por construção — mas o portão explícito diz o
223
+ -- motivo antes, em vez de deixar o job parar sem explicação.
224
+ INSERT INTO platform_capability (key, label, contract_version, declared_by_migration) VALUES
225
+ ('office.fiscal-ingestion.v2',
226
+ 'Ingestão fiscal com caixa de entrada de evento pendente, reconciliação e direção classificada',
227
+ 1, '0057')
228
+ ON CONFLICT (key) DO NOTHING;