@baruk/core 0.3.1 → 0.4.2

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 (696) hide show
  1. package/.baruk-provenance.json +10 -0
  2. package/CHANGELOG.md +85 -0
  3. package/dist/auth/password-reset.d.ts +30 -1
  4. package/dist/auth/password-reset.d.ts.map +1 -1
  5. package/dist/auth/password-reset.js +139 -11
  6. package/dist/auth/password-reset.js.map +1 -1
  7. package/dist/auth/service.d.ts +39 -2
  8. package/dist/auth/service.d.ts.map +1 -1
  9. package/dist/auth/service.js +75 -10
  10. package/dist/auth/service.js.map +1 -1
  11. package/dist/billing/ar-repository.d.ts +334 -0
  12. package/dist/billing/ar-repository.d.ts.map +1 -0
  13. package/dist/billing/ar-repository.js +707 -0
  14. package/dist/billing/ar-repository.js.map +1 -0
  15. package/dist/billing/auto-generation.d.ts +87 -0
  16. package/dist/billing/auto-generation.d.ts.map +1 -0
  17. package/dist/billing/auto-generation.js +181 -0
  18. package/dist/billing/auto-generation.js.map +1 -0
  19. package/dist/billing/live-guard.d.ts +64 -0
  20. package/dist/billing/live-guard.d.ts.map +1 -0
  21. package/dist/billing/live-guard.js +68 -0
  22. package/dist/billing/live-guard.js.map +1 -0
  23. package/dist/billing/payment-means-catalog.d.ts +17 -0
  24. package/dist/billing/payment-means-catalog.d.ts.map +1 -0
  25. package/dist/billing/payment-means-catalog.js +21 -0
  26. package/dist/billing/payment-means-catalog.js.map +1 -0
  27. package/dist/billing/payment-means.d.ts +38 -0
  28. package/dist/billing/payment-means.d.ts.map +1 -0
  29. package/dist/billing/payment-means.js +29 -0
  30. package/dist/billing/payment-means.js.map +1 -0
  31. package/dist/billing/payment-rail.d.ts +118 -0
  32. package/dist/billing/payment-rail.d.ts.map +1 -0
  33. package/dist/billing/payment-rail.js +295 -0
  34. package/dist/billing/payment-rail.js.map +1 -0
  35. package/dist/billing/provider/asaas.d.ts +46 -1
  36. package/dist/billing/provider/asaas.d.ts.map +1 -1
  37. package/dist/billing/provider/asaas.js +194 -14
  38. package/dist/billing/provider/asaas.js.map +1 -1
  39. package/dist/billing/provider/manual.js +1 -1
  40. package/dist/billing/provider/manual.js.map +1 -1
  41. package/dist/billing/provider/types.d.ts +115 -8
  42. package/dist/billing/provider/types.d.ts.map +1 -1
  43. package/dist/billing/provider-account.d.ts +89 -0
  44. package/dist/billing/provider-account.d.ts.map +1 -0
  45. package/dist/billing/provider-account.js +161 -0
  46. package/dist/billing/provider-account.js.map +1 -0
  47. package/dist/billing/provider-onboarding.d.ts +67 -0
  48. package/dist/billing/provider-onboarding.d.ts.map +1 -0
  49. package/dist/billing/provider-onboarding.js +234 -0
  50. package/dist/billing/provider-onboarding.js.map +1 -0
  51. package/dist/billing/receivable.d.ts +5 -1
  52. package/dist/billing/receivable.d.ts.map +1 -1
  53. package/dist/billing/reconcile.d.ts +58 -0
  54. package/dist/billing/reconcile.d.ts.map +1 -0
  55. package/dist/billing/reconcile.js +138 -0
  56. package/dist/billing/reconcile.js.map +1 -0
  57. package/dist/billing/repository.d.ts +90 -1
  58. package/dist/billing/repository.d.ts.map +1 -1
  59. package/dist/billing/repository.js +148 -19
  60. package/dist/billing/repository.js.map +1 -1
  61. package/dist/cjs/auth/password-reset.js +141 -11
  62. package/dist/cjs/auth/password-reset.js.map +1 -1
  63. package/dist/cjs/auth/service.js +75 -9
  64. package/dist/cjs/auth/service.js.map +1 -1
  65. package/dist/cjs/billing/ar-repository.js +728 -0
  66. package/dist/cjs/billing/ar-repository.js.map +1 -0
  67. package/dist/cjs/billing/auto-generation.js +186 -0
  68. package/dist/cjs/billing/auto-generation.js.map +1 -0
  69. package/dist/cjs/billing/live-guard.js +72 -0
  70. package/dist/cjs/billing/live-guard.js.map +1 -0
  71. package/dist/cjs/billing/payment-means-catalog.js +25 -0
  72. package/dist/cjs/billing/payment-means-catalog.js.map +1 -0
  73. package/dist/cjs/billing/payment-means.js +33 -0
  74. package/dist/cjs/billing/payment-means.js.map +1 -0
  75. package/dist/cjs/billing/payment-rail.js +301 -0
  76. package/dist/cjs/billing/payment-rail.js.map +1 -0
  77. package/dist/cjs/billing/provider/asaas.js +194 -14
  78. package/dist/cjs/billing/provider/asaas.js.map +1 -1
  79. package/dist/cjs/billing/provider/manual.js +1 -1
  80. package/dist/cjs/billing/provider/manual.js.map +1 -1
  81. package/dist/cjs/billing/provider-account.js +170 -0
  82. package/dist/cjs/billing/provider-account.js.map +1 -0
  83. package/dist/cjs/billing/provider-onboarding.js +239 -0
  84. package/dist/cjs/billing/provider-onboarding.js.map +1 -0
  85. package/dist/cjs/billing/reconcile.js +143 -0
  86. package/dist/cjs/billing/reconcile.js.map +1 -0
  87. package/dist/cjs/billing/repository.js +150 -18
  88. package/dist/cjs/billing/repository.js.map +1 -1
  89. package/dist/cjs/clients/repository.js +96 -20
  90. package/dist/cjs/clients/repository.js.map +1 -1
  91. package/dist/cjs/collection/branding.js +140 -0
  92. package/dist/cjs/collection/branding.js.map +1 -0
  93. package/dist/cjs/collection/channel-accounts.js +137 -0
  94. package/dist/cjs/collection/channel-accounts.js.map +1 -0
  95. package/dist/cjs/collection/channel.js +17 -0
  96. package/dist/cjs/collection/channel.js.map +1 -0
  97. package/dist/cjs/collection/communication-bridge.js +140 -0
  98. package/dist/cjs/collection/communication-bridge.js.map +1 -0
  99. package/dist/cjs/collection/communication-runtime.js +193 -0
  100. package/dist/cjs/collection/communication-runtime.js.map +1 -0
  101. package/dist/cjs/collection/consent-repository.js +84 -0
  102. package/dist/cjs/collection/consent-repository.js.map +1 -0
  103. package/dist/cjs/collection/copy.js +125 -0
  104. package/dist/cjs/collection/copy.js.map +1 -0
  105. package/dist/cjs/collection/delivery-callback.js +151 -0
  106. package/dist/cjs/collection/delivery-callback.js.map +1 -0
  107. package/dist/cjs/collection/email-layout.js +252 -0
  108. package/dist/cjs/collection/email-layout.js.map +1 -0
  109. package/dist/cjs/collection/engine.js +414 -0
  110. package/dist/cjs/collection/engine.js.map +1 -0
  111. package/dist/cjs/collection/format.js +76 -0
  112. package/dist/cjs/collection/format.js.map +1 -0
  113. package/dist/cjs/collection/payment-block.js +3 -0
  114. package/dist/cjs/collection/payment-block.js.map +1 -0
  115. package/dist/cjs/collection/policy.js +147 -0
  116. package/dist/cjs/collection/policy.js.map +1 -0
  117. package/dist/cjs/collection/repository.js +260 -0
  118. package/dist/cjs/collection/repository.js.map +1 -0
  119. package/dist/cjs/collection/subject.js +104 -0
  120. package/dist/cjs/collection/subject.js.map +1 -0
  121. package/dist/cjs/collection/template-catalog.js +165 -0
  122. package/dist/cjs/collection/template-catalog.js.map +1 -0
  123. package/dist/cjs/collection/template.js +78 -0
  124. package/dist/cjs/collection/template.js.map +1 -0
  125. package/dist/cjs/communication/adapters/email.js +132 -0
  126. package/dist/cjs/communication/adapters/email.js.map +1 -0
  127. package/dist/cjs/communication/adapters/sms.js +127 -0
  128. package/dist/cjs/communication/adapters/sms.js.map +1 -0
  129. package/dist/cjs/communication/adapters/whatsapp-language.js +39 -0
  130. package/dist/cjs/communication/adapters/whatsapp-language.js.map +1 -0
  131. package/dist/cjs/communication/adapters/whatsapp-templates.js +100 -0
  132. package/dist/cjs/communication/adapters/whatsapp-templates.js.map +1 -0
  133. package/dist/cjs/communication/adapters/whatsapp.js +213 -0
  134. package/dist/cjs/communication/adapters/whatsapp.js.map +1 -0
  135. package/dist/cjs/communication/audit.js +61 -0
  136. package/dist/cjs/communication/audit.js.map +1 -0
  137. package/dist/cjs/communication/capabilities.js +76 -0
  138. package/dist/cjs/communication/capabilities.js.map +1 -0
  139. package/dist/cjs/communication/channel.js +53 -0
  140. package/dist/cjs/communication/channel.js.map +1 -0
  141. package/dist/cjs/communication/claim.js +228 -0
  142. package/dist/cjs/communication/claim.js.map +1 -0
  143. package/dist/cjs/communication/consent.js +43 -0
  144. package/dist/cjs/communication/consent.js.map +1 -0
  145. package/dist/cjs/communication/context.js +45 -0
  146. package/dist/cjs/communication/context.js.map +1 -0
  147. package/dist/cjs/communication/credentials.js +121 -0
  148. package/dist/cjs/communication/credentials.js.map +1 -0
  149. package/dist/cjs/communication/dry-run-scenario.js +161 -0
  150. package/dist/cjs/communication/dry-run-scenario.js.map +1 -0
  151. package/dist/cjs/communication/dry-run.js +97 -0
  152. package/dist/cjs/communication/dry-run.js.map +1 -0
  153. package/dist/cjs/communication/engine.js +483 -0
  154. package/dist/cjs/communication/engine.js.map +1 -0
  155. package/dist/cjs/communication/graph.js +50 -0
  156. package/dist/cjs/communication/graph.js.map +1 -0
  157. package/dist/cjs/communication/idempotency.js +26 -0
  158. package/dist/cjs/communication/idempotency.js.map +1 -0
  159. package/dist/cjs/communication/identity.js +110 -0
  160. package/dist/cjs/communication/identity.js.map +1 -0
  161. package/dist/cjs/communication/index.js +135 -0
  162. package/dist/cjs/communication/index.js.map +1 -0
  163. package/dist/cjs/communication/onboarding.js +297 -0
  164. package/dist/cjs/communication/onboarding.js.map +1 -0
  165. package/dist/cjs/communication/policy.js +16 -0
  166. package/dist/cjs/communication/policy.js.map +1 -0
  167. package/dist/cjs/communication/routing.js +77 -0
  168. package/dist/cjs/communication/routing.js.map +1 -0
  169. package/dist/cjs/communication/sender-profile.js +128 -0
  170. package/dist/cjs/communication/sender-profile.js.map +1 -0
  171. package/dist/cjs/communication/status.js +86 -0
  172. package/dist/cjs/communication/status.js.map +1 -0
  173. package/dist/cjs/communication/stores/onboarding.js +135 -0
  174. package/dist/cjs/communication/stores/onboarding.js.map +1 -0
  175. package/dist/cjs/communication/stores/postgres.js +458 -0
  176. package/dist/cjs/communication/stores/postgres.js.map +1 -0
  177. package/dist/cjs/communication/stores/routing.js +334 -0
  178. package/dist/cjs/communication/stores/routing.js.map +1 -0
  179. package/dist/cjs/communication/stores/secrets.js +126 -0
  180. package/dist/cjs/communication/stores/secrets.js.map +1 -0
  181. package/dist/cjs/communication/template-provider.js +22 -0
  182. package/dist/cjs/communication/template-provider.js.map +1 -0
  183. package/dist/cjs/communication/template.js +63 -0
  184. package/dist/cjs/communication/template.js.map +1 -0
  185. package/dist/cjs/communication/webhook-subscription.js +258 -0
  186. package/dist/cjs/communication/webhook-subscription.js.map +1 -0
  187. package/dist/cjs/communication/webhook.js +292 -0
  188. package/dist/cjs/communication/webhook.js.map +1 -0
  189. package/dist/cjs/competence/repository.js +309 -0
  190. package/dist/cjs/competence/repository.js.map +1 -0
  191. package/dist/cjs/contacts/phone.js +192 -0
  192. package/dist/cjs/contacts/phone.js.map +1 -0
  193. package/dist/cjs/db/seed-catalog.js +48 -0
  194. package/dist/cjs/db/seed-catalog.js.map +1 -0
  195. package/dist/cjs/db/seed.js +15 -7
  196. package/dist/cjs/db/seed.js.map +1 -1
  197. package/dist/cjs/documents/repository.js +244 -0
  198. package/dist/cjs/documents/repository.js.map +1 -0
  199. package/dist/cjs/documents/requests.js +136 -0
  200. package/dist/cjs/documents/requests.js.map +1 -0
  201. package/dist/cjs/index.js +406 -39
  202. package/dist/cjs/index.js.map +1 -1
  203. package/dist/cjs/legal/documents.js +125 -3
  204. package/dist/cjs/legal/documents.js.map +1 -1
  205. package/dist/cjs/legal/types.js +1 -0
  206. package/dist/cjs/legal/types.js.map +1 -1
  207. package/dist/cjs/mail/auth-templates.js +29 -0
  208. package/dist/cjs/mail/auth-templates.js.map +1 -0
  209. package/dist/cjs/mail/dedup-store.js +12 -51
  210. package/dist/cjs/mail/dedup-store.js.map +1 -1
  211. package/dist/cjs/mail/index.js +16 -4
  212. package/dist/cjs/mail/index.js.map +1 -1
  213. package/dist/cjs/mail/provider.js +12 -247
  214. package/dist/cjs/mail/provider.js.map +1 -1
  215. package/dist/cjs/mail/resend.js +14 -18
  216. package/dist/cjs/mail/resend.js.map +1 -1
  217. package/dist/cjs/mail/sender-resolver.js +12 -11
  218. package/dist/cjs/mail/sender-resolver.js.map +1 -1
  219. package/dist/cjs/mail/template-renderer.js +11 -16
  220. package/dist/cjs/mail/template-renderer.js.map +1 -1
  221. package/dist/cjs/mail/transactional-outbound.js +117 -0
  222. package/dist/cjs/mail/transactional-outbound.js.map +1 -0
  223. package/dist/cjs/mail/types.js +8 -35
  224. package/dist/cjs/mail/types.js.map +1 -1
  225. package/dist/cjs/portal/provisioning.js +144 -0
  226. package/dist/cjs/portal/provisioning.js.map +1 -0
  227. package/dist/cjs/pricing-engine/canonical.js +42 -0
  228. package/dist/cjs/pricing-engine/canonical.js.map +1 -0
  229. package/dist/cjs/pricing-engine/errors.js +6 -1
  230. package/dist/cjs/pricing-engine/errors.js.map +1 -1
  231. package/dist/cjs/pricing-engine/index.js +12 -1
  232. package/dist/cjs/pricing-engine/index.js.map +1 -1
  233. package/dist/cjs/pricing-engine/pipeline.js +200 -26
  234. package/dist/cjs/pricing-engine/pipeline.js.map +1 -1
  235. package/dist/cjs/pricing-engine/shadow-adapter.js +79 -0
  236. package/dist/cjs/pricing-engine/shadow-adapter.js.map +1 -0
  237. package/dist/cjs/pricing-engine/shadow-diff.js +55 -0
  238. package/dist/cjs/pricing-engine/shadow-diff.js.map +1 -0
  239. package/dist/cjs/pricing-engine/shadow-facade.js +85 -0
  240. package/dist/cjs/pricing-engine/shadow-facade.js.map +1 -0
  241. package/dist/cjs/pricing-engine/shadow-legacy-types.js +19 -0
  242. package/dist/cjs/pricing-engine/shadow-legacy-types.js.map +1 -0
  243. package/dist/cjs/pricing-engine/shadow-metrics.js +34 -0
  244. package/dist/cjs/pricing-engine/shadow-metrics.js.map +1 -0
  245. package/dist/cjs/pricing-engine/shadow-normalizer.js +42 -0
  246. package/dist/cjs/pricing-engine/shadow-normalizer.js.map +1 -0
  247. package/dist/cjs/rbac/authorize.js +28 -0
  248. package/dist/cjs/rbac/authorize.js.map +1 -1
  249. package/dist/cjs/rbac/catalog.js +67 -1
  250. package/dist/cjs/rbac/catalog.js.map +1 -1
  251. package/dist/cjs/rbac/workload-provisioning.js +160 -0
  252. package/dist/cjs/rbac/workload-provisioning.js.map +1 -0
  253. package/dist/cjs/rbac/workload.js +191 -0
  254. package/dist/cjs/rbac/workload.js.map +1 -0
  255. package/dist/cjs/storage/checksum.js +15 -0
  256. package/dist/cjs/storage/checksum.js.map +1 -0
  257. package/dist/cjs/storage/key.js +72 -0
  258. package/dist/cjs/storage/key.js.map +1 -0
  259. package/dist/cjs/storage/local.js +100 -0
  260. package/dist/cjs/storage/local.js.map +1 -0
  261. package/dist/cjs/storage/r2.js +181 -0
  262. package/dist/cjs/storage/r2.js.map +1 -0
  263. package/dist/cjs/storage/registry.js +41 -0
  264. package/dist/cjs/storage/registry.js.map +1 -0
  265. package/dist/cjs/storage/types.js +50 -0
  266. package/dist/cjs/storage/types.js.map +1 -0
  267. package/dist/cjs/subscription-billing/provider/asaas.js +32 -7
  268. package/dist/cjs/subscription-billing/provider/asaas.js.map +1 -1
  269. package/dist/cjs/subscription-billing/provider/disabled.js +8 -0
  270. package/dist/cjs/subscription-billing/provider/disabled.js.map +1 -1
  271. package/dist/cjs/subscription-billing/provider/registry.js +17 -0
  272. package/dist/cjs/subscription-billing/provider/registry.js.map +1 -1
  273. package/dist/cjs/subscription-billing/service.js +57 -8
  274. package/dist/cjs/subscription-billing/service.js.map +1 -1
  275. package/dist/cjs/subscription-billing/types.js.map +1 -1
  276. package/dist/cjs/tenancy/provisioning.js +32 -8
  277. package/dist/cjs/tenancy/provisioning.js.map +1 -1
  278. package/dist/cjs/users/repository.js +119 -2
  279. package/dist/cjs/users/repository.js.map +1 -1
  280. package/dist/cjs/vault/repository.js +81 -0
  281. package/dist/cjs/vault/repository.js.map +1 -1
  282. package/dist/clients/repository.d.ts +42 -0
  283. package/dist/clients/repository.d.ts.map +1 -1
  284. package/dist/clients/repository.js +94 -21
  285. package/dist/clients/repository.js.map +1 -1
  286. package/dist/collection/branding.d.ts +72 -0
  287. package/dist/collection/branding.d.ts.map +1 -0
  288. package/dist/collection/branding.js +136 -0
  289. package/dist/collection/branding.js.map +1 -0
  290. package/dist/collection/channel-accounts.d.ts +57 -0
  291. package/dist/collection/channel-accounts.d.ts.map +1 -0
  292. package/dist/collection/channel-accounts.js +133 -0
  293. package/dist/collection/channel-accounts.js.map +1 -0
  294. package/dist/collection/channel.d.ts +24 -0
  295. package/dist/collection/channel.d.ts.map +1 -0
  296. package/dist/collection/channel.js +16 -0
  297. package/dist/collection/channel.js.map +1 -0
  298. package/dist/collection/communication-bridge.d.ts +66 -0
  299. package/dist/collection/communication-bridge.d.ts.map +1 -0
  300. package/dist/collection/communication-bridge.js +134 -0
  301. package/dist/collection/communication-bridge.js.map +1 -0
  302. package/dist/collection/communication-runtime.d.ts +95 -0
  303. package/dist/collection/communication-runtime.d.ts.map +1 -0
  304. package/dist/collection/communication-runtime.js +187 -0
  305. package/dist/collection/communication-runtime.js.map +1 -0
  306. package/dist/collection/consent-repository.d.ts +32 -0
  307. package/dist/collection/consent-repository.d.ts.map +1 -0
  308. package/dist/collection/consent-repository.js +79 -0
  309. package/dist/collection/consent-repository.js.map +1 -0
  310. package/dist/collection/copy.d.ts +50 -0
  311. package/dist/collection/copy.d.ts.map +1 -0
  312. package/dist/collection/copy.js +122 -0
  313. package/dist/collection/copy.js.map +1 -0
  314. package/dist/collection/delivery-callback.d.ts +49 -0
  315. package/dist/collection/delivery-callback.d.ts.map +1 -0
  316. package/dist/collection/delivery-callback.js +147 -0
  317. package/dist/collection/delivery-callback.js.map +1 -0
  318. package/dist/collection/email-layout.d.ts +45 -0
  319. package/dist/collection/email-layout.d.ts.map +1 -0
  320. package/dist/collection/email-layout.js +247 -0
  321. package/dist/collection/email-layout.js.map +1 -0
  322. package/dist/collection/engine.d.ts +177 -0
  323. package/dist/collection/engine.d.ts.map +1 -0
  324. package/dist/collection/engine.js +407 -0
  325. package/dist/collection/engine.js.map +1 -0
  326. package/dist/collection/format.d.ts +32 -0
  327. package/dist/collection/format.d.ts.map +1 -0
  328. package/dist/collection/format.js +70 -0
  329. package/dist/collection/format.js.map +1 -0
  330. package/dist/collection/payment-block.d.ts +24 -0
  331. package/dist/collection/payment-block.d.ts.map +1 -0
  332. package/dist/collection/payment-block.js +2 -0
  333. package/dist/collection/payment-block.js.map +1 -0
  334. package/dist/collection/policy.d.ts +53 -0
  335. package/dist/collection/policy.d.ts.map +1 -0
  336. package/dist/collection/policy.js +141 -0
  337. package/dist/collection/policy.js.map +1 -0
  338. package/dist/collection/repository.d.ts +129 -0
  339. package/dist/collection/repository.d.ts.map +1 -0
  340. package/dist/collection/repository.js +246 -0
  341. package/dist/collection/repository.js.map +1 -0
  342. package/dist/collection/subject.d.ts +66 -0
  343. package/dist/collection/subject.d.ts.map +1 -0
  344. package/dist/collection/subject.js +99 -0
  345. package/dist/collection/subject.js.map +1 -0
  346. package/dist/collection/template-catalog.d.ts +16 -0
  347. package/dist/collection/template-catalog.d.ts.map +1 -0
  348. package/dist/collection/template-catalog.js +162 -0
  349. package/dist/collection/template-catalog.js.map +1 -0
  350. package/dist/collection/template.d.ts +37 -0
  351. package/dist/collection/template.d.ts.map +1 -0
  352. package/dist/collection/template.js +73 -0
  353. package/dist/collection/template.js.map +1 -0
  354. package/dist/communication/adapters/email.d.ts +28 -0
  355. package/dist/communication/adapters/email.d.ts.map +1 -0
  356. package/dist/communication/adapters/email.js +129 -0
  357. package/dist/communication/adapters/email.js.map +1 -0
  358. package/dist/communication/adapters/sms.d.ts +95 -0
  359. package/dist/communication/adapters/sms.d.ts.map +1 -0
  360. package/dist/communication/adapters/sms.js +121 -0
  361. package/dist/communication/adapters/sms.js.map +1 -0
  362. package/dist/communication/adapters/whatsapp-language.d.ts +34 -0
  363. package/dist/communication/adapters/whatsapp-language.d.ts.map +1 -0
  364. package/dist/communication/adapters/whatsapp-language.js +36 -0
  365. package/dist/communication/adapters/whatsapp-language.js.map +1 -0
  366. package/dist/communication/adapters/whatsapp-templates.d.ts +18 -0
  367. package/dist/communication/adapters/whatsapp-templates.d.ts.map +1 -0
  368. package/dist/communication/adapters/whatsapp-templates.js +97 -0
  369. package/dist/communication/adapters/whatsapp-templates.js.map +1 -0
  370. package/dist/communication/adapters/whatsapp.d.ts +29 -0
  371. package/dist/communication/adapters/whatsapp.d.ts.map +1 -0
  372. package/dist/communication/adapters/whatsapp.js +210 -0
  373. package/dist/communication/adapters/whatsapp.js.map +1 -0
  374. package/dist/communication/audit.d.ts +105 -0
  375. package/dist/communication/audit.d.ts.map +1 -0
  376. package/dist/communication/audit.js +56 -0
  377. package/dist/communication/audit.js.map +1 -0
  378. package/dist/communication/capabilities.d.ts +88 -0
  379. package/dist/communication/capabilities.d.ts.map +1 -0
  380. package/dist/communication/capabilities.js +72 -0
  381. package/dist/communication/capabilities.js.map +1 -0
  382. package/dist/communication/channel.d.ts +125 -0
  383. package/dist/communication/channel.d.ts.map +1 -0
  384. package/dist/communication/channel.js +47 -0
  385. package/dist/communication/channel.js.map +1 -0
  386. package/dist/communication/claim.d.ts +368 -0
  387. package/dist/communication/claim.d.ts.map +1 -0
  388. package/dist/communication/claim.js +224 -0
  389. package/dist/communication/claim.js.map +1 -0
  390. package/dist/communication/consent.d.ts +65 -0
  391. package/dist/communication/consent.d.ts.map +1 -0
  392. package/dist/communication/consent.js +40 -0
  393. package/dist/communication/consent.js.map +1 -0
  394. package/dist/communication/context.d.ts +48 -0
  395. package/dist/communication/context.d.ts.map +1 -0
  396. package/dist/communication/context.js +41 -0
  397. package/dist/communication/context.js.map +1 -0
  398. package/dist/communication/credentials.d.ts +74 -0
  399. package/dist/communication/credentials.d.ts.map +1 -0
  400. package/dist/communication/credentials.js +115 -0
  401. package/dist/communication/credentials.js.map +1 -0
  402. package/dist/communication/dry-run-scenario.d.ts +37 -0
  403. package/dist/communication/dry-run-scenario.d.ts.map +1 -0
  404. package/dist/communication/dry-run-scenario.js +158 -0
  405. package/dist/communication/dry-run-scenario.js.map +1 -0
  406. package/dist/communication/dry-run.d.ts +40 -0
  407. package/dist/communication/dry-run.d.ts.map +1 -0
  408. package/dist/communication/dry-run.js +92 -0
  409. package/dist/communication/dry-run.js.map +1 -0
  410. package/dist/communication/engine.d.ts +153 -0
  411. package/dist/communication/engine.d.ts.map +1 -0
  412. package/dist/communication/engine.js +480 -0
  413. package/dist/communication/engine.js.map +1 -0
  414. package/dist/communication/graph.d.ts +38 -0
  415. package/dist/communication/graph.d.ts.map +1 -0
  416. package/dist/communication/graph.js +45 -0
  417. package/dist/communication/graph.js.map +1 -0
  418. package/dist/communication/idempotency.d.ts +33 -0
  419. package/dist/communication/idempotency.d.ts.map +1 -0
  420. package/dist/communication/idempotency.js +23 -0
  421. package/dist/communication/idempotency.js.map +1 -0
  422. package/dist/communication/identity.d.ts +127 -0
  423. package/dist/communication/identity.d.ts.map +1 -0
  424. package/dist/communication/identity.js +105 -0
  425. package/dist/communication/identity.js.map +1 -0
  426. package/dist/communication/index.d.ts +66 -0
  427. package/dist/communication/index.d.ts.map +1 -0
  428. package/dist/communication/index.js +42 -0
  429. package/dist/communication/index.js.map +1 -0
  430. package/dist/communication/onboarding.d.ts +223 -0
  431. package/dist/communication/onboarding.d.ts.map +1 -0
  432. package/dist/communication/onboarding.js +287 -0
  433. package/dist/communication/onboarding.js.map +1 -0
  434. package/dist/communication/policy.d.ts +99 -0
  435. package/dist/communication/policy.d.ts.map +1 -0
  436. package/dist/communication/policy.js +13 -0
  437. package/dist/communication/policy.js.map +1 -0
  438. package/dist/communication/routing.d.ts +99 -0
  439. package/dist/communication/routing.d.ts.map +1 -0
  440. package/dist/communication/routing.js +73 -0
  441. package/dist/communication/routing.js.map +1 -0
  442. package/dist/communication/sender-profile.d.ts +100 -0
  443. package/dist/communication/sender-profile.d.ts.map +1 -0
  444. package/dist/communication/sender-profile.js +123 -0
  445. package/dist/communication/sender-profile.js.map +1 -0
  446. package/dist/communication/status.d.ts +48 -0
  447. package/dist/communication/status.d.ts.map +1 -0
  448. package/dist/communication/status.js +79 -0
  449. package/dist/communication/status.js.map +1 -0
  450. package/dist/communication/stores/onboarding.d.ts +88 -0
  451. package/dist/communication/stores/onboarding.d.ts.map +1 -0
  452. package/dist/communication/stores/onboarding.js +127 -0
  453. package/dist/communication/stores/onboarding.js.map +1 -0
  454. package/dist/communication/stores/postgres.d.ts +81 -0
  455. package/dist/communication/stores/postgres.d.ts.map +1 -0
  456. package/dist/communication/stores/postgres.js +453 -0
  457. package/dist/communication/stores/postgres.js.map +1 -0
  458. package/dist/communication/stores/routing.d.ts +169 -0
  459. package/dist/communication/stores/routing.d.ts.map +1 -0
  460. package/dist/communication/stores/routing.js +324 -0
  461. package/dist/communication/stores/routing.js.map +1 -0
  462. package/dist/communication/stores/secrets.d.ts +89 -0
  463. package/dist/communication/stores/secrets.d.ts.map +1 -0
  464. package/dist/communication/stores/secrets.js +122 -0
  465. package/dist/communication/stores/secrets.js.map +1 -0
  466. package/dist/communication/template-provider.d.ts +85 -0
  467. package/dist/communication/template-provider.d.ts.map +1 -0
  468. package/dist/communication/template-provider.js +21 -0
  469. package/dist/communication/template-provider.js.map +1 -0
  470. package/dist/communication/template.d.ts +87 -0
  471. package/dist/communication/template.d.ts.map +1 -0
  472. package/dist/communication/template.js +59 -0
  473. package/dist/communication/template.js.map +1 -0
  474. package/dist/communication/webhook-subscription.d.ts +156 -0
  475. package/dist/communication/webhook-subscription.d.ts.map +1 -0
  476. package/dist/communication/webhook-subscription.js +252 -0
  477. package/dist/communication/webhook-subscription.js.map +1 -0
  478. package/dist/communication/webhook.d.ts +121 -0
  479. package/dist/communication/webhook.d.ts.map +1 -0
  480. package/dist/communication/webhook.js +283 -0
  481. package/dist/communication/webhook.js.map +1 -0
  482. package/dist/competence/repository.d.ts +147 -0
  483. package/dist/competence/repository.d.ts.map +1 -0
  484. package/dist/competence/repository.js +294 -0
  485. package/dist/competence/repository.js.map +1 -0
  486. package/dist/contacts/phone.d.ts +70 -0
  487. package/dist/contacts/phone.d.ts.map +1 -0
  488. package/dist/contacts/phone.js +186 -0
  489. package/dist/contacts/phone.js.map +1 -0
  490. package/dist/db/migrate.js +17 -11
  491. package/dist/db/migrate.js.map +1 -1
  492. package/dist/db/seed-catalog.d.ts +18 -0
  493. package/dist/db/seed-catalog.d.ts.map +1 -0
  494. package/dist/db/seed-catalog.js +45 -0
  495. package/dist/db/seed-catalog.js.map +1 -0
  496. package/dist/db/seed.js +16 -8
  497. package/dist/db/seed.js.map +1 -1
  498. package/dist/documents/repository.d.ts +97 -0
  499. package/dist/documents/repository.d.ts.map +1 -0
  500. package/dist/documents/repository.js +236 -0
  501. package/dist/documents/repository.js.map +1 -0
  502. package/dist/documents/requests.d.ts +73 -0
  503. package/dist/documents/requests.d.ts.map +1 -0
  504. package/dist/documents/requests.js +127 -0
  505. package/dist/documents/requests.js.map +1 -0
  506. package/dist/index.d.ts +109 -13
  507. package/dist/index.d.ts.map +1 -1
  508. package/dist/index.js +100 -8
  509. package/dist/index.js.map +1 -1
  510. package/dist/legal/documents.d.ts.map +1 -1
  511. package/dist/legal/documents.js +125 -3
  512. package/dist/legal/documents.js.map +1 -1
  513. package/dist/legal/index.d.ts +1 -1
  514. package/dist/legal/index.d.ts.map +1 -1
  515. package/dist/legal/types.d.ts +32 -1
  516. package/dist/legal/types.d.ts.map +1 -1
  517. package/dist/legal/types.js +1 -0
  518. package/dist/legal/types.js.map +1 -1
  519. package/dist/mail/auth-templates.d.ts +15 -0
  520. package/dist/mail/auth-templates.d.ts.map +1 -0
  521. package/dist/mail/auth-templates.js +26 -0
  522. package/dist/mail/auth-templates.js.map +1 -0
  523. package/dist/mail/dedup-store.d.ts +1 -24
  524. package/dist/mail/dedup-store.d.ts.map +1 -1
  525. package/dist/mail/dedup-store.js +9 -48
  526. package/dist/mail/dedup-store.js.map +1 -1
  527. package/dist/mail/index.d.ts +2 -1
  528. package/dist/mail/index.d.ts.map +1 -1
  529. package/dist/mail/index.js +13 -2
  530. package/dist/mail/index.js.map +1 -1
  531. package/dist/mail/provider.d.ts +2 -30
  532. package/dist/mail/provider.d.ts.map +1 -1
  533. package/dist/mail/provider.js +10 -246
  534. package/dist/mail/provider.js.map +1 -1
  535. package/dist/mail/resend.d.ts +2 -12
  536. package/dist/mail/resend.d.ts.map +1 -1
  537. package/dist/mail/resend.js +11 -17
  538. package/dist/mail/resend.js.map +1 -1
  539. package/dist/mail/sender-resolver.d.ts +1 -7
  540. package/dist/mail/sender-resolver.d.ts.map +1 -1
  541. package/dist/mail/sender-resolver.js +11 -10
  542. package/dist/mail/sender-resolver.js.map +1 -1
  543. package/dist/mail/template-renderer.d.ts +1 -9
  544. package/dist/mail/template-renderer.d.ts.map +1 -1
  545. package/dist/mail/template-renderer.js +10 -15
  546. package/dist/mail/template-renderer.js.map +1 -1
  547. package/dist/mail/transactional-outbound.d.ts +115 -0
  548. package/dist/mail/transactional-outbound.d.ts.map +1 -0
  549. package/dist/mail/transactional-outbound.js +113 -0
  550. package/dist/mail/transactional-outbound.js.map +1 -0
  551. package/dist/mail/types.d.ts +2 -142
  552. package/dist/mail/types.d.ts.map +1 -1
  553. package/dist/mail/types.js +1 -33
  554. package/dist/mail/types.js.map +1 -1
  555. package/dist/portal/provisioning.d.ts +58 -0
  556. package/dist/portal/provisioning.d.ts.map +1 -0
  557. package/dist/portal/provisioning.js +139 -0
  558. package/dist/portal/provisioning.js.map +1 -0
  559. package/dist/pricing-engine/canonical.d.ts +14 -0
  560. package/dist/pricing-engine/canonical.d.ts.map +1 -0
  561. package/dist/pricing-engine/canonical.js +39 -0
  562. package/dist/pricing-engine/canonical.js.map +1 -0
  563. package/dist/pricing-engine/errors.d.ts.map +1 -1
  564. package/dist/pricing-engine/errors.js +6 -1
  565. package/dist/pricing-engine/errors.js.map +1 -1
  566. package/dist/pricing-engine/index.d.ts +12 -2
  567. package/dist/pricing-engine/index.d.ts.map +1 -1
  568. package/dist/pricing-engine/index.js +6 -1
  569. package/dist/pricing-engine/index.js.map +1 -1
  570. package/dist/pricing-engine/pipeline.d.ts.map +1 -1
  571. package/dist/pricing-engine/pipeline.js +202 -28
  572. package/dist/pricing-engine/pipeline.js.map +1 -1
  573. package/dist/pricing-engine/shadow-adapter.d.ts +37 -0
  574. package/dist/pricing-engine/shadow-adapter.d.ts.map +1 -0
  575. package/dist/pricing-engine/shadow-adapter.js +75 -0
  576. package/dist/pricing-engine/shadow-adapter.js.map +1 -0
  577. package/dist/pricing-engine/shadow-diff.d.ts +26 -0
  578. package/dist/pricing-engine/shadow-diff.d.ts.map +1 -0
  579. package/dist/pricing-engine/shadow-diff.js +52 -0
  580. package/dist/pricing-engine/shadow-diff.js.map +1 -0
  581. package/dist/pricing-engine/shadow-facade.d.ts +42 -0
  582. package/dist/pricing-engine/shadow-facade.d.ts.map +1 -0
  583. package/dist/pricing-engine/shadow-facade.js +82 -0
  584. package/dist/pricing-engine/shadow-facade.js.map +1 -0
  585. package/dist/pricing-engine/shadow-legacy-types.d.ts +64 -0
  586. package/dist/pricing-engine/shadow-legacy-types.d.ts.map +1 -0
  587. package/dist/pricing-engine/shadow-legacy-types.js +18 -0
  588. package/dist/pricing-engine/shadow-legacy-types.js.map +1 -0
  589. package/dist/pricing-engine/shadow-metrics.d.ts +33 -0
  590. package/dist/pricing-engine/shadow-metrics.d.ts.map +1 -0
  591. package/dist/pricing-engine/shadow-metrics.js +31 -0
  592. package/dist/pricing-engine/shadow-metrics.js.map +1 -0
  593. package/dist/pricing-engine/shadow-normalizer.d.ts +17 -0
  594. package/dist/pricing-engine/shadow-normalizer.d.ts.map +1 -0
  595. package/dist/pricing-engine/shadow-normalizer.js +39 -0
  596. package/dist/pricing-engine/shadow-normalizer.js.map +1 -0
  597. package/dist/pricing-engine/types.d.ts +2 -0
  598. package/dist/pricing-engine/types.d.ts.map +1 -1
  599. package/dist/rbac/authorize.d.ts +12 -0
  600. package/dist/rbac/authorize.d.ts.map +1 -1
  601. package/dist/rbac/authorize.js +27 -0
  602. package/dist/rbac/authorize.js.map +1 -1
  603. package/dist/rbac/catalog.d.ts.map +1 -1
  604. package/dist/rbac/catalog.js +67 -1
  605. package/dist/rbac/catalog.js.map +1 -1
  606. package/dist/rbac/workload-provisioning.d.ts +66 -0
  607. package/dist/rbac/workload-provisioning.d.ts.map +1 -0
  608. package/dist/rbac/workload-provisioning.js +153 -0
  609. package/dist/rbac/workload-provisioning.js.map +1 -0
  610. package/dist/rbac/workload.d.ts +124 -0
  611. package/dist/rbac/workload.d.ts.map +1 -0
  612. package/dist/rbac/workload.js +184 -0
  613. package/dist/rbac/workload.js.map +1 -0
  614. package/dist/storage/checksum.d.ts +9 -0
  615. package/dist/storage/checksum.d.ts.map +1 -0
  616. package/dist/storage/checksum.js +12 -0
  617. package/dist/storage/checksum.js.map +1 -0
  618. package/dist/storage/key.d.ts +37 -0
  619. package/dist/storage/key.d.ts.map +1 -0
  620. package/dist/storage/key.js +67 -0
  621. package/dist/storage/key.js.map +1 -0
  622. package/dist/storage/local.d.ts +40 -0
  623. package/dist/storage/local.d.ts.map +1 -0
  624. package/dist/storage/local.js +96 -0
  625. package/dist/storage/local.js.map +1 -0
  626. package/dist/storage/r2.d.ts +43 -0
  627. package/dist/storage/r2.d.ts.map +1 -0
  628. package/dist/storage/r2.js +177 -0
  629. package/dist/storage/r2.js.map +1 -0
  630. package/dist/storage/registry.d.ts +23 -0
  631. package/dist/storage/registry.d.ts.map +1 -0
  632. package/dist/storage/registry.js +37 -0
  633. package/dist/storage/registry.js.map +1 -0
  634. package/dist/storage/types.d.ts +67 -0
  635. package/dist/storage/types.d.ts.map +1 -0
  636. package/dist/storage/types.js +45 -0
  637. package/dist/storage/types.js.map +1 -0
  638. package/dist/subscription-billing/provider/asaas.d.ts +14 -0
  639. package/dist/subscription-billing/provider/asaas.d.ts.map +1 -1
  640. package/dist/subscription-billing/provider/asaas.js +32 -7
  641. package/dist/subscription-billing/provider/asaas.js.map +1 -1
  642. package/dist/subscription-billing/provider/disabled.d.ts +9 -0
  643. package/dist/subscription-billing/provider/disabled.d.ts.map +1 -1
  644. package/dist/subscription-billing/provider/disabled.js +8 -0
  645. package/dist/subscription-billing/provider/disabled.js.map +1 -1
  646. package/dist/subscription-billing/provider/registry.d.ts +10 -0
  647. package/dist/subscription-billing/provider/registry.d.ts.map +1 -1
  648. package/dist/subscription-billing/provider/registry.js +17 -0
  649. package/dist/subscription-billing/provider/registry.js.map +1 -1
  650. package/dist/subscription-billing/service.d.ts +17 -1
  651. package/dist/subscription-billing/service.d.ts.map +1 -1
  652. package/dist/subscription-billing/service.js +57 -8
  653. package/dist/subscription-billing/service.js.map +1 -1
  654. package/dist/subscription-billing/types.d.ts +13 -0
  655. package/dist/subscription-billing/types.d.ts.map +1 -1
  656. package/dist/subscription-billing/types.js.map +1 -1
  657. package/dist/tenancy/provisioning.d.ts.map +1 -1
  658. package/dist/tenancy/provisioning.js +32 -8
  659. package/dist/tenancy/provisioning.js.map +1 -1
  660. package/dist/users/repository.d.ts +38 -0
  661. package/dist/users/repository.d.ts.map +1 -1
  662. package/dist/users/repository.js +118 -3
  663. package/dist/users/repository.js.map +1 -1
  664. package/dist/vault/repository.d.ts +67 -0
  665. package/dist/vault/repository.d.ts.map +1 -1
  666. package/dist/vault/repository.js +78 -0
  667. package/dist/vault/repository.js.map +1 -1
  668. package/package.json +13 -4
  669. package/src/db/migrations/0023_user_invite_activation.sql +15 -0
  670. package/src/db/migrations/0024_set_user_password_function.sql +32 -0
  671. package/src/db/migrations/0025_revoke_user_session_function.sql +28 -0
  672. package/src/db/migrations/0026_documents_and_competence.sql +353 -0
  673. package/src/db/migrations/0027_receivable_client_isolation.sql +58 -0
  674. package/src/db/migrations/0028_collection_engine.sql +94 -0
  675. package/src/db/migrations/0029_payment_rail.sql +107 -0
  676. package/src/db/migrations/0030_provider_account_uniqueness.sql +22 -0
  677. package/src/db/migrations/0031_provider_account_environment_scope.sql +21 -0
  678. package/src/db/migrations/0032_settlement_reversal.sql +76 -0
  679. package/src/db/migrations/0033_receivable_idempotency.sql +28 -0
  680. package/src/db/migrations/0034_payment_customer_reconcile.sql +30 -0
  681. package/src/db/migrations/0035_payment_account_environments.sql +61 -0
  682. package/src/db/migrations/0036_primary_backfill_under_force_rls.sql +41 -0
  683. package/src/db/migrations/0037_receivable_generation_source.sql +29 -0
  684. package/src/db/migrations/0038_notification_channels.sql +175 -0
  685. package/src/db/migrations/0039_communication_dispatch_claim.sql +193 -0
  686. package/src/db/migrations/0040_communication_unknown_outcome.sql +99 -0
  687. package/src/db/migrations/0041_channel_binding_routing.sql +146 -0
  688. package/src/db/migrations/0042_channel_route_read_isolation.sql +165 -0
  689. package/src/db/migrations/0043_workload_identity.sql +105 -0
  690. package/src/db/migrations/0044_channel_onboarding_session.sql +96 -0
  691. package/dist/cjs/mail/resend-client.js +0 -103
  692. package/dist/cjs/mail/resend-client.js.map +0 -1
  693. package/dist/mail/resend-client.d.ts +0 -16
  694. package/dist/mail/resend-client.d.ts.map +0 -1
  695. package/dist/mail/resend-client.js +0 -100
  696. package/dist/mail/resend-client.js.map +0 -1
@@ -0,0 +1,76 @@
1
+ -- 0032 — Reversão de liquidação (dívida L da Onda 1B.4.2)
2
+ --
3
+ -- O gateway pode desfazer uma baixa que ele próprio registrou: o caso concreto é
4
+ -- o "recebimento em dinheiro" informado à mão no painel e depois cancelado. Até
5
+ -- aqui o sistema não tinha como representar isso. A cobrança continuava `paid`,
6
+ -- o `payment` continuava `confirmed`, o recebível continuava quitado — e a régua
7
+ -- de cobrança ficava desligada para sempre. Um dinheiro que nunca entrou saía do
8
+ -- sistema como recebido.
9
+ --
10
+ -- A distinção que este schema passa a suportar é financeira, não cosmética:
11
+ --
12
+ -- `refunded` — o dinheiro ENTROU e depois SAIU. Dois fatos, duas linhas na
13
+ -- história do caixa. A dívida foi quitada; o estorno é evento
14
+ -- novo, e não desfaz a quitação.
15
+ -- `reversed` — o dinheiro NUNCA ENTROU. O registro de liquidação estava
16
+ -- errado e foi anulado na origem. Não há fato novo de caixa: há
17
+ -- um fato anterior que deixou de valer.
18
+ --
19
+ -- Reaproveitar `refunded` para o segundo caso poria no extrato do escritório uma
20
+ -- devolução que nunca aconteceu.
21
+ --
22
+ -- NADA é apagado. A linha de `payment` permanece, com o valor, o método e a data
23
+ -- em que se acreditou ter recebido; ela apenas deixa de contar no saldo, e passa
24
+ -- a carregar quando e por que foi anulada. `charge.paid_at` também permanece: é
25
+ -- o registro de quando a cobrança esteve liquidada.
26
+
27
+ -- ── payment ────────────────────────────────────────────────────────────────
28
+ ALTER TABLE payment DROP CONSTRAINT IF EXISTS payment_status_check;
29
+ ALTER TABLE payment ADD CONSTRAINT payment_status_check
30
+ CHECK (status IN ('detected', 'confirmed', 'refunded', 'reversed'));
31
+
32
+ ALTER TABLE payment ADD COLUMN IF NOT EXISTS reversed_at timestamptz;
33
+ -- Motivo em texto curto e estável (`settlement_undone`, …), não prosa livre:
34
+ -- quem for auditar precisa agrupar, e não ler.
35
+ ALTER TABLE payment ADD COLUMN IF NOT EXISTS reversal_reason text;
36
+
37
+ -- Um pagamento revertido tem de dizer QUANDO. Sem isto a anulação vira um
38
+ -- status sem data, e a pergunta "desde quando este dinheiro deixou de contar?"
39
+ -- deixa de ter resposta.
40
+ ALTER TABLE payment DROP CONSTRAINT IF EXISTS payment_reversed_at_check;
41
+ ALTER TABLE payment ADD CONSTRAINT payment_reversed_at_check
42
+ CHECK ((status = 'reversed') = (reversed_at IS NOT NULL));
43
+
44
+ -- ── charge ─────────────────────────────────────────────────────────────────
45
+ ALTER TABLE charge DROP CONSTRAINT IF EXISTS charge_status_check;
46
+ ALTER TABLE charge ADD CONSTRAINT charge_status_check
47
+ CHECK (status IN ('pending', 'sent', 'paid', 'overdue', 'canceled', 'refunded', 'reversed'));
48
+
49
+ ALTER TABLE charge ADD COLUMN IF NOT EXISTS reversed_at timestamptz;
50
+
51
+ -- `reversed` NÃO entra na lista de cobranças mortas do índice único parcial de
52
+ -- `charge (tenant_id, receivable_id)`, e isso é deliberado: o boleto ou o PIX
53
+ -- continuam válidos no gateway e continuam pagáveis. A cobrança revertida segue
54
+ -- sendo A cobrança viva do recebível — emitir uma segunda para a mesma dívida é
55
+ -- que produziria duplicidade. Pela mesma razão, a régua de cobrança volta a
56
+ -- alcançá-la: `dunning` só ignora `paid`, `canceled` e `refunded`.
57
+
58
+ -- ── Unicidade do id de pagamento no provider ───────────────────────────────
59
+ --
60
+ -- O índice da 0029 impede que o MESMO pagamento do gateway vire duas baixas —
61
+ -- webhook duplicado é a regra, não a exceção. Ele continua valendo.
62
+ --
63
+ -- O que muda é o alcance: uma linha ANULADA deixa de ocupar o id. A cobrança
64
+ -- revertida continua pagável, e quando o cliente de fato paga, o gateway usa o
65
+ -- mesmo `pay_…`. Com o índice cobrindo linhas anuladas, essa baixa legítima
66
+ -- estouraria unicidade, a transação inteira do webhook falharia, e o gateway
67
+ -- reenviaria para sempre um pagamento que o sistema nunca conseguiria registrar.
68
+ -- Dinheiro real entrando e não sendo baixado — o defeito que esta onda existe
69
+ -- para eliminar, reaparecendo pela porta oposta.
70
+ --
71
+ -- Duas linhas CONFIRMADAS com o mesmo id continuam impossíveis, que é o que o
72
+ -- índice protege.
73
+ DROP INDEX IF EXISTS payment_provider_payment_uq;
74
+ CREATE UNIQUE INDEX payment_provider_payment_uq
75
+ ON payment (tenant_id, provider, provider_payment_id)
76
+ WHERE provider_payment_id IS NOT NULL AND status <> 'reversed';
@@ -0,0 +1,28 @@
1
+ -- 0033 — Idempotência do lançamento avulso
2
+ --
3
+ -- A emissão da COBRANÇA já era idempotente: o índice único parcial de
4
+ -- `charge (tenant_id, receivable_id)` garante uma cobrança viva por recebível.
5
+ -- O RECEBÍVEL não era. Um duplo clique no formulário, um retry de HTTP ou um
6
+ -- refresh depois do POST criavam dois recebíveis para o mesmo serviço — e, com
7
+ -- dois recebíveis, duas cobranças legítimas, cada uma com o seu PIX. O cliente
8
+ -- receberia dois pedidos de pagamento do mesmo trabalho.
9
+ --
10
+ -- O fluxo por competência nunca teve esse problema porque a idempotência dele é
11
+ -- semântica: o único `(tenant, fee_agreement, competence)` diz "esta dívida já
12
+ -- existe". No avulso não há chave semântica — dois honorários avulsos de mesmo
13
+ -- valor, mesma descrição e mesmo vencimento podem ser dois serviços de verdade,
14
+ -- e recusar o segundo seria um defeito pior.
15
+ --
16
+ -- Daí a chave vir de FORA: quem inicia a operação declara um identificador da
17
+ -- TENTATIVA. Reenviar a mesma tentativa devolve o mesmo recebível; um novo
18
+ -- lançamento tem chave nova e é criado normalmente. A distinção entre "de novo" e
19
+ -- "outra vez" passa a ser do chamador, que é quem realmente sabe.
20
+ ALTER TABLE receivable ADD COLUMN IF NOT EXISTS idempotency_key text;
21
+
22
+ -- Parcial: só as linhas que declararam chave participam. Sem o `WHERE`, todo
23
+ -- recebível gerado por competência (chave nula) colidiria com o primeiro —
24
+ -- porque em índice único comum `NULL` não colide, mas a intenção aqui é explícita
25
+ -- e vale registrá-la.
26
+ CREATE UNIQUE INDEX IF NOT EXISTS receivable_idempotency_uq
27
+ ON receivable (tenant_id, idempotency_key)
28
+ WHERE idempotency_key IS NOT NULL;
@@ -0,0 +1,30 @@
1
+ -- 0034 — Reconciliação cadastral do pagador no provedor (dívida R)
2
+ --
3
+ -- `payment_customer` guardava o id do pagador no gateway e nada mais. O efeito
4
+ -- prático era uma afirmação que o schema nunca fez, mas o código assumia:
5
+ -- *"este cliente já existe lá, logo os dados dele estão corretos para sempre"*.
6
+ --
7
+ -- Não estão. Um cliente cadastrado no Office SEM CPF/CNPJ produzia um pagador
8
+ -- sem documento no gateway. Corrigir o documento no Office não mudava nada lá —
9
+ -- o cache devolvia o id e o adapter sequer era chamado — e a emissão continuava
10
+ -- sendo recusada pelo provedor. O escritório corrigia no lugar certo e o sistema
11
+ -- seguia dizendo não.
12
+ --
13
+ -- A coluna nova responde outra pergunta: não "existe?", mas **"o que está lá
14
+ -- corresponde ao que temos aqui?"**.
15
+ ALTER TABLE payment_customer ADD COLUMN IF NOT EXISTS synced_fingerprint text;
16
+ ALTER TABLE payment_customer ADD COLUMN IF NOT EXISTS synced_at timestamptz;
17
+
18
+ -- É um DIGEST, nunca os dados.
19
+ --
20
+ -- Guardar nome, documento, e-mail e telefone aqui duplicaria dado pessoal que já
21
+ -- vive em `client`, com uma segunda cópia para manter em dia e vazar. O digest
22
+ -- responde exatamente a pergunta que interessa — "mudou desde a última
23
+ -- sincronização?" — e não responde nenhuma outra.
24
+ COMMENT ON COLUMN payment_customer.synced_fingerprint IS
25
+ 'sha256 dos campos cadastrais enviados ao provedor. Digest, nunca os valores.';
26
+
27
+ -- Linhas antigas ficam com `NULL` de propósito: nulo significa "nunca
28
+ -- reconciliado", e a primeira emissão de cada cliente existente vai reconciliar.
29
+ -- É assim que a dívida R se cura sozinha na base atual, sem backfill adivinhando
30
+ -- o que o gateway tem.
@@ -0,0 +1,61 @@
1
+ -- 0035 — Sandbox e produção deixam de ser excludentes (dívida U)
2
+ --
3
+ -- O único por `(tenant_id, provider)` dizia, na prática: *um escritório tem UMA
4
+ -- conta Asaas*. Ambiente não fazia parte da identidade lógica, então ativar
5
+ -- produção exigia **revogar o laboratório** — e a decisão "vou cobrar de
6
+ -- verdade" ficava amarrada a "vou perder onde ensaio".
7
+ --
8
+ -- Duas contas do mesmo provedor em ambientes diferentes não são a mesma conta.
9
+ -- Elas têm credencial distinta, endpoint distinto, dinheiro distinto — e a 0031
10
+ -- já reconhecia isso ao pôr `environment` na unicidade da referência externa. O
11
+ -- que faltava era reconhecer o mesmo no único por tenant.
12
+ ALTER TABLE payment_provider_account
13
+ ADD COLUMN IF NOT EXISTS is_primary boolean NOT NULL DEFAULT false;
14
+
15
+ -- ── 1. Uma conta por (tenant, provider, AMBIENTE) ──────────────────────────
16
+ DROP INDEX IF EXISTS payment_provider_account_provider_uq;
17
+ CREATE UNIQUE INDEX IF NOT EXISTS payment_provider_account_env_uq
18
+ ON payment_provider_account (tenant_id, provider, environment)
19
+ WHERE status <> 'revoked';
20
+
21
+ -- ── 2. Exatamente UMA conta operacional por (tenant, provider) ─────────────
22
+ --
23
+ -- Coexistir não pode virar ambiguidade. Sem esta trava, "qual conta emite?"
24
+ -- passaria a depender de ordem de criação ou de um `LIMIT 1` — que é
25
+ -- exatamente a escolha implícita que não se pode ter quando o assunto é
26
+ -- para onde vai o dinheiro.
27
+ --
28
+ -- A promoção desta marca é o ato de "entrar em produção": explícito, auditável
29
+ -- e reversível, em vez de um efeito colateral de revogar o sandbox.
30
+ CREATE UNIQUE INDEX IF NOT EXISTS payment_provider_account_primary_uq
31
+ ON payment_provider_account (tenant_id, provider)
32
+ WHERE is_primary AND status <> 'revoked';
33
+
34
+ -- ── 3. Classificação dos registros existentes ──────────────────────────────
35
+ --
36
+ -- Toda conta viva hoje É a conta operacional do seu escritório — era a única
37
+ -- que podia existir. Marcá-la mantém o comportamento atual EXATAMENTE como
38
+ -- está: quem já emitia pelo sandbox continua emitindo pelo sandbox.
39
+ --
40
+ -- Nada é reinterpretado: nenhum `sandbox` vira `live`, nenhum ambiente é
41
+ -- adivinhado. `environment` já existia preenchido e NOT NULL desde a 0015.
42
+ UPDATE payment_provider_account SET is_primary = true WHERE status <> 'revoked';
43
+
44
+ -- ── 4. Isolamento do identificador externo da cobrança ─────────────────────
45
+ --
46
+ -- `charge_external_uq` era `(tenant_id, provider, external_id)` — cego ao
47
+ -- ambiente. Com as duas contas coexistindo, um `pay_…` de sandbox e um de
48
+ -- produção que coincidissem colidiriam, e a emissão legítima seria recusada.
49
+ --
50
+ -- Passa a incluir a CONTA. Cobrança anterior ao Payment Rail não tem conta;
51
+ -- `COALESCE` para o uuid nulo preserva a proteção dessas linhas entre si, em
52
+ -- vez de deixá-las fora do índice como `NULL` faria.
53
+ DROP INDEX IF EXISTS charge_external_uq;
54
+ CREATE UNIQUE INDEX IF NOT EXISTS charge_external_uq
55
+ ON charge (tenant_id, provider,
56
+ COALESCE(provider_account_id, '00000000-0000-0000-0000-000000000000'::uuid),
57
+ external_id)
58
+ WHERE external_id IS NOT NULL AND deleted_at IS NULL;
59
+
60
+ COMMENT ON COLUMN payment_provider_account.is_primary IS
61
+ 'A conta que o escritório OPERA. Exatamente uma por (tenant, provider). Promover é o ato de entrar em produção.';
@@ -0,0 +1,41 @@
1
+ -- 0036 — refazer o backfill de `is_primary` que a 0035 não conseguiu aplicar.
2
+ --
3
+ -- `payment_provider_account` tem FORCE ROW LEVEL SECURITY: a política vale
4
+ -- inclusive para o DONO da tabela, que é quem roda as migrations. O UPDATE da
5
+ -- 0035 rodou sem `app.current_tenant` definida, a política não casou com linha
6
+ -- nenhuma e o comando afetou ZERO linhas — sem erro, sem aviso, com a migration
7
+ -- registrada como aplicada.
8
+ --
9
+ -- A consequência não é cosmética. `getActiveProviderAccount` passou a exigir
10
+ -- `is_primary`; com o backfill vazio, um escritório que já tinha conta conectada
11
+ -- ficava sem conta operacional e simplesmente não conseguia emitir — um sistema
12
+ -- financeiro mudo por causa de um UPDATE que "deu certo".
13
+ --
14
+ -- A 0035 é imutável (o migrador confere checksum), então a correção vem adiante.
15
+ -- `NO FORCE` só vale dentro desta transação; se qualquer passo falhar, o ALTER
16
+ -- volta atrás junto com o resto.
17
+
18
+ ALTER TABLE payment_provider_account NO FORCE ROW LEVEL SECURITY;
19
+
20
+ -- Uma primária por (tenant, provider). `created_at` decide: a conta mais antiga
21
+ -- é a que o escritório vinha usando, e promover a mais nova trocaria silen-
22
+ -- ciosamente para onde o dinheiro vai — exatamente o que esta migration não
23
+ -- pode fazer. Tenant que já tem primária não é tocado.
24
+ WITH escolhida AS (
25
+ SELECT DISTINCT ON (tenant_id, provider) id
26
+ FROM payment_provider_account
27
+ WHERE status <> 'revoked'
28
+ AND NOT EXISTS (
29
+ SELECT 1 FROM payment_provider_account outra
30
+ WHERE outra.tenant_id = payment_provider_account.tenant_id
31
+ AND outra.provider = payment_provider_account.provider
32
+ AND outra.is_primary
33
+ AND outra.status <> 'revoked')
34
+ ORDER BY tenant_id, provider, created_at
35
+ )
36
+ UPDATE payment_provider_account
37
+ SET is_primary = true, updated_at = now()
38
+ FROM escolhida
39
+ WHERE payment_provider_account.id = escolhida.id;
40
+
41
+ ALTER TABLE payment_provider_account FORCE ROW LEVEL SECURITY;
@@ -0,0 +1,29 @@
1
+ -- 0037 — Quem criou o recebível: a pessoa ou o relógio
2
+ --
3
+ -- A geração por competência passa a acontecer sozinha, todo dia, para todo
4
+ -- honorário ativo (AUTO_RECURRING_RECEIVABLE_GENERATION). O motor é o MESMO da
5
+ -- ação "Gerar competência manualmente" — deliberadamente, para que não existam
6
+ -- duas regras discordando sobre quanto o cliente deve. O que muda é só quem
7
+ -- puxou o gatilho.
8
+ --
9
+ -- E é essa distinção que precisa sobreviver na linha. Sem ela, "por que este
10
+ -- cliente tem recebível de agosto se ninguém gerou agosto?" não tem resposta
11
+ -- consultável: `created_by` fica nulo tanto no automático quanto num script, e
12
+ -- a trilha de auditoria responde por LOTE, não por recebível. Quando alguém
13
+ -- contestar uma dívida — e alguém vai —, a pergunta será sobre UMA linha.
14
+ --
15
+ -- `generated_by` NÃO é natureza financeira. Um recebível automático e um manual
16
+ -- da mesma competência são a mesma dívida, com o mesmo valor, o mesmo
17
+ -- vencimento e a mesma régua. A coluna é procedência, não classificação: nada
18
+ -- no domínio pode decidir cobrar diferente por causa dela.
19
+ ALTER TABLE receivable
20
+ ADD COLUMN IF NOT EXISTS generated_by text NOT NULL DEFAULT 'manual'
21
+ CHECK (generated_by IN ('manual', 'automatic'));
22
+
23
+ -- Default 'manual' e não 'unknown': tudo que existe hoje foi criado por uma
24
+ -- pessoa apertando um botão ou por um script que uma pessoa rodou. Marcar o
25
+ -- acervo como desconhecido descreveria pior a realidade do que descrevê-lo como
26
+ -- manual, e obrigaria toda leitura futura a tratar um terceiro caso que só
27
+ -- existiria por causa desta migração.
28
+ COMMENT ON COLUMN receivable.generated_by IS
29
+ 'Procedência: manual (operador/rota) ou automatic (scheduler). Nunca altera a natureza financeira.';
@@ -0,0 +1,175 @@
1
+ -- 0038 — Canais de cobrança além do e-mail: WhatsApp e SMS
2
+ --
3
+ -- O motor de cobrança (0028) já era agnóstico de canal: a política declara o
4
+ -- canal por etapa, o adapter entrega, o desfecho volta normalizado. O que não
5
+ -- existia era o que um canal de TELEFONE exige e o e-mail não: um número em
6
+ -- formato canônico, permissão para usá-lo, e correlação com o identificador que
7
+ -- o provider devolve.
8
+ --
9
+ -- Esta migração acrescenta só isso. Nenhuma regra financeira muda.
10
+
11
+ -- ── Telefone canônico do cliente ────────────────────────────────────────────
12
+ --
13
+ -- `client.phone` continua sendo o que o operador digitou — "(62) 99999-8888",
14
+ -- "62 9 9999 8888", "+55 62 99999-8888". Deliberadamente: é o que ele reconhece
15
+ -- na tela, e reescrever o campo do usuário para caber num provider é o tipo de
16
+ -- "melhoria" que faz alguém perder o ramal anotado no fim da linha.
17
+ --
18
+ -- O que os providers exigem é E.164, e essa é uma DERIVAÇÃO. Guardá-la separada
19
+ -- permite responder três perguntas que hoje não têm resposta: este cliente tem
20
+ -- número utilizável? é celular (WhatsApp/SMS) ou fixo? e o que exatamente foi
21
+ -- enviado ao provider, seis meses depois, quando o cadastro já mudou.
22
+ ALTER TABLE client ADD COLUMN IF NOT EXISTS phone_e164 text;
23
+
24
+ -- Parcial e não único: dois clientes do mesmo escritório podem legitimamente
25
+ -- compartilhar um telefone (marido e mulher, matriz e filial, o contador da
26
+ -- empresa). Unicidade aqui recusaria cadastro válido.
27
+ CREATE INDEX IF NOT EXISTS client_phone_e164_idx
28
+ ON client (tenant_id, phone_e164)
29
+ WHERE phone_e164 IS NOT NULL AND deleted_at IS NULL;
30
+
31
+ -- ── Permissão de canal ──────────────────────────────────────────────────────
32
+ --
33
+ -- Uma linha por (cliente, canal). A AUSÊNCIA de linha não é "pode": é
34
+ -- "ninguém decidiu ainda", e quem decide o que fazer com a ausência é a
35
+ -- política do tenant, não esta tabela.
36
+ --
37
+ -- Por que uma tabela e não uma coluna booleana em `client`: opt-out precisa
38
+ -- registrar QUANDO e POR QUEM. "Este cliente pediu para não receber WhatsApp"
39
+ -- vira uma discussão desagradável no dia em que ele recebe assim mesmo, e uma
40
+ -- coluna `whatsapp_ok = false` não tem como responder quando ele pediu, se
41
+ -- pediu ou se alguém desmarcou por engano.
42
+ CREATE TABLE IF NOT EXISTS client_channel_consent (
43
+ id uuid PRIMARY KEY,
44
+ tenant_id uuid NOT NULL REFERENCES tenant(id),
45
+ client_id uuid NOT NULL,
46
+ channel text NOT NULL CHECK (channel IN ('email', 'whatsapp', 'sms')),
47
+ -- `granted` = pode enviar (consentimento registrado ou relação contratual)
48
+ -- `denied` = opt-out. NUNCA enviar, por nenhuma via — nem manual.
49
+ -- `unknown` = registro existe mas ninguém decidiu; a política resolve.
50
+ state text NOT NULL DEFAULT 'unknown'
51
+ CHECK (state IN ('granted', 'denied', 'unknown')),
52
+ -- Como a decisão chegou. `customer_reply` é o opt-out que o próprio cliente
53
+ -- pediu (responder SAIR no WhatsApp, por exemplo) e é o mais forte de todos:
54
+ -- ninguém no escritório deveria poder reverter isso por engano na tela.
55
+ source text NOT NULL DEFAULT 'staff'
56
+ CHECK (source IN ('staff', 'customer_reply', 'import', 'contract', 'provider')),
57
+ -- Texto curto do operador. Nunca conteúdo de mensagem, nunca PII extra.
58
+ note text,
59
+ decided_at timestamptz NOT NULL DEFAULT now(),
60
+ decided_by uuid REFERENCES "user"(id),
61
+ created_at timestamptz NOT NULL DEFAULT now(),
62
+ updated_at timestamptz NOT NULL DEFAULT now(),
63
+ CONSTRAINT client_channel_consent_tenant_id_uq UNIQUE (tenant_id, id),
64
+ FOREIGN KEY (tenant_id, client_id) REFERENCES client (tenant_id, id)
65
+ );
66
+
67
+ -- Uma decisão viva por (cliente, canal). Sem isto, dois opt-outs concorrentes
68
+ -- criariam duas linhas e a leitura teria de escolher uma — e escolher errado
69
+ -- significa enviar para quem pediu para não receber.
70
+ CREATE UNIQUE INDEX IF NOT EXISTS client_channel_consent_uq
71
+ ON client_channel_consent (tenant_id, client_id, channel);
72
+
73
+ CREATE INDEX IF NOT EXISTS client_channel_consent_denied_idx
74
+ ON client_channel_consent (tenant_id, channel)
75
+ WHERE state = 'denied';
76
+
77
+ -- ── RLS: tenant E cliente ───────────────────────────────────────────────────
78
+ --
79
+ -- Mesmo padrão das tabelas client-scoped (0026/0027). Sem isto a tabela seria a
80
+ -- única com `tenant_id` do schema sem política — e não falharia em teste
81
+ -- nenhum, porque não existe guard que varra cobertura de RLS. Passaria verde,
82
+ -- e a permissão de contato de um escritório ficaria legível por outro.
83
+ --
84
+ -- FORCE porque a política precisa valer inclusive para o dono da tabela: é o
85
+ -- papel que roda migration, e é exatamente onde a armadilha da 0035/0036 mora.
86
+ ALTER TABLE client_channel_consent ENABLE ROW LEVEL SECURITY;
87
+ ALTER TABLE client_channel_consent FORCE ROW LEVEL SECURITY;
88
+
89
+ CREATE POLICY client_channel_consent_tenant_client_isolation ON client_channel_consent
90
+ USING (
91
+ tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid
92
+ AND (
93
+ NULLIF(current_setting('app.current_client', true), '') IS NULL
94
+ OR client_id = NULLIF(current_setting('app.current_client', true), '')::uuid
95
+ )
96
+ )
97
+ WITH CHECK (
98
+ tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid
99
+ AND (
100
+ NULLIF(current_setting('app.current_client', true), '') IS NULL
101
+ OR client_id = NULLIF(current_setting('app.current_client', true), '')::uuid
102
+ )
103
+ );
104
+
105
+ -- ── Correlação com o provider ───────────────────────────────────────────────
106
+ --
107
+ -- CORREÇÃO DE DEFEITO. `markSent` gravava o identificador do provider em
108
+ -- `template`, com `COALESCE(template, provider_ref)` — ou seja, quando o
109
+ -- template já estava preenchido (sempre, pois `scheduleAction` o grava), o id
110
+ -- do provider era descartado em silêncio; e se estivesse nulo, o id ocuparia o
111
+ -- lugar do nome do template e corromperia o relatório da régua.
112
+ --
113
+ -- Nunca deu problema porque o adapter de e-mail não devolve identificador.
114
+ -- WhatsApp e SMS devolvem sempre, e o webhook de entrega chega trazendo APENAS
115
+ -- esse id — sem uma coluna própria e indexada, não há como achar a ação que ele
116
+ -- descreve, e todo status de entrega se perderia.
117
+ ALTER TABLE dunning_event ADD COLUMN IF NOT EXISTS provider_ref text;
118
+
119
+ -- O webhook chega com o id do provider e nada mais. Esta é a via de acesso.
120
+ CREATE INDEX IF NOT EXISTS dunning_event_provider_ref_idx
121
+ ON dunning_event (tenant_id, provider_ref)
122
+ WHERE provider_ref IS NOT NULL;
123
+
124
+ -- ── Estados de entrega ──────────────────────────────────────────────────────
125
+ --
126
+ -- `sent` significa "o provider aceitou", que é o único desfecho que o e-mail
127
+ -- consegue afirmar. WhatsApp e SMS informam depois, por webhook, se a mensagem
128
+ -- chegou ao aparelho (`delivered`) e — só o WhatsApp — se foi aberta (`read`).
129
+ --
130
+ -- São estados NOVOS, não uma renomeação: `sent` continua querendo dizer
131
+ -- exatamente o que dizia, e nenhuma linha existente muda de valor. Colapsar
132
+ -- "aceito pelo provider" e "chegou ao cliente" faria a tela afirmar entrega que
133
+ -- ninguém confirmou — que é precisamente a pergunta que o escritório faz quando
134
+ -- o cliente diz "não recebi nada".
135
+ --
136
+ -- Não há `read` para SMS, e não se inventa: o canal simplesmente nunca alcança
137
+ -- esse estado. Ver a nota sobre equivalência falsa no adapter.
138
+ ALTER TABLE dunning_event DROP CONSTRAINT IF EXISTS dunning_event_status_check;
139
+ ALTER TABLE dunning_event ADD CONSTRAINT dunning_event_status_check
140
+ CHECK (status IN ('scheduled', 'sent', 'delivered', 'read', 'failed', 'canceled', 'escalated'));
141
+
142
+ -- Quando o provider confirmou a entrega no aparelho. Separado de `sent_at`
143
+ -- porque a distância entre os dois é a informação útil: aceito às 9h e entregue
144
+ -- às 14h significa aparelho desligado, não falha.
145
+ ALTER TABLE dunning_event ADD COLUMN IF NOT EXISTS delivered_at timestamptz;
146
+ ALTER TABLE dunning_event ADD COLUMN IF NOT EXISTS read_at timestamptz;
147
+
148
+ COMMENT ON COLUMN dunning_event.provider_ref IS
149
+ 'Identificador da mensagem no provider. Nunca uma credencial. Via de acesso do webhook de entrega.';
150
+ COMMENT ON TABLE client_channel_consent IS
151
+ 'Permissão de contato por canal. Ausência de linha = indefinido; a política do tenant decide.';
152
+
153
+ -- ── Idempotência com contexto ───────────────────────────────────────────────
154
+ --
155
+ -- A chave de `(receivable, step_key)` protege o AGENDAMENTO: o índice único de
156
+ -- `dunning_event` já impede duas ações para a mesma etapa do mesmo recebível.
157
+ -- O que ela não protege é o ENVIO feito pelo motor compartilhado, cuja chave
158
+ -- carrega tenant, produto, marca, canal, evento e versão de template — porque
159
+ -- lá o mesmo aviso pode sair legitimamente por dois canais, e um template
160
+ -- corrigido é um envio novo.
161
+ --
162
+ -- Sem esta coluna, "já enviei isto?" só teria como ser respondido por
163
+ -- aproximação, e aproximação numa régua de cobrança erra para os dois lados:
164
+ -- cala um aviso devido, ou repete um que já saiu.
165
+ ALTER TABLE dunning_event ADD COLUMN IF NOT EXISTS idempotency_key text;
166
+
167
+ -- Único por tenant. Parcial porque as ações do caminho legado (e todas as
168
+ -- criadas antes desta migração) não têm chave — e NULL não deve colidir com
169
+ -- NULL.
170
+ CREATE UNIQUE INDEX IF NOT EXISTS dunning_event_idempotency_key_uq
171
+ ON dunning_event (tenant_id, idempotency_key)
172
+ WHERE idempotency_key IS NOT NULL;
173
+
174
+ COMMENT ON COLUMN dunning_event.idempotency_key IS
175
+ 'Chave do motor compartilhado (sha256 de tenant/produto/marca/canal/evento/assunto/versão). NULL no caminho legado.';
@@ -0,0 +1,193 @@
1
+ -- 0039 — Reserva atômica de envio e trilha do motor de comunicação
2
+ --
3
+ -- CORREÇÃO DE DEFEITO ESTRUTURAL. A 0038 entregou a chave de idempotência
4
+ -- (`dunning_event.idempotency_key`) e o motor a consultava assim:
5
+ --
6
+ -- alreadySent(key) → adapter.send() → markSent(key)
7
+ --
8
+ -- Três passos. Entre o primeiro e o terceiro há uma janela em que dois workers
9
+ -- concorrentes — cron e envio manual, duas instâncias durante um redeploy, um
10
+ -- retry sobreposto ao original — recebem os DOIS a resposta "ainda não enviei",
11
+ -- e os dois chamam o provider. O cliente recebe a mesma cobrança duas vezes, e
12
+ -- a coluna de idempotência registra fielmente que houve um envio.
13
+ --
14
+ -- O índice único da 0038 não protege isso: ele impede duas LINHAS com a mesma
15
+ -- chave, e o envio duplo acontece antes de qualquer linha ser escrita. Um índice
16
+ -- é uma restrição sobre o registro; o que faltava é uma restrição sobre o
17
+ -- DIREITO DE AGIR.
18
+ --
19
+ -- Estas duas tabelas são isso. Nenhuma regra financeira muda; `dunning_event`
20
+ -- continua sendo o estado da etapa da régua.
21
+
22
+ -- ── Reserva de envio ────────────────────────────────────────────────────────
23
+ --
24
+ -- Uma linha por chave de idempotência. A linha É a reserva: quem consegue
25
+ -- gravá-la (ou retomá-la de um titular vencido) tem o direito de chamar o
26
+ -- provider, e mais ninguém.
27
+ --
28
+ -- Tabela do MOTOR, não da cobrança. Um segundo consumidor (Dental, Manga Verde)
29
+ -- reserva aqui sem nenhuma coluna nova — `product` e `brand` já estão na chave,
30
+ -- e é por isso que ela não mora dentro de `dunning_event`: amarrar a reserva à
31
+ -- tabela de uma régua de honorários obrigaria o próximo produto a ter uma régua
32
+ -- de honorários.
33
+ CREATE TABLE IF NOT EXISTS communication_dispatch (
34
+ id uuid PRIMARY KEY,
35
+ tenant_id uuid NOT NULL REFERENCES tenant(id),
36
+ product text NOT NULL,
37
+ brand text NOT NULL,
38
+ channel text NOT NULL CHECK (channel IN ('email', 'whatsapp', 'sms', 'notification')),
39
+ event_type text NOT NULL,
40
+ -- Referência do assunto no domínio do consumidor (recebível, fatura, pedido).
41
+ -- Nunca PII.
42
+ subject_ref text NOT NULL,
43
+ -- sha256 composto pelo motor a partir de tenant/produto/marca/canal/evento/
44
+ -- assunto/versão de template. Ver `communication/idempotency.ts`.
45
+ idempotency_key text NOT NULL,
46
+
47
+ -- in_flight alguém tem a reserva AGORA e pode estar chamando o provider
48
+ -- sent o provider aceitou. Terminal para esta chave.
49
+ -- failed_retryable falhou de um jeito que vale tentar de novo; a chave volta a ser reservável
50
+ -- failed_permanent falhou de um jeito que não vale; só ação humana muda isto
51
+ --
52
+ -- `sent` NÃO é "entregue": quem afirma entrega é o webhook, e o estado de
53
+ -- entrega mora em `communication_event`/`dunning_event`. Colapsar os dois
54
+ -- faria a reserva virar uma segunda fonte de verdade sobre entrega, que
55
+ -- divergiria da primeira no primeiro callback fora de ordem.
56
+ status text NOT NULL DEFAULT 'in_flight'
57
+ CHECK (status IN ('in_flight', 'sent', 'failed_retryable', 'failed_permanent')),
58
+
59
+ -- Identidade do TITULAR atual da reserva. Sem isto, um processo que ressuscita
60
+ -- depois do prazo vencido assentaria o desfecho por cima de quem está
61
+ -- enviando agora — e marcaria como enviada uma mensagem que o titular novo
62
+ -- ainda não mandou.
63
+ claim_id uuid,
64
+ claimed_at timestamptz,
65
+ -- Prazo. Existe porque processo morre: sem ele, um worker que cai segurando a
66
+ -- reserva calaria a etapa para sempre, e o silêncio de uma régua de cobrança
67
+ -- é indistinguível de "o cliente não devia nada".
68
+ claim_expires_at timestamptz,
69
+ -- A reserva atual foi TOMADA de um titular vencido. Significa risco real de
70
+ -- duplicata (o anterior pode ter enviado e morrido antes de assentar), e é o
71
+ -- que permite a um humano explicar "por que chegou duas vezes".
72
+ recovered_from_expired boolean NOT NULL DEFAULT false,
73
+
74
+ attempts integer NOT NULL DEFAULT 0,
75
+ provider_ref text,
76
+ -- Rótulo curto e estável para métrica. snake_case. Nunca PII, nunca segredo.
77
+ last_code text,
78
+ last_detail text,
79
+ sent_at timestamptz,
80
+ settled_at timestamptz,
81
+ created_at timestamptz NOT NULL DEFAULT now(),
82
+ updated_at timestamptz NOT NULL DEFAULT now(),
83
+
84
+ CONSTRAINT communication_dispatch_tenant_id_uq UNIQUE (tenant_id, id)
85
+ );
86
+
87
+ -- A restrição que faz a reserva existir. Sem ela, `ON CONFLICT` não tem alvo e
88
+ -- todo `claim` viraria um INSERT novo — ou seja, todo mundo ganharia a reserva.
89
+ CREATE UNIQUE INDEX IF NOT EXISTS communication_dispatch_key_uq
90
+ ON communication_dispatch (tenant_id, idempotency_key);
91
+
92
+ -- Via de acesso do webhook de entrega: ele chega com o id do provider e nada mais.
93
+ CREATE INDEX IF NOT EXISTS communication_dispatch_provider_ref_idx
94
+ ON communication_dispatch (tenant_id, provider_ref)
95
+ WHERE provider_ref IS NOT NULL;
96
+
97
+ -- Reservas penduradas, para diagnóstico operacional.
98
+ CREATE INDEX IF NOT EXISTS communication_dispatch_in_flight_idx
99
+ ON communication_dispatch (tenant_id, claim_expires_at)
100
+ WHERE status = 'in_flight';
101
+
102
+ CREATE INDEX IF NOT EXISTS communication_dispatch_subject_idx
103
+ ON communication_dispatch (tenant_id, subject_ref, event_type);
104
+
105
+ -- FORCE porque a política precisa valer inclusive para o dono da tabela — é o
106
+ -- papel que roda migration, e é onde a armadilha da 0035/0036 morava.
107
+ ALTER TABLE communication_dispatch ENABLE ROW LEVEL SECURITY;
108
+ ALTER TABLE communication_dispatch FORCE ROW LEVEL SECURITY;
109
+
110
+ CREATE POLICY communication_dispatch_tenant_isolation ON communication_dispatch
111
+ USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
112
+ WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
113
+
114
+ -- ── Trilha do motor ─────────────────────────────────────────────────────────
115
+ --
116
+ -- Registro estruturado do que o motor decidiu. NÃO é log: log é texto que
117
+ -- alguém talvez leia, isto responde "por que este cliente não recebeu?" seis
118
+ -- meses depois, com filtro por motivo.
119
+ --
120
+ -- Separada de `audit_log` de propósito. `audit_log` registra ATOS DE USUÁRIO —
121
+ -- tem ator, IP, user-agent, e é lida por auditoria de acesso. A maioria dos
122
+ -- eventos daqui não tem ator humano nenhum (cron, webhook), tem volume de outra
123
+ -- ordem de grandeza, e é consultada por chave de idempotência e referência do
124
+ -- provider. Misturá-las tornaria a auditoria de acesso ilegível e esta consulta
125
+ -- lenta.
126
+ --
127
+ -- Também separada de `dunning_event`: lá mora o estado da ETAPA da régua, um por
128
+ -- (recebível, passo). Aqui mora o histórico da INTENÇÃO — várias por etapa,
129
+ -- porque uma etapa pode ter um canal recusado por consentimento, outro por
130
+ -- telefone inválido, e um terceiro entregue.
131
+ CREATE TABLE IF NOT EXISTS communication_event (
132
+ id uuid PRIMARY KEY,
133
+ tenant_id uuid NOT NULL REFERENCES tenant(id),
134
+ product text NOT NULL,
135
+ -- NULLABLE, e a exceção é real: um callback que chega com um identificador de
136
+ -- provider que não corresponde a nenhuma mensagem nossa não pertence a marca
137
+ -- nenhuma. Forçar um valor ali inventaria uma marca — e a trilha passaria a
138
+ -- afirmar que o escritório X mandou algo que ele não mandou.
139
+ brand text,
140
+ -- Vocabulário FECHADO. Um `text` livre viraria dez grafias do mesmo evento e
141
+ -- nenhuma métrica agregável.
142
+ kind text NOT NULL CHECK (kind IN (
143
+ 'intent_created', 'dispatch_claimed', 'dispatch_skipped',
144
+ 'dispatch_sent', 'dispatch_failed', 'consent_blocked',
145
+ 'invalid_contact', 'callback_delivered', 'callback_read',
146
+ 'callback_failed', 'opt_out')),
147
+ channel text CHECK (channel IS NULL OR channel IN ('email', 'whatsapp', 'sms', 'notification')),
148
+ event_type text NOT NULL,
149
+ subject_ref text,
150
+ -- Referência LÓGICA do destinatário (id do cliente). NUNCA telefone, NUNCA
151
+ -- e-mail: a trilha é consultada por quem opera, e uma trilha que carrega o
152
+ -- contato transforma toda consulta operacional numa exportação de PII.
153
+ recipient_ref text,
154
+ idempotency_key text,
155
+ provider_ref text,
156
+ skip_reason text,
157
+ status text,
158
+ code text,
159
+ -- Truncado na escrita. Nunca corpo de mensagem, nunca credencial.
160
+ detail text,
161
+ attempt integer,
162
+ occurred_at timestamptz NOT NULL DEFAULT now(),
163
+ created_at timestamptz NOT NULL DEFAULT now(),
164
+
165
+ CONSTRAINT communication_event_tenant_id_uq UNIQUE (tenant_id, id)
166
+ );
167
+
168
+ CREATE INDEX IF NOT EXISTS communication_event_key_idx
169
+ ON communication_event (tenant_id, idempotency_key, occurred_at DESC)
170
+ WHERE idempotency_key IS NOT NULL;
171
+
172
+ CREATE INDEX IF NOT EXISTS communication_event_subject_idx
173
+ ON communication_event (tenant_id, subject_ref, occurred_at DESC);
174
+
175
+ CREATE INDEX IF NOT EXISTS communication_event_recipient_idx
176
+ ON communication_event (tenant_id, recipient_ref, occurred_at DESC);
177
+
178
+ CREATE INDEX IF NOT EXISTS communication_event_kind_idx
179
+ ON communication_event (tenant_id, kind, occurred_at DESC);
180
+
181
+ ALTER TABLE communication_event ENABLE ROW LEVEL SECURITY;
182
+ ALTER TABLE communication_event FORCE ROW LEVEL SECURITY;
183
+
184
+ CREATE POLICY communication_event_tenant_isolation ON communication_event
185
+ USING (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid)
186
+ WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant', true), '')::uuid);
187
+
188
+ COMMENT ON TABLE communication_dispatch IS
189
+ 'Reserva atômica do direito de enviar, por chave de idempotência. A linha É a reserva.';
190
+ COMMENT ON TABLE communication_event IS
191
+ 'Trilha estruturada do motor de comunicação. Sem PII de contato, sem credencial, sem corpo de mensagem.';
192
+ COMMENT ON COLUMN communication_dispatch.recovered_from_expired IS
193
+ 'A reserva foi tomada de um titular vencido — há risco real de o anterior ter enviado antes de morrer.';