From d088a89fb7564fbdc446d8f51db2caab8927893c Mon Sep 17 00:00:00 2001 From: Leonardo Date: Mon, 30 Mar 2026 14:08:19 -0300 Subject: [PATCH] =?UTF-8?q?Documentos=20Pacientes,=20Template=20Documentos?= =?UTF-8?q?=20Pacientes=20Saas,=20Documentos=20prontu=C3=A1rios,=20Documen?= =?UTF-8?q?tos=20Externos,=20Visualiza=C3=A7=C3=A3o=20Externa,=20Permiss?= =?UTF-8?q?=C3=A3o=20de=20Visualiza=C3=A7=C3=A3o,=20Render=20Otimiza=C3=A7?= =?UTF-8?q?=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 6 +- Atestado_Psicológico_1774873197838.pdf | Bin 0 -> 79162 bytes Atestado_Psicológico_1774873520538.pdf | Bin 0 -> 79162 bytes database-novo/README-GENERATE-DASHBOARD.md | 96 + database-novo/agenciapsi-db-dashboard.html | 489 + database-novo/backups/2026-03-27/data.sql | 1651 ++ .../backups/2026-03-27/full_dump.sql | 21202 +++++++++++++++ database-novo/backups/2026-03-27/schema.sql | 19202 +++++++++++++ database-novo/backups/2026-03-29/data.sql | 1799 ++ .../backups/2026-03-29/full_dump.sql | 22428 ++++++++++++++++ database-novo/backups/2026-03-29/schema.sql | 20279 ++++++++++++++ database-novo/generate-dashboard.cjs | 457 + .../migrations/002_setup_wizard1_fields.sql | 57 + .../migrations/003_tenants_address_fields.sql | 33 + .../20260328000001_create_medicos.sql | 147 + .../20260328000002_patients_new_columns.sql | 119 + ...000003_patients_drop_check_constraints.sql | 70 + ...000004_create_patient_support_contacts.sql | 56 + ...20260329000001_create_documents_tables.sql | 454 + ...260329000002_create_document_templates.sql | 260 + .../20260329000003_create_storage_buckets.sql | 93 + .../migrations/migration_patients.sql | 661 + database-novo/seeds/seed_011_features.sql | 11 +- .../seeds/seed_012_plan_features.sql | 32 +- .../seeds/seed_015_document_templates.sql | 230 + .../documentos_arquivos_top_de_linha.html | 766 + .../plano_implementacao_documentos.html | 870 + .../cadastro_pacientes_levantamento.html | 372 + .../pacientes_status_implementacao.html | 964 + index.html | 20 +- mvp-assessment.html | 445 + package-lock.json | 793 +- package.json | 5 + src/App.vue | 36 +- src/assets/styles.scss | 20 + src/components/CadastroRapidoConvenio.vue | 351 + src/components/CadastroRapidoMedico.vue | 646 + src/components/ComponentCadastroRapido.vue | 13 +- src/components/landing/FooterWidget.vue | 2 +- src/components/landing/TopbarWidget.vue | 2 +- src/components/ui/JoditEmailEditor.vue | 203 + src/components/ui/PatientCreatePopover.vue | 10 +- src/composables/useFormValidation.js | 183 + .../documents/DocumentTemplatesPage.vue | 297 + src/features/documents/DocumentsListPage.vue | 377 + .../documents/components/DocumentCard.vue | 159 + .../components/DocumentGenerateDialog.vue | 266 + .../components/DocumentPreviewDialog.vue | 174 + .../components/DocumentShareDialog.vue | 245 + .../components/DocumentSignatureDialog.vue | 306 + .../components/DocumentTagsInput.vue | 123 + .../components/DocumentTemplateEditor.vue | 207 + .../components/DocumentUploadDialog.vue | 279 + .../composables/useDocumentGenerate.js | 197 + .../composables/useDocumentTemplates.js | 213 + .../documents/composables/useDocuments.js | 231 + src/features/patients/PatientsListPage.vue | 2 +- .../cadastro/PatientsCadastroPage - Bkp.vue | 965 + .../PatientsCadastroPage--preview.vue | 679 + .../cadastro/PatientsCadastroPage.vue | 2236 +- src/features/patients/medicos/MedicosPage.vue | 971 + .../PatientProntuario - design1.vue | 1101 + .../PatientProntuario - design2.vue | 1101 + .../PatientProntuario - design3.vue | 667 + .../patients/prontuario/PatientProntuario.vue | 1646 +- .../setup/SetupWizardPage - Copia.vue | 1807 ++ src/features/setup/SetupWizardPage.vue | 5280 ++-- src/layout/AppConfigurator.vue | 14 + src/layout/AppFooter.vue | 2 +- src/layout/AppLayout.vue | 2 +- src/layout/AppMenuFooterPanel.vue | 17 +- src/layout/AppMenuItem.vue | 1 + src/layout/AppRail.vue | 22 +- src/layout/AppRailPanel.vue | 4 +- src/layout/AppThemeBar.vue | 14 + src/layout/AppTopbar.vue | 13 +- src/layout/composables/layout.js | 19 +- .../configuracoes/ConfiguracoesAgendaPage.vue | 10 +- .../ConfiguracoesEmailTemplatesPage.vue | 367 +- .../ConfiguracoesWhatsappPage.vue | 207 +- src/main.js | 115 +- src/navigation/menus/clinic.menu.js | 6 +- src/navigation/menus/saas.menu.js | 3 +- src/navigation/menus/therapist.menu.js | 4 + src/router/guards.js | 40 +- src/router/routes.account.js | 12 +- src/router/routes.clinic.js | 16 + src/router/routes.public.js | 7 + src/router/routes.saas.js | 6 + src/router/routes.therapist.js | 28 + src/services/DocumentAuditLog.service.js | 144 + src/services/DocumentGenerate.service.js | 386 + src/services/DocumentShareLinks.service.js | 166 + src/services/DocumentSignatures.service.js | 172 + src/services/DocumentTemplates.service.js | 247 + src/services/Documents.service.js | 313 + src/services/Medicos.service.js | 224 + src/services/pdf.service.js | 113 + src/utils/validators.js | 199 + src/views/pages/account/Negociopage.vue | 1074 + src/views/pages/account/ProfilePage.vue | 247 +- src/views/pages/clinic/ClinicDashboard.vue | 1128 +- .../pages/public/CadastroPacienteExterno.vue | 52 +- src/views/pages/public/SharedDocumentPage.vue | 138 + .../pages/saas/SaasDocumentTemplatesPage.vue | 523 + src/views/pages/saas/SaasFeaturesPage.vue | 2 +- .../pages/saas/SaasPlanFeaturesMatrixPage.vue | 2 +- src/views/pages/saas/SaasPlanLimitsPage.vue | 2 +- src/views/pages/saas/SaasPlansPage.vue | 2 +- src/views/pages/saas/SaasPlansPublicPage.vue | 2 +- .../pages/saas/SubscriptionIntentsPage.vue | 2 +- vite.config.mjs | 4 + 112 files changed, 115867 insertions(+), 5266 deletions(-) create mode 100644 Atestado_Psicológico_1774873197838.pdf create mode 100644 Atestado_Psicológico_1774873520538.pdf create mode 100644 database-novo/README-GENERATE-DASHBOARD.md create mode 100644 database-novo/agenciapsi-db-dashboard.html create mode 100644 database-novo/backups/2026-03-27/data.sql create mode 100644 database-novo/backups/2026-03-27/full_dump.sql create mode 100644 database-novo/backups/2026-03-27/schema.sql create mode 100644 database-novo/backups/2026-03-29/data.sql create mode 100644 database-novo/backups/2026-03-29/full_dump.sql create mode 100644 database-novo/backups/2026-03-29/schema.sql create mode 100644 database-novo/generate-dashboard.cjs create mode 100644 database-novo/migrations/002_setup_wizard1_fields.sql create mode 100644 database-novo/migrations/003_tenants_address_fields.sql create mode 100644 database-novo/migrations/20260328000001_create_medicos.sql create mode 100644 database-novo/migrations/20260328000002_patients_new_columns.sql create mode 100644 database-novo/migrations/20260328000003_patients_drop_check_constraints.sql create mode 100644 database-novo/migrations/20260328000004_create_patient_support_contacts.sql create mode 100644 database-novo/migrations/20260329000001_create_documents_tables.sql create mode 100644 database-novo/migrations/20260329000002_create_document_templates.sql create mode 100644 database-novo/migrations/20260329000003_create_storage_buckets.sql create mode 100644 database-novo/migrations/migration_patients.sql create mode 100644 database-novo/seeds/seed_015_document_templates.sql create mode 100644 docs/architecture/Documentos e arquivos/documentos_arquivos_top_de_linha.html create mode 100644 docs/architecture/Documentos e arquivos/plano_implementacao_documentos.html create mode 100644 docs/architecture/Pacientes/cadastro_pacientes_levantamento.html create mode 100644 docs/architecture/Pacientes/pacientes_status_implementacao.html create mode 100644 mvp-assessment.html create mode 100644 src/components/CadastroRapidoConvenio.vue create mode 100644 src/components/CadastroRapidoMedico.vue create mode 100644 src/components/ui/JoditEmailEditor.vue create mode 100644 src/composables/useFormValidation.js create mode 100644 src/features/documents/DocumentTemplatesPage.vue create mode 100644 src/features/documents/DocumentsListPage.vue create mode 100644 src/features/documents/components/DocumentCard.vue create mode 100644 src/features/documents/components/DocumentGenerateDialog.vue create mode 100644 src/features/documents/components/DocumentPreviewDialog.vue create mode 100644 src/features/documents/components/DocumentShareDialog.vue create mode 100644 src/features/documents/components/DocumentSignatureDialog.vue create mode 100644 src/features/documents/components/DocumentTagsInput.vue create mode 100644 src/features/documents/components/DocumentTemplateEditor.vue create mode 100644 src/features/documents/components/DocumentUploadDialog.vue create mode 100644 src/features/documents/composables/useDocumentGenerate.js create mode 100644 src/features/documents/composables/useDocumentTemplates.js create mode 100644 src/features/documents/composables/useDocuments.js create mode 100644 src/features/patients/cadastro/PatientsCadastroPage - Bkp.vue create mode 100644 src/features/patients/cadastro/PatientsCadastroPage--preview.vue create mode 100644 src/features/patients/medicos/MedicosPage.vue create mode 100644 src/features/patients/prontuario/PatientProntuario - design1.vue create mode 100644 src/features/patients/prontuario/PatientProntuario - design2.vue create mode 100644 src/features/patients/prontuario/PatientProntuario - design3.vue create mode 100644 src/features/setup/SetupWizardPage - Copia.vue create mode 100644 src/services/DocumentAuditLog.service.js create mode 100644 src/services/DocumentGenerate.service.js create mode 100644 src/services/DocumentShareLinks.service.js create mode 100644 src/services/DocumentSignatures.service.js create mode 100644 src/services/DocumentTemplates.service.js create mode 100644 src/services/Documents.service.js create mode 100644 src/services/Medicos.service.js create mode 100644 src/services/pdf.service.js create mode 100644 src/utils/validators.js create mode 100644 src/views/pages/account/Negociopage.vue create mode 100644 src/views/pages/public/SharedDocumentPage.vue create mode 100644 src/views/pages/saas/SaasDocumentTemplatesPage.vue diff --git a/.claude/settings.local.json b/.claude/settings.local.json index f113e61..f26aa21 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -43,7 +43,11 @@ "Bash(\"D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai/database-novo/backups/2026-03-23/schema.sql\")", "Bash(\"D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai/database-novo/backups/2026-03-23/data.sql\")", "Bash(\"D:/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai/database-novo/backups/2026-03-23/full_dump.sql\")", - "Bash(wc:*)" + "Bash(wc:*)", + "Bash(python _wizard_patch.py)", + "Bash(rm _wizard_patch.py)", + "Bash(npm ls:*)", + "Bash(find /d/leonohama/AgenciaPsi.com.br/Sistema/agenciapsi-primesakai/src/features/patients -type f \\\\\\(-name *.vue -o -name *.js \\\\\\))" ] } } diff --git a/Atestado_Psicológico_1774873197838.pdf b/Atestado_Psicológico_1774873197838.pdf new file mode 100644 index 0000000000000000000000000000000000000000..35e27fe1bfd4367f6632cbf9f8a124cc0dcb07fe GIT binary patch literal 79162 zcmeFZcU+U(vM?OQvc)&v);Af9L%3eRt)TCzDyvnwd3OYi6yP z^+f)Lk+F)p>Lo#W)>z3W!AsH?r7<2T!ONG`U=g>xrPXe@`+5uVuUPLOoU}T>)>cr> z);k0fiuLpkk=FPt!phsr-~Ct2ZE5F=d<0k*tg4}{`}d-)p)IYWsjjM{^LL!Cj+Q1; zP|Xk%gyT~Vk=FbRr7Kqiy@R}d2=L?kUj-3Vv-A$~#ra9=Y76q~LvUDcceLO|RgH@m zFKU3L!v!x&n+OIAg4O>ITz`n8sja1|t*xnbQ5USOuC1eUNmJ{RwB|)s=}Q`5Rdsdn zCEbgf8af*4>ROu8o@hZevy0M37(wU{LH@?;ZzBEZ{r{$$5J5Eyf3FZ}=O3E0mHtbY zLVu|0hpc~V2>$nmf`8MhF~8e_YQMUNc>jQ;)l9tu!@P0+p6-HbcA*})zi9r4{?!H5 zAVHoOFaIE4X*Gm@(Dk4Y|9?XWsu}xZLvV(E?pS_T_*!!R+ZPQm_&+e$`mZxr`4toB z_0N|;`@bQ9jYpt=a43Jo{j=4e^WPBShZ_E6g6RIIf*A5Am%lgmpRWV;AJgIACe>f3 z_y0N&|Hk`YG!oSRK860#O!zke{$=`C|6#NK>q_|7$?8 z!SEj~?EiT%{FVQ|#we)&=SK0b?9FiQf&QNVg8u*KhVk$H|Cb5zpW8+!_gg+jeEaX= zj^q29|J5x1mt27Qe{THjBG4X~!2etN|GQ0du<<~7d-8qUzYA=J{>w@N;qS$FaQ6Ntgu-E#NjFFK68!~8wHZB2gV`$&IW$PI6-AqIU56ZB&Z zqVwM)6@*5e#jJ)~2()&NG688G{J^_9nRYyl>K zUw`zU|D9hjc>sXU8UP^h@b5T}CjdazJpka$;NNj_&j5gve*gfLoqxyuT_?BPvF?8% zcYuH0@8bgitQG?R#~lFx;g0}-pz~kq_?Lemn>4>lf-e__|MCR{0=xmz05d=kz!RX# zhco~e0qOv)y&-@h;Gn>P0|x{S@-GJu9z1mT*iVP~<>b+$M~(@dJatOwq>zyCX$etb z5iy{U(3xM(h)GIHNl6Kd%E-x_lan|nb?yg|ef*|}4;?=7(@!VPi3o|D`)`-MR{)`( z4p<*37T6~P*e|qCKxp6IJAfo#-TMXh@yoyU7I5hBfrCE@?B92UU+wTSV4uK&ef#(G zk%tZ*Ikf*MVBda$0|yTYojUxBu=-CTK$+`Z8c;iT&(ue!MT38r{nZwp)^o`tlvsX7 zOIrsU68F6Hh@2*_tU?}a7?zjv`rTRkyMGk&DU19Ej(=nQ11t1*IsOeh2skFd7g9(- z2yhj!@Z#U){J#QjyU0bufTx;RQ8FQ!tW_99XQ;@LQILFNLA$;0YWCpJ@vI$9uqNz% ziWT!i57?TvirP`oJn`|M;%o!Eoe=Ze2)#P43)=4eNNVC|DX1S;-uB~2AN`u@Utz_-2}B=JODk`)p~R= z+3NvT$%So|msjj<6@86&C~hpvJwv>>jP@-wj{cd6p}fZ4`-;BY6Q>`wYrGB6ou}^s zR)25yYFXWm)h+EBudrPCl;`9(cB7?>2C<3Eh9c5AHM>`Q5}&hoiBA?QJ2UQ#^>d_B zs^FSE-A1irBG_Oe`OOn35Gv|~jrjIV(&)H&hy;Yw>8`CSWKg$I7*pL!4o(xzNs3D9D(XbUmLs&w@C41!qU=kobNCm zPeU1=((EYy#G3at5T95Cim}M=lxkOZxgR0@!mBc%qEtcgtRYfl$s-2!06TlUMRV2L zp%ziCjP*(nLwb1Bn~NfhQ%gys=2R(F)2C6(uE@^XSovlRktczo-{B+DFf;r6WxCs7 zx4SPv^WT1F9?JivP5o)8hvi+F3a88HP1ibS35m5CIx1_|gm&k;d)0t*6r*m)UnR(x z8sJ0ml!k@->;X)v%?)oGZfRX(!zW)(N#d2(T(A2VS6C)FP^U_gDwa)RfM&qk^j$I6 zNpV{C&9YBtBqEMi7WG6@sm6M7X;p```(&~6jlPo9{#+miQwJ#j{W#Y?JmCEor=v4W z^eKCqgq_Rt0&+kK!9*LyDVDbnXz&eRDC+BY;=VzR8!8z<;-?(qDHXhop9+aWmpO?p zltaA#tGV;fYIF$9*#orA?E#jg_5j_*hqm;1-FtxC@oEcNHiX_tB9HdZaXn2ofqv7v zv4s;#!gv`_xJejXe?0Ger;VH%+_;5}qC{}*ObpcW4qE}+sL}0F{NcLEid_XTSW+2Q zw|EhAuA=g+Az|gPC%KYHVVy!7EmQmqV{K$9EW8oCCf@Mt?h}uy2Ox>Lc?+mxM1FP4 zBQ>W4u{O+cF>+%P3^G^^kC4d-)9>0=I>%k)94WW$wS^*|MT6NYX_9to=cy4&!}u&C zcy|Ckd~w7o*X+wtZL3n7ff}GyjFm#kmHh0$2hmTnX+?v5MKubqPQ>t}>G=ga6A(CD zkB6iDn*G7*8Ij9#vwj+su{)F6}6g+G>$t%_=o{z^Yf{=R8X*Fi3mz6#p zn%zz1RgEa{u*(DEVz0$Y#@DOC^oQ=1L66&e$Tul94pYQc1Li8Y8;f1`n+CIButAUv zDCTBJhQpR!kFs;uAm_1XS#?#F7V!(BvUxxuANydCck{ICH9>g)l8i?)6VsUFpX zB)Tl0y5h8z6=;)bvZFw+(Y>=~rS?@R%Oo$^;)0s;OY^FsKP+SA@+nl0R}Hpmhrn~W z*qVlB@A(5zyVnvw8@J>ok($&KikPHKBcn|jY2Y59pm^tikJQ4<9$@aCRNN@N^5nI- z`LuL1_YyOO1oQ5<`hp_x~P2VN_IzJ5^4|N z?ShZ5tG`f`dyreNn}A!qr5DyaT=KyzOIG!S7lBwtp=K_rpaNLifbn5W5yvts|BH+I z(qlRH+*BGiw4L0a&^Sjov8(PdmXQglf0`2|e}&lheI#Zkzpo@S;$}^+^QR(OrmoXQ zrHOe{_3S!@i5!c9zy>g8vroF4N9>v9Uh1zHQ*%}{-?)UG?2>@<4J*sb3X<1mchxf| zA1|&?A;OAsr9RIEcXx<;V!?}%vftF(>IM@A>|Oxf+>|BNxHgu4!I6RXbe4D{J5}Re zm99+SSK|+$Z}Qk`rxD#6@Um*)Qhx6?@&QNALHRsun5ICPPScbK5J+an%}MbNFY)=Nwk2KI zr6ics=i=?rlot9`%S_he8?)A_FHg00t)cb=yosxdG@d`*yHoWTiI|jk$GF|2 z8(gMKZUlz$P%>a4eeLnY3at$ZH_dl-XD%hGT0!P;P*+7Mh;`Z*wz6QELVTmc(7HsL_X7pOlH`r@%0~n-my^JAW&r{*C{vA>0;v1r%+S4_*CInuhU>UBP zQT(E-7d8+8|3Ef>!tOTR2pF5p949wYo^qIIuSJh5f)3fs$PIk z_uAl#GGgr?sFr)$J24Em)h^^DW{=*FBV=agH%8t{+5?!?Liv7)39)Ut+jX^Y#fdT- z9UW%|a%G!Wv5m@qjjtK+oPC+6)(EFGM20Izet0qg98oIfHoi`ShoK_J8b8UZEM$A0x~_{Na|xCyXw(GCG`4h%ucv`nECa7!QBC^dTbn`ei5hFm3Y zH>O98pn-i3MNxAo*_E3qnppJ}sw_(8d;2s2vXz8$#b;~aN6ajMkeyUJ*w!0%p{0dp zy_08}$3!D|ve)Ly+%wlM3RR3cP5P6&**o5giWWAR{bxD7e+C_~q6wV|EU+ajdC-Xs@7@1F{*OZb zXE6NFMED=e@c(P82GBl`jq!!D)qYLWoGFYJVCJwo?^M-F9Zbw zp~5H8(o2eG}9QXTAEv#l&w z_bakJ?}PNGqJ?St6a|x(*7J~_ClXL@XZQM;++ZFTs7ambS*l%gy7R2R^!?Z37lVon zpj8nbaRUymrZkAGxJ-uC7jRYtihplEyWzaG?9qmKl)jYp+8{5E?sFX>PhfULD`V#% zQC`NBV5?QDUxZ(5-3Tq(11Og|yhxPnGtDFrAK6n{)8gyRNv#>ChyCMfXN{dRrfw^9 zp02)YKi;HAmi=O2>*Vz%vyXE10UZ&Is9)=!(Qhd1^E?5C4IVvbpmJZ2qoKgc@qY3} zmSfQ>Z~WE1Xq(yan`F+h0Awim!+=F_K2ffSaxc}0w;JLSPik1QdmK5&70Ufw=koIN z+DfQQ(D^KPOQj2Cs-d(!m~_TcGN_Wszf1p=ojm$GdXEU`291>KnL)1<>biV zjNUm1jtnEkl3~BR%vlvE|J@m5=v$~8Zf?kJafi3Q)h~x)hZ<)sf#OZ2=XYF!ZV%-&f2(^A6qT~p9viu@`#Jm zf>p~E#@V;OVekEAv&k?~FxPUR(!|Eh8!b_8 zVoxJRpVyLugzPNMW^dPg6KQhe`bN_0Spz*Tzv``n+lU=Vk+Rks3DdOu@+juj#I(@q zgvrBB0*@}*Sm;#qksVYwv#s-2f!2wl5+Hgw2nQ}Iq{z(rEymR0!s zk}HMIDk`!dMSB?Ht&DDm5qx|S4l%)$JrllYmy>8<*P>UP?5e7JT#}ydBvxs7>PDli z7|kBL+_ALWK!q^FUP7JFY1;Z}mbeOBshg1!5_8tkB!);HX3_ zNhb-MnF9{IqQX*m^C+Rf$mH4@d^~|6Kd6v$LDJ`#52s77KSPWCszT``Ax)y^?U%Lo zTs2j_)wAZ}9Ck+S}&Sr9aO=lE#A&p#v-k}Z4i&XToR6%8LFC0;sy+}`5_5gcE zRX#n(9v#g6S>?rbmhj`@5PI4mw&uMiZY?bV)YfKSU8?;D0p%|-0}4Y$kgY>~yX)gR zA~IWlcl=y$BiyHe=rw2+$VsFbH+}Cu{~bFyvaOZ+q|Z-CSeVJdkP z>SSW!N5`8JqLJ`WdsDaDMXzb+-UWPrUlFPrU>Di_KG}Hrb6u>=+N_jIpd=iRI^WIe zi7E0;P&t;8-?cDW-L$A6XI0c=N^Zc1?;rSwKJcGfp?kQe{1;N4cYmifny?~oqLt?` zwBujsc7kseMXW9M07)lmavVOznN3G(=nOKV(xSjZT9o3iePcmwtSQcXN3-b5W(*D2 zZQsLgzcgX)=NaOBD*rKlGVG;H!1#fTZoAX5W_qcAOloP}B8Y*4R8jtt25P5kV%M|Y z#x`0HHZMJ@ssSDxzc;((G#3Uln%rbAnkeiR3y0?BWqU9`gB{PgJ-sCVoQZ<}zM)6S zXd=K+)np}H<{*X@_UjT)y4{{1Cs*_6wP>Tfy-_g;U;b1xmJ}O@?$`soIVj%C*jh2% z9jn>{@ZN+wzR5P<129CcE`E>Q16V4A{=PlTExyqZhP?UG`&`R<)nWGax!v+lm@H0% zs_H5yoxtSXnV(tqJWns%ZT&9JzOdulxSpKcJ{E9${!LJQ`bNX=?{||U!u0j|OEyBAuTkIa?cImj+Nt5+;*larP)>e{HG?7Gii&)9G zD^q)S!l3m+h>gkaOS67|>YXDg z_w>|vMPmzyANzu%O1z;z(-NM*v1PknB;Vg3e1u-(;BE`2Is?4T7ev~Eq^ zqOg1ZZ9rGY;*OWiCoDTU?C3@TdOt3OKsg%CF%R!jmkqhCE5Rl%qBa8kNHhMS1)DNq z*Oos#-vhiqmbjA!-vb+XG~Ov)Kdua&jA@*FcK>Dj>rT@4{o>1h+|jH@G@; zhri@$G28aFVD=w8k6o3i=zAz+)}Ud5~P=3Opn@5_#P zR%P_E^!E4Bl8!gzZT&hv0peDeSNT}vUC&pG9~@&KNhzj-4d!{?Mgg&SZMWTT*(dWE z$SijY*~ly_Nt*kYnCogl@_u7-q3`_%6V0D#en;&-HmK|r%r>gj`|HB{r$Ud#*WZ!q zPKf;zII#z~XMULGw#F{r0|Yu>-E!C1IfhBQ?du$L+kkB=|5D4`>}-9j-TrDjw^liu z20v7tv=Udv^|2ZY(f+C1)`j-9Qvn!$2>h_Ry#NC5mRUFImux2v(5jCU%Gz%i5a{>I zMl6F2(SeHEdy{B%TekF#wC+LDsgMd^TgVwB~5wYo~V6f*-V%gQ=yl zoSt~Oapi1iq%5m%pz1q?`-Sdzz3pAL7|42<2e&l~Bd42sEpeI;tDOMbEO3(`HdNo+ z_8+14L*A9_ZbJi+E@h2t>oTS9Vckr-cBf4V6*d+5J?|Wm)}eU8&7d-`ZjFaQ7KCV6 zZN8w^pRl&JsRmMz92*Pkr;SN^tTBslGE493?5td|aqyiQ*)b+c`wj|EW&9=*Yviyk z-u+Ga^nlb{LI|~z3Jq@q&NPb7cQX^W2|OLf$BmQqJ_e4|xwPrSmH15m8M9%waVJ7e z4(8jJWTK0h+qkiKO~kYQdb3wO3O@sHjZo>6zFgT}Ca;P;FH<6QW zpjA@V42>Yn2fUOOZY(tSP6KW&zg|RwFs;bOJD)Dc=+RRgw+xTh|4u7LPtP^MOZ*$X z0vu)-DiMngomWX6yk8n=={d=ElR|b)RZ8!>>mxS5Z=cyve=xRgho{#w?U21(SzbIZ zhF&usHrP^J<$_c>Xka3BIy8ZW^{0^V({+RVLk8A6ZJe%HMmiyPqTJn)2@VwLT{{`0 zVtM1M1H8SeS@udw)DR2?`3i$s?Yqj`Y(g^)u2gxO9SE$3A~dp^jCyiKlCUfA%{*Np zySe4HOiTM^co^@e+Dh_L6v-onNC;mS*9Vffg(MQT<9!P{9dFJ}R+cCos&WDOi`%Ct z<(@8}W|{YaA6PL&Ow@AH;L)kb#o^qe833L#Ya-2@O(Tp=z^MrT3A0NslaS5%=9FvP)$NohDGWx*-oq}ai^J;UBT(9okrwrF#Gb! z;?27CMAiE5T9K4Ki=zg%5fR|Cg~+C6neeUlt2Cev$8F0nw3y58;AAD?Z_l3<_V-S3 zSyF{~+!k9}&ce&DkQD0>PIU87`P zwRzw(WY;qv2774So(2q2to5?eT`}dT0p0MjMBo*E_Sq4@bDN*aFWy|}w?|t!9|Rj1 zj#=1=T$V zY;d}wr|TS>!=KHnUC{7;6?29KA)RekpF@>w^Sy60zB!p!6ylO=o7Y(g9(}5<7sWAB zcW#uGBjm6?A2+fh3Q6?U=a|TVWGEPmFL1?VbIYv4C&%Bkj6NPh^B(pzYt!oqy^qjD zmy*vF<7{kE_H=fo)0MQbHFxgJ4&~}@*DJ3<2K)BLGo^qFUO2dLqr9vuyPGLb4Ur%< zoD79+^A5a4pK??kV8S)+hO-{zm?|6&sE#aOCSX(cZ z6c^sTij-+<%czT|eu3EM#cDj^-J0936RSg(K4tZ|qz}xD7+EHriZs7p40Jv5BTp?; zv@rnGo_szE|Je{K^(XWgw%l(?4W+A5++Kq^16V=*BCkEs2griX^AG=>mip!Wf3wRnO zok^fy7*h$TB%D}tV1;ngjvu^tTF{hH3X!l}NV@-Ayv-oSGzAo}^y~WF?w+EhrtW%_ z#aq=oQguP*IQR@hE;ai0@;D0_t=(T7bFM^ti5_{~rZ*s4Jg>4FL5s0uxgD3c;avpZ z0p%>OVq!G~s;~W1*XI9-n8&C}Z$^|AlJZ%+uz-5+qjgiuw9;d~?@5JAWI?_{ATm3b zxnW0owpgrM<2OTrYk`3w4ShvVTzZjtvHT2Z1Dm!7knsY%tsA3+i01V3a6 z#=B7B*L5|?6-(I}9s0za99+X8?wS?S`WLUzO7kXg+R^#cwq#=pC5*Ua)W>0kFIoQf zvqi3HWH|PNuokVk*7-Y1mTcaDBw={O`MWP=yRu&vqXVk2VE`Zmk)LZ!L3xvNPs}JY z5Ot$HRI6$Ps;+RWgD%?T`oz8Zfao?xY)$;p@et1}4n=O906~3c5S{AM?i~CBK*OH6 ziz9q@-I~7-G)iru%+NG@?7(gHC|bgz)gC}LHV!v&=yi1su!%$~tE)xoL%p7K?6$S6 zA?BAx6C4EIOqkf*s_Ok{Z!+EkdpGOjD{pOy1?g&K_mnY+!rU2)Th_UKaQL{EnokEp z%tXH~C?bL>>X*sy=<=HI#RGzj{5qWy{-y$rD(w{8nOC8%75DY3@n{^;TPr$Y-TpfB zRYU{8PsdG6^n$NozN5B75PJY8v>UCg@_BBLOkVy=EhFEztN@FZua`qk#Pi;&wV<%a z63?s+rP)Y{Vcf{c8K?8@M!e#C7vi%YmI8oDSN12}w>T)TtZWQ9 zrk;OR+^$uucOhZpmnFc@F7pM*%OBKA$21;94T}Dh7pR=YQAo5zQSyS57Hz<8zw3HJ z9~d(=<1-C;`{*qT_JM>RdCE>;LG_I`5`!;G6x^;AS8tws(0Uf3!V8&gN7+8VNUF}S z_79j#<1bvw$AdW|w9_vI4mJ8R9cV;n<;pqdP!Yu)36US^qhYKw{mM^=hC9 z?L04Z_C4F0d`DQ&;KL-TZ7#h$HpJElrNC$VLcsCC#JZ(Vk7ThF)7JTdCp^TxDCx`y zBP+ud#ag0{n0`QapUd(IZFeJ%y!uGG*{#h2Et3DNlSL_6_e|NHOE&|`xe;`K0{B&Plp6`g|<=%*0VY0 z_gnVa#s? zb$1kgwK)X|>AJEwoAKL$OFCRPn-HgaT&a zlW7`f+$d=UzS9mO*JXgQ28I(;Qzt@e_8_v;oOtT z8JS8atw__j(6h+bFJRS)#Ts}uu0r^CqWB&lew`Om%|0)TNI;(Bsyv?n{24WWJ1Gh2V)z9DH(8d-NW$%y0^Zh8(>#-qiRF>!IRq8FV@rsKhS&^4uxKq2a8k2z0tH%nyt3$ zq{TQ$Ik95Hg?2;XSwV~>>(8i`c}#_9)H#GkYfQ-(D&?jx71}fsTpamz$fegRH~_9y zAHz0NX)m#NSb|rCDf_Cnc|Id{!Px`o9D~cY+T2T7Pe+4n@RuH*Hr4F+fqK7J{&`Fp zc&FS1RGno{l@cFhg`8tAab&*bWjzG^HYtf{UigM?7jZ_fevW*OKPzr>-8i+?HU;dG zBPLs$nXs(8?UZG`^L{~q6c+fVvVPzoaIo`sRX!3PSYx{hR4*NaYz^jTHaNrYNIC7h zc+fXuUL}X4;?h0tSki~rMM_$YE9uu+YoaIy-$RH?o^a8i0HA)f)w+MLB}TZ?F(^Z~ zFs6RxMvLOy%>bl0w2Ng#9D=z@IMtg{NA&lp9ypXmvF8RyFuKQ|WGmU0vlab{$_+sC z$mSHiGhJ{vKFig5JUb&&<08FaaMdL)Ff2;-da|qR-9AT1M82t%roNG*tbKI{mcPf} z7#lv&FGUD(Vuz%O^;%IV>qbgPSOe}v6iB(h-aEFU$G}l#aKL}X-+&FnGTWxBXW5X! zQcCu375ju95xwYS~lfheKQ^;Wl_QQ0qX&mzRBALnI%@OZaOOGXdaNo689j z!OY9UGiqKobvF9!4{`1Olo}*zqTqQ!LU*TTVo_;)7Th**ZVzBj)Bf<#>*IO#3}?T9 z#Z(grj=@xhrJV?g>CjK9uH6Gr7y;0*-`pO3HMFylgj_D|I96iPEEi+~UgU2OKw&W0 zX0F$%RFyLKI)!G{jOs~GxvK8#HpkTU94S6Hs|krfOt|iyg5AjL_|RY3(e+V>D6dmI zIT6yK5EaSkd(tt^V)1l`G>PeIJO2uVOfh@DE!c2Cg_{BU0>rLKm7J<{VsNW8$yXi??oB6{(|t~qD90pyW`kjn{ri^(3} zRMSMq@%%!1<|<8RNV=BWxdza}EJKvq3aSh|=;_TtE1!#rmnRU_9gs-OY*or0;1VWa z&iI#GuR&iOmU52gC7eqmWGZMl`Ih2l$IeyDcHy3e^3NwnU6X%x_w$HxqI?|$ZFxzf zLgO}46PTgzVQ8ad_laOaVZ!zKkqY<`Eh}71W-~m5FmQ`?0+OZG|R@mo#QNC?qIBl`5B zwvH-1Bj7Dp7R50xyZY4fiJTd5J1StrXuPi7hjHcYi)e8nYKmZ+l6;pU*H74Vt;jPE zQT`4dNhi`c-dM`~`=bYdYkyKtI}@DEk`N9WQ3sK)>CU)}sBmhdbE6hnY!yC5-*!oI zQyp4}oQi=m9W?~A4j#Ii>ctV$8IljwGCz>ZhPGA~5JED0LsD5_AYy6%?PL0qZ}gct zDKkI=_$AQuK=N@*3yIQ2?Z!pdMQj8pTI4-IaImT*>@+gKU&Ub^5nE8z_M&f?X5W~T za+n#jPW>iR=e+WX%WxhNMqU*MQWYDT}E~7!+I$3Xs)JYCWQ0) z@^7WTpZkFQ9`wr}>?zI#(Dd`dq?9XqVc>3xZU;XVHEFfjx}shlxZ0dBG^@by4uvCI zZx1c@YR(%VotdppEj^uPBXGFTl?G|~Yk%GxF{Lv;$j>%F5E zq_Gg-g)ua8DMl8AsEK7S^?h>zn->y5|}rb)m~XsiqBgb^$g@ z6t3TeX&Rk;KRx}B(p09u(1RrM^o(wlj6z>=He~`+<2?S-iAoNbowjO(S}A!Yao3m~ zaL6u1@=<6;VV&XYuSb=68(TH-&rPXsm83~roXe;TD0u8=cS5P`9} z#W0~r+#iDCsyx2b+c(YUf!DAtWz|4nDJ8G^1EgEKvY!6Q<=Hb&UIq8=VtHR=j`4sD zvBcch_FYO#Vna+r_sV$Ya+S=6^}BZUd3%+_OTwXV;=Y2s)yvGK_MgdkYjTT|P%Z4n z73W1nTsVDGX^9jXAijJ=&+%7x)3HLa>KgaIXcG{>|-c!rwo^(NBe z#r215Sf`tQM6)5}l$l0(AFxmARHuHoL`b4 z`y>h8t#3+U+tdL=SNb~D%xt!PdVh7Ga}U4D=iV?Zlz zXx)X=I!Za2p}9xq4rWf>Dz$QGr9IS(q=-ExrX5cbLZry(I!z)2$}{S#XhbE2LIta( z)f;v_-D3j?q-)z5azRnzI|!C}$^->RT?#V#I_BN2`djU5GuIsjy-=SzH?*i&01s{- zkt@){&V!MNj8C*g+we{LNTX)1n`cDM+StA~3t?GFgOOYXR=0_x6{hGOgKU};Pnwab z78^4N506o;D%{_N&3B?>^JLDm5ua{4EQbXwD$SK-L>?{FKUd0f(o45&s=GW$VZ$Ic zq~AW(xx8XMPjv0;-x}4d!0OeH*@>Clcu0iO$e5~X$}Ek>_&@qe?cC8(q1^iK;c+=OJ8U(PvG~?CuNSs|;UM~PC{?goFBIJw!9kW(o$dZf z3X?#Ol{W#c1`>QFZ^2-Efdp(&yu??kFI;CTUq?{z?SU3)b^|tIGUaGUQVmgyhsaY) zjZ%I3%tgBRR<7xL0kxLEu%niu3_n*%Sx1SAm1643)NYzB#Apx<*I7jdL=GfA-m<|p zi?V)RbrO7hJpD3M5=+ep?eZA zIyE&-807UrpOX-rjT%{%HeAxh!? zzyJum>krCP@QQ*1wGzKf=zX?sGP%TQjh{9cgl}TI)iQE#s@^(r;r(Lza3|yJy&9Ts+8wZ2 zP{3r_yE0iBoJ(C}au%HbjDx6^wGG_$J4Ar_`u!b3xOJg^*O)4)IHi69e97C=2bB(L zRk8(d{j^BUOr$H0XjjEL}f8M#vM5x+{YYWomdW;qJ3xe zOLYPK^nz8^qYu5+vTWAiZT*CF;ef>^GvcuPb0W(fSIX}IfPXI2U+cLz`ZkpfO~EV6 zP)=>m-qq!;g72nKb%U`Y0coOm5dSGtSGmm2T+i@8f%Vb2yu1K&;~Cwv)*F@_*_S1E z#G%Lz$syCk;UlI>?V|Q*__yQrt+>Y3;h=!5v`50OXz~HLh74#Ejny4?CE;uNtA&~V zjkc|PRGAl0OYm6i`Bu7SaHV;F;G-i~Dfy{rGl>~6X+{Bu0x^aUd=Uy5jY#hQ(&j?p zZPmo6ID4A<7Zr!ou9)ncv6UN2w_2b0(!E?3*f(Ktt*~_tMT0*Ma`WT|;c}rhb=8Uk zm;DQ{;Eu@eqpMNW!leTX?`m8YEAdxdSi_1J6swszV9Sn#r%zb{5xP(<3MoDpD1F-= z@tY^|ROMu43q|>H{x9&txco<{+K&iE9tsN8A3~}MOw^t@hkhrEM2K2D29g?AYa=7G z(vEUZe?@;eYnw#!ps2UZ$mj}bHx46^YXjG~0La(vrRIB$rtOIVRHgaR=^Om?!3PqB z5@Hu*Y@BNwL7~XR_*9*myO;T$SS3y?pFG2!b{J6sOD-iyhuVqr(iOlT0@hZHkl)r9 zo>~9ycw%99n+6VmT zh6a5K;)k$(M}wO%6m%)PpbS(~wXpfY2Z9aM)t=$4a7gcaS05Wk4^PbLxyN00O~u{H zU24sV0FYxoA z_vXq=;1>50Jx;-c7B1{?^IqcF8*4$FHy%a9S^NibjCYnc_ubzAC+{@e^?n{@F@_4| zAB++dth`(BP0DHOu9{sLwU9`@O?-6JP^_lWO=vMz-qg#;s3&@Dy8dQ3Nk&qlIk?uj zIDN=i7MDKnjk@B5s#fr#${5T|xmpG|k(scCdrDYh9h6t?bxI*I`Q{DfvIr%~8~DUs z{xgDnSF!o%#FN9KpZf2G7o_b0)Y~+o@iSI7m^6>_*8)9n?|qmovC;+V~*-cIk_%c$Zot4k~8sf%V6|NE%oU80P0xC#**gh2evoNztLqf0IDrc zY)fcQ$>T!94hP*O8mLL(N45s;-S81L&+@raS!Lm9XUWEuGM6HmP@!Zbg-PbY%6CM+ z?56d9VvVwV(_GX_`zm!%DRg_K=i_wy#RL(I2c5%JhzZ-ja9D5z9aj66^kiYguVYDP zv9EWA)nmDl@7x?T@tN6Dn;HM0$FVSMqXDdU^5b-k+DqIWMZTvsPk`~NtPsp}^B|S+ zn($DJswuwVr0U@Q8NeUYNb~ZEs=j{3MZ_?E?Xz6>M@PRM4+j}Hg%-**Grr+?O~Bed ztpi$Jy$`~;cadd-_K1ShVdhnWisWq&%lF-=O~OjGoegKKaI0m?1X=2be#9y2$5tC2)W?#M4yK6&4z@fe41IF6&2HG@i-N(x86q5>wef7D?#fm{tUeT*3akNz0 z`r$^En5xqqs(m^xr0;Ctb2pPlQR^tUOLB;K;ZP>q`rV)MbJi-JZ!TQYMU5f6=$St~ zCl`&=jM9doOOv!7yR9)1{-Nia=cVb2dT*0ki9cj~;$0$)I#B{x8s@Y-^e{lS# zLw4*3v0~L-PD|+x;J)51Y+GrnKUw>8<-d5y%#8dMVXxdP&kuy5+4$fnybKk&=KbYmlmetR0H*t5Ob^F;5nl?5b}=|W=!D4{v# znEgd3OexJuVI&$qjsyZdR{{u5FSF+%DueZLJrBGGWL0`p4A97&fr-}#$h^mN1lVPI znWZk7AMtdi^0IN}y#P-Jjq#FF88>j-?7Fdf8 z=RX{L7Eq$7HMsS3TwD3(h&mgD?G-PKyq-t}{&_?6e zh0yioJ;153o)leK6Q$e1R$mdaP%zV~XmUiiQ~6t?N(ha|5fg03?J|&sb=zH}l{A5~L29N9!VNx4MYlI+fnv!4^q#jV89~E!1e=9la#E#3F86?;slC zwAE=DHbCTcGH{os&LS3pS1{IdW&!qNl44r)1CRttKZn51LaBKpcN(YsnNEu$%S9DK zev4nOn3!*JZw6Y04A<}jeew9IO-5#7Hid(8@~$58n@>;o>9yN>cRclVTD^_0oU5u$ zT=6YXpcofjv1Jk}L*U*jcur!VOL@UC%l?rYIj5?IA#PElF#cxhSD~K%rOM$yHOhL| zVjf$yW^7T&=rv6dYsY-z@DqLh4$y!y5&_B!_=K}+muy}NH+QBrH3raLLMbXLaEKh% zVt?MEjzJ4$bmzO`)I*QQ^$=y-$ulFF%W{gOY^0&Y+`BEJE1R>rzqoAtu>cDal~*4s zs8pD_78wJ}Xle(_%9bFiYB3m|A6ZrlpEJkk^TiFRHuQw*uOBK`X3l7B4COjYcBef+ zL&TV~z4+&{aF0cx<`O?Id#@1gS;ihnX@~n&bp{&e8(5qB=$%lUdvG;9&eNwo~q-N)vqNEsD=jT$tqBv;rZFrsbXCU z>7aYp&|+Sz`-k$mZ^-M3BO1COKY0gNvU1R8QG6emGG)?Q=dYh(0hj2-V$8FaGG(jl zl}IRzvFB)y|Aj? zF`%m10&2@6fMK3#U0=h!u?s)-A=3i%GmrG5XkI3-Sm!Gafkn>GLJc{M$>qX65K9wG*PC* zpE=-n_PS4H*N|U7r6expP@{PGOc7J)=My(sULuYNNWf|>l`0xq%kGBd2M-|wVebiW z6IuWCL)wjZYOs^kia|!uWX^`oL4-}@@q!al(E03AQGoc|oRddYUl zN_6P1EG2kbnLCfK_y(j}o7517@!4sj@7qHOZL=2?NBpWvv^Y$jI_;89cQv`F#Ehm6 z)t9Mqx~4SB^43;T851hC8ykAy%m^)OL`K>hQ?2_z)&3OE?Dd2Tn@= zaCamCZazmv8)J4-G~(y5~- zwGqbTkYUST2GC;q_a)<9g1F%?Jt{#psc;}@rs~}{qFrkQH6m~9nb9R(jt#Bvm(m}F}S1GBbXnD$r2>$L8QJ<68v&TBJsv%sl!60 zAUMC&NoAel9WH+#|KwpQVt)LcQUA{;%|T9-oFwGwoJ?~Pd8UX=jnf=#wUJneCNq** z_=eZVrI4(IIG4ziG3fGNDqSS8mGydhD90>iIvg{Qw@B*L2IU2JFGKoB_)OPXKgAbM zHL^G{&O$E?=NBC2FY5=Ytlej1dG*Bb+{j@E<=LiGe_EYHJ#Ck7eogip8Vo4rXFsxS zH-DF~92j3>1UJ!6DMl}s{y*HkXH=7E+xP3}IOA9c5l}!KX-a4V5|F0jK&S=`X%Gk~ zorHjd-sDzq5Q4xY7|s@>8_rv>X z^MMsuxh}$Wp4V}m$8r4szmr$(I;%Tu+dQY67hKk{C_k$~d-39ab~-E8^rET&@gvB3 zd9%WoAMcRumhdB|nPX>pW9wk*g*AUGLE2j@{^ewLE1JzbeR^H%k#_}obI-)T#uV0{ zW@)K>`1`)`*R+?Hf<{=MlYGxqIO!bPoa*+uU)sVPDatNsKZYt3G|Tv?4@0VZrRrg> z*4f5qngIqV`{=A)#C4U6U*}PR$XqzOgEWl{Dx|UsCnaoyzXu$CR_l9Z0>z0V(SV@s z3eF&7qJ+e|pG{lMQd{k&$T!|5%0SLF!#2<*9y`3F0 zIKK+$JSF>h{u=^%g^eedqnNdEV929;X9)h3Xjk%QU z(M{_YFZ*))ttc5N@uAWAy9x`~|3IVyqP6ctzMxhhqsSCv+u zYYBZHQB{4ub-^gPgIcA2O!Y{8Gv!ue)CJadlz4uZ z)ajoMIOJ3F;i_4fcuU?ikMOt~6Ua&jE&?w4Am(fjVrPF@95wY zWBLFT)%^R`_JkuFjgO9Uup&0@NXu{jwAi1dETac@MtU7#4@1K zQzzSvoYj1P&d&8_TWLl(afs|f&+u>AxA#*UM++3;z1x4$w8MStz?t@oU%f zuXJa5>59|&3rLhl=P=|%M!5lJRH$vRY;@~Y0x$09W7vj2|E(dUA8%9c=t^{Hjx&G8 zg^x48(4uMg&NGq_1J z=?OrqW2B2JF)HjfsZDV|?R8L11lqNv5fOn`Zdtn?MA6{C1v@CV9vB*C2!C{%3x(Mv z>ZN+Eo!GnO<2g^S*mN|g+=)8h`hs-iV1_7w0Ge38$v60^?k?L}CFmo}@)+3s@tTZ{ zp+Tjq8Xl-Tz=qlr(nJvGr=QQpo)6D0`?h{Coa@Uu5g>RS=AxP^HH7@2`2VpTAThHcvxjLncN@R^Y(7+ zRN9MIErxSPD%V<>TKf7C#>xU-SZd*?DKD&J!6bqdxcC%*41+c79n{zXLq0aTBh<{Cd<94 z7n{jnnw%b5E`PfW=+ps{suikWsM)g}6awnd^B=WF>s1lUm<><0O{yFcb}3-S;y5Od zN-Hc&da?^(6WKB{$Tx7$DF%D1f4aQo{y1bN0uS*_+&4xyyny3B8dWaXVB0WTP@ z)g7NHuR$@i=lse!5SZIpMXMV8-ve*T!GR6Z<+C9LjIEruyj}Uh713_#Tk9+qurR%r z_AP%$(d$buuZ;7ZXpJWCxz}!0eO~QJKp%bC_}*T@DtkQ|BS?2=S}!2& zO6i5mBik3A&KhL7ec4N!aQtE27u_?)o*zE95D%=5LwMSjoz)JA1r#{M@S zNxX%le7;C0u)Xb3td@Xi%;V$l3L43G9=CcIEV~X9FsQWkWB* zP83$y#DR*t&YGhv-`=jtKnKae5$b86P>{P;tO9_R7RjoRo$uFqv}82fh{ zb>0_F_K?z%lc&5S7uV4y$^=@i#s+KJN2DX4d$uuqPN#|>vT8;e|jA<~(q{(f+ilB8>>Wgyi)H(lqA z>LZi~mv5C6XIz>P2gU%YDUJ0}*FxuGUuX2$fipiYWT&Vnny3~O`y7j7v+N&PfzT=% ze@~8DZvpHx9UKe#dz?;HLmf~v=hhdz@%@E>S_v9Zx>w?CHV`!w>gjN>$$jOQP>TH% z)db$se6qXJ0qONvP96TwG=RnpguP@w={CV~`-hoNc1sE4$R<}tSy6^bsdyo#k(v=X zz+H&%!7h-x?iQ_SO%{%ZmrQoPE-&?YKRVFdMtf-zRg}#*Hfqup=HLlc>s&ndI*+{= zY|5D#sWJw87U#mg)X(N^G|oQ^58EqCkghML2BmDYi~g?5?_;~91D^jDTMOxGl0S^H z9m@ch=47E#(gth8&Sf5d7f`;2z?dUi!9ffUxM8TUX)k6m@XP$o+ zC7ga5Q|snUsZ(-;P3dzZtRBVnQW;Z>(xSwBJ#r=!XF}{lnrdPux3mWHzwd*#;7(~e zS$;0f>u@|eR_+vo3pw89lXCpBrz4HljaT_0hy=El`UyW{x!<|s z8=_%n;ozPt>_wb{Qs>v}eJR6o+}_~gS_r7=cz(8gv#e1)S~}#paQL2|&i8$atp-2$ z@pf;q(RnxMFLiLrVdeH*aV}2mVDEi{-&%%BV4W@I+p&5X&?G&iVM~f(}u6Gh{iQ&`tuLP z^At-^{M+BR7cfYVWd}Jvjhd1rbQ_Z*>{c8dF;wNK{-L{rnOhe`XZ)I+;%X5SX;3Gq zjv~K8E&~CG#%#xI61^3Kt^rQd4sNi$v=1L1J^CJi@S zS&m=ACJQ28qFC~qa%Yd}fR*vhiiH3bb_U3b4=D55d@3iO1_wP263BOGGSg1}Src-P zb_*AxLQm?ItGl7onrj(Cy%BGY@@y zDZ3Vn!)_!Uu*HoUtbGH*#D(^mFlgVk$#*k z=8_6glS1L!v@>2?^sfw%68?aVQeyr;rU!spS=D@Q4hpT=Eh-VW$4ES#7}}z4@RMueUybOjS*4=nGw}gr?xzk&nhW=;bCwrFO}Ez5Y`R10kk|z zXcnt10Ed75<%hSzBi#p#VPOwrEjhfn^00E4HWg=Cl=|#B$NFP^ZB-?ThgwV<#SUrY z{`PL!Jvm^iY23)Ifmr%vOLDp_3@XiNkbmHj)=RE)u_yXl$MouT3e#ujf3~I1zDW1r zX?c4xD+7_4S@mTR7ofwjfV`hGTuy$px{;6a0h0#tp@^iAD~s#`NkSuGUR8+YEP5M^@u$Uxzr_ zP4TLY?$CxR#AwnJ=pl*j`#4{-3J-^^$Hg16tFGEpU2n8hMK#UVEm-xDN9E8I~l0&etdB(sapv3p1xLpE1_W_=B-Jx?{uSJ_3o7pa=2zWIe3^kZXeG#YOid5L{m{E}WpCCI} zd){XiEM*za$3eN@aE+8m4mD_Fb5=K}{?;@i|KZ6pqC|J#w&9@uCJ4TFVfG@my?iIks~y%!HNRVKXp`z#jyimAY|cucp9iwB2{-VCJx3? zh640GZRa#VSv_)c6z*FcE`e7tbn8A%eQL^NPDGfB0nkHG=3ALD)xN`!*@fo{ulVR- zL}xV%NP^rDIg`khCg;bbIL)s}po~IKWiT2i+-4trtYCi_wPgo&z79PiRdsDhZ{|{? zYnd+klBm9q+L4_%49v2||5;w(cQMk{#m(RHE<`b=^PCEZP1~Iz@9E>q;drRc>%@oL zi9L|NQY;vzpjZb|azY(?|psok5! z?}PCCDpR^Aq_OY)hH*6z9kv?mtDsw6f#oG*FIK4o#a<&uQxW^P3B zvrU`Iu+6Q3!t*{GcruOR?M25pB7%)`yKeZG+P7h!(_^agFNsGk4OqyM77SCUDfJ6i zFsi6@jEaf|QTEf%=Ds&x4JN4R^-v|}=S|Vf$7#WIN(RM6B04ADqNjq&8ZNYsqdv_XBwFb87y- zpPf-s6uk48v~uiVoTOJQi|4l`R{EDe^!8R60n=NlWTy43?5+Yal+zP{%le38-z;Ig z{$c^r6n%_cu1=BN>?TZqj* zE|^_xup`#YwW4eoNm($So(S(Ebo#ygu$4sUyN)TIs`zq=COR6Q(-qB@G2%K-apDyY z<@i9oKc*tb!y&SxI3eOj#THhHLtNS{WPIPZOzN%+=LeUG79w2@ z6i~Rwf^-@q=`l}>PHZGTtE+wJ8|A?^_{cE|U=^90`jc#u5S7J6g-bkHVcD%%u_H2~7!8z~OXYz^+tG0~Rem1P^ zgYk#;PGDyc@^pt!`p9>rYnfRS{IbNTfQY2+lg%M(5V&V(u8y!!tsgJ^(0uu0e(XPW zz9Ep3N_wSrOXVXgEUKTca*?F+!_Hp*TrX_K9RGLDj}m`1GDZet?n7NRs?yH=vKhLS zlXy0y4j*9y>|pK@q6LKub0<3SPl{exHv-MZ@f%^pxRtOgz`JID&HVKL`ZyHHb!@qA zgN0_tV6604&d&IHdZw;GPd;eM#!-tk-OqKOL z9a+uT3x=C4@tgLlETl84LF&X_>}COtgZ!22hiJi3?sxk<(YDkY0njkCQ`^+T?)4%F zF&nq;`TK=@VqjnGC!Ghhe8b2zg}%&+B1{3aROSgO&(1VIa+ZR!lniv# zSccVR-u1t{qFEqq{KV<5>?O6p=1f)rAp(Zoo`ibs0*Y%3+GA`?cXK^ z*}>@}dS{Xnd|bMY#d)#&;rc6$s_4rPAXHWuq@&*o15jZQ&FejlhsC%FG#DMs6m#YwV$r%@1DiJ zI%!Qep-zmDjfV}x{w*`K&=}ftiEf(z1PE`TALd&3SlX##XLr=FaFU~7^q>t8C0H1( zeaio-;{UF!pZ@&cE9+)1jAg4>-G7w^&aaCdDQg~UpyxJE1?~ar_FL3~afZ;jG(hv#R_jixgw_83pY_pEfcSU~R_c_vKb=?7IK^EG_fqq2PTF>NJb@&+_ z^sd)J7&@AwH^ApuoWVSdn*&u79qbZ+u-FClvK%%3_CKACfa8g{2#bunDU%|lSKh1Z zNas#h!1sM1JE)F%pwfh=Mq!~zLr#_aHD^7ehPP#Pj=&U8+<=1slpWQ4P!8&wC>L_d zGZg7g4$u8M>J`kX_Y$A{z>xqs#de3CqmO-YHUz6)!DgO~6xMiqe-xTzR3W_AkH8D6 z=-R24Hp++Awl`IR4xM*0&FQ1ueJrM{wK3&CUef7(4Fy?Ps#5aiy@0)@>K^VKLJDQ+ z%ScDFkWO&2e&lr|Np`k?DfOS+E+KPWW~W$4YXN9wLpiOYVaBxF_*Z&EiKI-f(ka|V zmCtdAcg$1HcNo)(3Hg?*mB$$lyZ4tnRTJx#fq35n`%|PSQmn`JDyNmHY=FN0p>@JO zB&tkElaV=p4ip@AmbKt{#;hPy5Gyuw^5h1FbK$QoEk6wvpeU&_#Nv$fXDF6O8{>-z zi^gRSvM&d+I9v9cN~*p=5x;Y+i^TODw3s&aki69{U3aM;Tkj3w@{5SG2XU7* zGD$Id3WcS6U$orrLk6~-z1hTapnmO1DkUF5H))*4s8VQy_0rYCr?Q98DwHU;Hstl( zWzF9iYMh5cX1HUrCc|&}O0;%1qON=_e`VH0x6@I{qVVuXt=sII&2T3t$;%rZk*Z@4 z19(dAshsM7d`aYdjL+${2N&*}#_eW^qLMG-GfXAcjzT?Q$|?-BpEP4!tu|*JclLoA zcQnvh1X7MdZB?M+dq8Bl_pznV-bMBT7uV79*2aYC$Zdgz?;ssMK)ED9pft7n?{GXi zQV8q%$`rJo@8(Im60q<-b1mG6u84a0rtQ48;k@^-lJRS4lN3>lEnWeyU;fwhGu~v# z>^ySkZJC9A6;A=-0Kd~bS4s$w0N`yT19gy{7df1_lu-Y<=qs&y$W{2>O_k9x`C3yq zdaSQ+pvUgV_sInwq^o&z23o3kX^QLXVX9kt`$?Rh$Dp647j8hBb`xKIH275dQJ_aB5Vx!`S+GJLZG) zFUR|rN?*F_aGso#DisLi|DjX>|D`+}o;*rkUDFKCC-!H3Wc}JIOH0TN@kyItrYp7- zOGm*e!f>M3m{#I*HS6QNo~K;@X8ZznC~rNhvVISc`+Z-GdFh)k5^To^ZQ1OoF)+3f z>s@Ol`lgp#S0v5jL^Tndp*eZt(m1kOh8yEhb3NNLp#|`r@Y=S%9ls1J>zAo z#;`?YT{T1($!$$AK}*b;-7?M~jU|Uvj6Ljv1@xvuA<8;IS*lR*)C9QRmY&-5o56n+ zHUHiF`9nd&WaJ#;wpX*{0zJZafqlF*9*1dpmqeWolOo@6a=>EGiID$sTr@%m=`}DG z!e8GIsRcpGt5jurq--DXH+qGSQ_8td|2cENi-DW^7H`fH!(1bzft=_Am*2#hYew+Q z@G54t&GnYS()TGU%K~csjEp^W{t{Roz?79B`hm>EE5$BDTrAm;E*MDEqweNtz6i$2 zs~)sK!(;h6Xv@!71i*HqRGWoVDQtv5s3~+Vd_MK1@r!-?`XLh>`}g#Eg)-)J)QkJ3 z1WMvBv!7d}kdOxQ71mxc9Qw~+{pahz8%|Y5Dv?*kI_%f3>Xq(GJe?7j1IC1O!Q)io z|JeA?yZ-t9QEX;sZ+EmY7jcKAj8zA2Y$>{ zz;HX@M`VU)z{Ef*V|Wz*!{Z+c->wjp>57tEw1p?lS-{IStwZ$_Xz$S$y@4GpFEiW( zH5Z1)ncZvr|Ni_pjXv$$ClF2;5wr=TnK1Y=*WEiCg^Fz1&=M(Lly>a_K-s|Ij$CO% z3VyLK;J1#2KL52V(A#u}Bw$^RLK`#Wer28Hot1~%WX+0@y?nKxBLHwYaM=_U%O!A8 zh<-tjlHyA!-V<+Jhm0S}g<(|I!L0fq;}?o)`3^puiO7aXPVJkzVP_=d`e%y~fOT$S zLkcsPco&en`9tmhbJhNjy!ydyP4WA_ekF8Alo<2n$k}}PvLrYW;m}<&1VP$1W32n( zFfk*CvsNVEEL1$4k>=2^MLw)nv%(|U+nYEedYnb(Mx2Y^Oe*kf$la37nIzC3*%`&Y zRZ|I<>ZhUz+oUevS5+T7xSmox;wd}rw3g?Dr8sF*bp5J+&=GBJ>{WN|y-plMrKJ5L z*V)-gu~~M4Q1%Y%)-Jnc90*|vYpA?GTwV=(x=wR%B0OLseK^P zE1c+{$jbX%FKzIebFBJkd5GPC4-vr7_qVlg6lc(RNj^`tG}3BKywD6)CmSW)$5g?k4>}v;vAai2k8QvgLU?=)iMhjdJ(g*Rt7t4mATf%?CpJ!IBC_qdto* zF6KHp9c$)8S|)<3Y(BMiooR>2&E&#BFmWj-_dwA5PU;norw=FMr_uBtOcQL2tdl_Y z!}P(l;HY*tK!sXlO$In7%F@3-ZYm+@m4(p$nS5se!O+vAjXpWFBorvNXhG7Q)ZzGw zA<&Xs6pP{K!q(9^|JTQIBdsi5!CEaHSR78W7JMm8&G2v(*F`R=4Q#J&96q#=fa+j` z1?PB#xh~4daNoKeWRm-rp+z!E$444&!6F6lh(`L-+_CwDTdxGG(zG|<86p-g`)1n* zx+=4%McFPBSom@sT@W6OCAN=_Pc@x?&$uedHqNxn8>*bgy{blN0u~msee13t5YW0j zn~sq1AcTr;L!I8!6NN2R|6UE2fLUeoJPU zT4|cM`4`# zi4uUm);Wcr8$aj5$>z7yS;1ll+R&B4;~*%mp&4Wu_9yUy|4W@a;Y3_lFuqU&$Lqf+ro{x z-EbPvgD8i@AvlI`vBoz&9PM75WG!STe>OJPkKMO5#J5zUP7amZ===#%MKKX8d$n@n z_(MtehqkS+llYSsDqLUJ1Ycncm9;YH3bE1F^_;o3@l?+3fMUND;+xcd1fP$09(Pr> zkiiKxwG~d)w}z#!+^ouQlB4Dv$Izty;OOnafte;csNC!LIbY?8vojm z>~hA@IpI^WfWveub>+Nb$(J|NoI^Xm$mR-Uhl2&O*uhul&-~>51pPHGS+$RQ1kma4 z6U-f|I0ASrJK=aEiTI8>-IPH_2BbWXNeh;MzP(Dm(WbibEUTA{I$m77iq&Ba1<;8jbwbv9Fmwg#oeX=ZD1YpV!>B z4()F!Qtu49(p;u=Pa7GI3XnmVi|0D%?mu4(-`0EORhQ$8 z)>XgTh??rq_-sMR5ta~q(sQw6ARATqmD%>1k=}^G+rPJL<%TEsKI!vAxM;2@TR#_S zU8uF_NoJ|1d$eCbUR1LQh4B}l-}kAl&Ngq*UC&ppC<WW)A=8_7%d0Rfc(XWDyiI6qc>HKA zTiDgVeYUUkiIHa#pIgB92_3pLYT4k-TPK8?1v;xU?&}zquvsn?^2GPGPmA zwAQOu;f?5mH?i};Z$5Tz6av+8PP)gu3RPI!rE4Vz{QvTcNg6eIb$i&f z)-?~9N4z>0)?f-MOIo?8@uqFOFMrQM5TkNo>Av4n)#})1C(Jp6njw#h*0xORy+>-; zFY#lOSf7n2Q!I`EzIV-o5EIe}l-zZQiPVd23mtm;1y(GK4ROuSC?FOGdrQYoOHM{k zId_2gG`Vl&>$&n;DsXeR^L&llf>W&q3MX+5;V$Boek+?OPm3R3%zC)l z{5$;kG!X&qgn#mw6CE@3bd2$IS+6qMwP(S=JLd1j+qKALH{ZKhPr|K=Ld4cEe>B)N z_l^stf3aGxPWuri-i>K&%d^h!xj&JUXd~$a0Z-K!i`|n;XDyxFPBxEmO$Y4X!SEWC z)fG?O8J_i$sze@~GEkTi8TV-j!F6o(<*kkNp zJz4LIBNA#`t@Q~4ZSjTcdO43%#5kj?13#-4IS8&IRhbZ3c5=SUCMzB&zN#$BYV#Wrd(~UuK9zhU z?Z=w2$EUxJtbfp1796KmV9^yaC)KS|S^T;9obd^Nw{o}u9nxJwD`X@OhraA}ZbrPU zQ;lmXu_zYJLOonT!R#WG7(Ojm{Mz7L{$!5npo2Vx#OeBjY)GzDC96iIWLTw( zg}qzC+KnGHI===L)T!4WdZ)cEepFaELSEw!yk9Mj+CaqYHI40Xp!o$$57(xcjb7RhvT&?;S=K55dR<%Y${#v)L*+?JU>p-J1<&m#zOC-AoN6?Gpw&9>l787m6w~Tny}}GT zH{p4(9KVU<2g)s?>k}vxcs&M!punu)aOs_wxW8dFhBJgq__b<~6)pz@5}Qr0<`*hR z1Dh`qio>gv_CgY$oYR#It8&c(x@VcPFd=&Qtd1O$q+)8{+UPOyh5my2w6tSvF$l`u9zU%VfUgBR~A5WbwQ5oNp#dyYt_mB!!^v|U!hXS-*atG8$ z2HFqR7AG#OKAIi>=6y%2qF*V!_x+mh+tH+|g()tjtVHzwsfD6; zWc@<^z4!1~cDyl%mT6^OSP`C86*cb@q0*Uqt@w%ho4M=o_@?mz`y=V%{8XCSSG54z z#UCAeVsYjT1F`!ZviwZ$UIX1~Y5cP*y1OEi{IV*~=l*>So8i7|q`H|Fwp$HIOkckm z`o77K?JwN9MukNx*hSxW#gW&2r7g&16|J#2bvP_CCxFU@IjN`$j~oe$(LeX1>mGV3 zn-L3p8~PFs)dudJqF9lMT10qPm`wD7oOH`R|5eKCdu9gro6gKEY$%gR7aN0f?2RGC z!T8WmEf$h#dSN{#wRmg$Ua0$AAd@4>P4c110ks_|{uNm+{ctH}8!rdX0n8ou#AIG- zo{uK;nw|62kEx68hTWjYw=K|$MXD1cGlUG`cE!7@%SX)c%8pC7!x@L&14>Gkfjs4p z%+VDQ;?uWo2ZFc!j;YjhenTqh47&ID24&DH>!v`}qQzG9P1S{vLTIRAj<&WPTX&-P zvPJJUZJZV&Q-cjp_4W@3D+H>op{%lItR7{K#0{lL)6kUXi(7B2&FRlAZpc#Q=WZyN zQf!2S*afF70e#$ScK)-*P-|@fJr^du?`B!7rtl5=Mg#Ln`%!bhCtXKBc zRQE9Y_DP#?w&3ch>mMSv5+D~~#oH>BSk#^6Z8?>TR`(Bva$Krx7h!TRr`$`gdSo#d z!ZDk}Dq0@>cnvXaf+n)4;U?yG#=i+Mj*Wd*TDQotxjyCqcTyEU6P=>iou-Y} zQbOGJV;}TKTt%%?JI^Nd1W?F6sq4Pf1#ui8y?# zIGN=t@eb2`GSy0*j8hVISPZL8pGA(Y#f%6^HDb&mYH^vS@AzzB&NY`*aiEu#X$CkVk<_aczyg zR}VM->r3a#Tz2TpL!ysyfaY&>a<$czb7fchNmJG_zDYNFmd4xj?{R-hVMX=H)xf;p zm$^M~9wOhoG0iFZ+rzDccytj;x@RPPUG4OU zXY_OG#=OVISg2rXPP{QyxkF^Z`p8DPRvv@qIzoViRXgq6-wS_>d-XAZ=EZPvCpM1qi+U?y{5lg2 zsk-&U(IXmYLHyXzeAXeh7h?V{(n4o8F2YiRJv{)3e%=c((x`V}t~lT3KkEu}zgtKz z9hyV)M_qLecBJ-9e}Y-_R#cnHr5o#lwjNh(C6awCuDqN5Jt(bAgzIK~Xf7>gRt+{{ zvN@;l#lbd3MAb;CuZ+#i*VnMSA6|5cH{e{Aqw`+x1S#VlNp;twu*B_@{)%IF7%D}& z0muA{klzoB=Z8NcN`ENCyg+t!D>TC$cy>A0~s4&pn$@OvB?r*6?9h+?Iu z6P~`$D7hWyBGRymy4dK(Wc9V1r!Q0u~+u-z^>VwSNd`$sCZw zt1P*Vq-a%cf8V#0ADf3=SMM2^3o$m)3`~IUY0t+q5hjm>QXgC|MB!HOiu*P_+uT|A z1#JA4*jqb8Q?f9k-HG=(&ewiMj8}_lYpn?~4dPR2k$MMo+g{#OW@CHPx%iONPYB7zG z5lY1ZFbBc3I9OFlCKVt5>^KdzPPbD)c}dG1Dsz~B`6B&dS{$;`^9jbaF0OXoPc<2f zwPp4*&mpbB$(P1pct&I{zD~9&XP|*mAu&I#W1j!MB{5KFV^G3}w{^xJ1x2k6^4pj& z>+Qyz;6+-YoF9`HUIu8VGAfTI@7``i`*1%XT`%q8maoDGcgs{9@MX*N zppyNxExjiep%0Ybp=84Fvz^5{&y-ZSKEFh{lQ%B8E?%K5z9hy@mxU(cvVSkm4iCS!z5^Cg7It&faU8n7|Klz`I z`R4~qQ!K$fl)<`<^aN@@Y8+L2)u*P5XQ+mq?zdT>mEjm7qvtV~|LZX8>b9_le16vh zgv%I%09yW3Z=>?oOwPQ{W=UVlyGfy~706Y6j$v+?uqP9&>s;1+md|3@qFlfm(YX;V zLy|+8Nn5U!6-FgyH?I2c|2pme^5Ot8wzYgCH;KiYwwaR^W8_Oy5jI6$WE69{Sx#}% z3O=BQ4hbwV`q%$*rbX{EWX{Io~gOJv7UQBROanEM)AW z5YaW;R&fPc1XE+S(Go$o{##%e7ka0x#gzH%wsESG13H|`UBkLs^p__ij7j>)SrdW) z52@Y%>+Jznc}2}Pa7sOJoAxJ^-?aTpWQ$E1sqxJ_4Hg4sfT zEu*DYTP-aB+obG~NmbFsn=gm(({LQm^Hu?)q{P>~vR&!>zTU=;mStFQ{v;lts#KfM zGH`-(wKG+$bJ8q#hI+d!jK}ttp*MY#!&JRrx^a>D_V2Z+F(R{3P55ndX^J_z_G)QK zxcBAart!_+cSM+{k4DCr6k-JAKq2W&;TE~KP1q>ABuo$5MbGxj_173oeBiKHW7|Hz zZZ$Lv-)t>X9x*pc$$&Xx7njDYtJKSK_(k##PRZ;KE_3oLmEVdFGru^46D!zO(67CP zBAbK910x7O#V#P=cYH|nxDKJ+Vr`-b1V<-e=s%X}N?M&l9qJbp-jhyi6=ALhc-?mu z{^+Sr^v05|8E&B#hDL@A%7aJK_WBVXGb&Zlb^xSyTct_H7GU5af27Yg7w*w^bNI)H zwCb^qhvREclzg+lx-KBxXCyx5YngIw6Hi@LRMv%Ao;FtboqR&wmKi7&<}3e^Wr(aD zVzOb7=$a25?RhFvZ(rmT5I(~k>GQlJyQ4(?7>Pr|ZPL-tT_JW#J`_z&jeA|*yfi5~ zxkjol#0FdV%#wLq+d3X@wjI<`TlH8Q)fWWN9%I_N9PBaN>rUAu#y;vX3_lZmjEJIM z=nT|vUpyrzj5)Un@P>uSf@8zdXC|B?;E_BJyH~AX?Yuu5S5<}stC8V->lm)E(n76} z?kb}!u#*SbMQ>6k@pTca#JC?~e4YlIWY~rHuFM@f?C!Dd%(II6nQ^+x1U~Z&l_I1s zbfkXl|Gazi@0GzJj@wVy<=`z>c07^sC*Mq0Z{Ju}9Kmf5)(l9Ysmm?UGRw+N0SGu* z!=6>nG9D!qc$b7xCPs53g|rKEu+X=HtB;W_Eu6AJD0GvWzmdFow!MBU$Mz?VXyAxp z9;SYL4e82nms690RB{MOv4V0bP#0j+F552CS-NY_^O2%wB-XM?g10urQSTDQQ^GnF zbd)T6$7xPG6xeOO?imA-tsqmX;e-b5-fbY)@srCyNVu3vlkOOGH0KwLVnz*zZ<84ev&xC4R2 zNzvAu&rKXHeVWUwJ@86o3-0~1s#8y;JDHGFV(1mGWiiEOg-@8%XRwur*JOQ zdv-x5?zd8MZ*6XLBOJw=UjXbxEnAqjXAK4Qjmw+q04oRF)_OH*7#5`bFJ~cN!!l6rugXfy^|&4c zUh5cK80BVeZyKkDtLX=J$BB6tfnU~pVjxC)n#JNsF{O?c!Wp%DrkJDB^{I`eTUddA zdyE6HDoynUYfz?5ARTF|&zfv%-({_K4zDIjiqN8tK{uj87xeF_#7{pK8A>vKe(0Wy zYr~(WF%Z|}`j@ZkSP`qYor1vl%BHxQpriD>mCdeOVdR)4MAE zIvQd?nAu6N%U;WK$PnI!2PXCyndgK?A0 zoj+D3t&rb`#6j{$F{|SCPw#bB?2i8B@MB8zbXk!))}1Nc2}TCGq<(PKNt zMVEg?fmWY&B~-i>({P3B*V0%XwB~rvBZ((uX9Io_p2+ixsR5oGSD<2rPlqrnpEgU9 z?kbPR0vASd+#I)@tzkK(P`wgs^EsOn?hXe%ISo1?uP1sAyc=lEUz5hd)DYLv(9GgG zw}m4Bq*33^m*t*dg}0u1O>X;Tu$>u4#C3-?cxbBV3gFOc zBCO#KS25S`fZxAh%Eya&?RA}n@1=C{k!B5Z#cG$Q3bX7=1)syMg3cN@nX@F)Gp=JQ9|k7Sbj~l zX)a%Z>ez~@yb`Q!Z*85!jhV<5n5!W+uPO~vzb)l#i7x~6-IJS>BO(|68rJ_N36XE@ z($UEy8C+7wUGD8hG{PZ0j)2DPxm%7Rp)Td>k|pK_D@TakRKGe#Rz+G+rz-hc(fSfX z{v7%2u&kTKEy;l6RL6MpScfJ$EZg_K(I!6H**=e)o|~CG9l{|FN^eoF1Xwx@o^h^u z+54vL_Deok3DRT@av7?Sg00!`Q6W7jb_w^QU^R|?-NxaR%mr+WFrNTe%H` zdzHgx-}m{WuWhO7Z}~26{kaBV%}?T(ZD~`qPi9SD_M=nm{O@{a8$3{}A7XgXlC-Fp z$+#D_b>4%n`?Vm71I8mBfW`0oAO*aLv{I7YotHLbKCkxC0`$7m|3+Gy2@sbU{tzsExK4?=$?My1K=I2}(@o-pgBGgu$XLD+rcJ2kQ-W830SPMA^{wlQ^ z<{ZmzEhe}Oe|UC}-R}63sTgM=>$zhSa^UU2T3x8{U)6_NLi>+iI~-viqqCx@DM4-> zj%@i>mbD}`9`?Yt*NOsDOR%9>hv4|em`err`5t#K{y5px^nVfeUSUn9QTs2WGFF^H zh)ACCcVpyBM3pjpaJO`AV5NbL;{i!Q0ZMth;-?_mrw-% z?|l2Z_Tm2aKH4Wa&6OwbyPj3GZjtx)O^X^zB0jm7 z;E4nLA>iPR0CUkABl3V?pZWH`fUxBZc}f_5v&X{p%_elMwPgsrnxG*Bl}0P186?`z z2TkR>F5hddH9RNGH4uiP6qHdnA-$2;^&8p#QAeA#qhS^{%biuiA-_X27nz_;L`|$b^RYMzg67(Qzic_EMVjT=ku*!_Ab?|VfbXn#H zz*?v9c9|F@QF=Hj1khy9y_is_D`tt#IZVGh%rc3(_};#huemJRux-(w8L_)@(0uvq ztEr07tia6k1nda!4%bZi2bJ-8-d6vd2^wlsZ%GD}dwn`n*_Ab)ZFjXFtS$A2lOlb# zS{WHSx;Z%pQqSDGBk@jnN^s4gd%wf&y50!*-p=je96?G_aBnLP0ax)eBIi3zIe`*` z4?KX(6@LUX3#r^k+Rm+3Wm!wNd}810EVG@+HwrE(u~f2YKsjO$TFFP7qs3Kn#2y$B8(kFnAj8@VZC)N^#?O ziHVQUkS6m)k6K+|(t4y|;&LyS>flMR?HxwQ6Bg)0kXD6Hbqi#Q9|CUZhPB#z5?^bOqpV= zRShjgE9U@g)_DzW#pFKM?S?m93qmHW`!ANyIN~0UmkRa$R10rB(ttKa^T^X`s z7Z>X*NbR(2{GVnEo0=a?b|qeiPwZthB$5&ag8zF^IeisLu>{I;K;{WkTyx(b2?`3# zjdsdbtdwKQ&4GHMC2b{EjcV4u#i{DLAR-La?q4M;FoSE&k<1Ou0q_V!N-0*2pMn6mS%p^%Iz%*WK%G~bk;{Dze4A@O~Wy4w0zT0KQ!k|eT3 zN;-xgWy zlhZrIk3Nah#Gxg}jlDH>P$0fYAN~3S$>Ahj(|yi%aKx70m|nKwLUy<8M1 zse|YvCmeIXV)}uc2KxF37u8dx-wy#RpdqlL@FYqH9+rm-q#$<*1Td%~qW^rReVJTp zgKujw-~u#)K4FA2detKHeAVwYpdC?P(D7gWamN>ezWYzmPEu!6oftYO`jmj3u%x6l zTZ>YeQo1RBZu$H3%ZZg&c)jP74t}$E-S!n}RZEyWs#LhmdFmJ{(r#(2Vb43?I_huC zij@Jn4$>8^JGTig7|E;A7ny#n2$+!JHc8-RZjn{6a$qVaiq)WN4T50$Wf~;Nc>(Oi zNn)uQ1ZRF}gt-D1OCe(@|kr*Em?wNm3z8s}0lc6Y^(XJuLqFQh?HS_M5M z-v!k1=s&E&y;ao??q5Wi>|e36@s_%|djt-#3>AxXuvw*Q+5e3HT`1M53XiYMa$V$W zjd3WkzLbQax6E*$%}JQOV5II!jOTM3O8XY6i=3%@21#Szq2MlM>f*>9(bzQ~&dOhM zB_;XH?Fn@Oad8`1O7BgoYD{VF&LQ8{Gal=Z*;gQ=1Q-z z%!AUXQ=Bj23@W|E%%pM6BM= z>Y;A}MRtg;5n$`_)T4rq*u8quRkUwO=&x$HThr<~+wNL&Ly*QR`CmrBcR?fM+C5}F{y%J&PyLq)rm!q&VO zb)&GUw7_YQoVlU7dgj>Z+Jt9T;u4Du9e~FQr#Xe;i+$d9jOI)man>U825+lHyDa%j zzO4oxt-6(!Ugch)vsNL~p~Bd-Y!bEG*4ZUpe^smyT55hXDVg?p zsMbWb%wtY+O@^39dVE*ZT&(5NG~R{{msoXhPMr;HQ_wVc!PDfU>WPYC)PYbzGt?MN zVOr*=wV158S;3=5Bw+Hp{QWdlJ8d$IC~q6k=<{vZ<86xs&W5A3&H}b_zE9~xpHAyQ zF28MYPwp=KJ!nyi`Tc5;&bcJQ5M)4##(3Y{Y)A1-`PJa1i3*}Q*fMz5NaO!*IrC>+r40qX#_@4H{LHG5bIaA;SNXEWs%k^~YQoIj4P zpgsVGtE_fFS)*<)mTC6%Ru+4in+s$~h&9g@HNO^lZhrw`K<0%t8UcqYOGNvI_?Iyq z<_FY9%r*M@w|%7Ml1vP-aRfJuswaT?0uEsgAx&htg-v)mjk$%9u$K`g!{&~$|XkWZ_otTl` z^Isp!YSd$$8!}XtmK7v)?kh>Qm}Wc`h({Ta`Ba{?Xmsfu(ewrEmV+JkIgj%?i)glP zR=1to^E3)r{T*QuKi(7R`UbD`$6;by$bft#3h5i-@stX%LRRu$Q$|dK>XIw1(I(9n zX2dsIXG5QiZG(nToF?1jCyD4qE#rA%yn`3|l;<2+X%Lj;o<^Hr1?GS&{~1XI7S%Y{ z-dH?oszk2eNvMDeKs;Ut@gsWSh#%3Tawh)DyRZi_k2Y+_<`w&5KFF&VdZ(mcUxSuf z!&jrvJL*SA=LQ7Lkt!PII#?dN5%(K%hs5X;`?GPY0u8$x8=JfCXTdE&6{E{YuB~p6 zW)R=~usV%D^vmYCVBr|GZ#Ij%gk=k%S=-h~0>Z6OL*NmqlZY&3-qGz@`uiK#zd>*i5-wV;EP@(N1<*rCjcVb;{Vs zbfud+%rAH$*J?aSNPFOWEzx2htz zo5#K;l!&XQ?CHC>qKp^ur2bw)g9Kn~&u^X}QTeaqh-QO73=~A!(Y(Q+_cDe_8jmw? z6r{|`QDd)%`b$|fvwO<^yDynhPTe8H}fseE7+8j8^XSf!6@Kz3%#A8dQgjYK^{G{%SWp%CLAxdgA{OoA&CLVNJ){<1Ae^h792R&_U#d~4M!qu``tf8e`j4)* zieEgh1lnA^v35_U+GR`M#6OytmG^2bQ!3*4IoNfB$mRWbwU34`w_gzV#3ypPJz5A+ z-hfjMpV(F}BL5dppbtLLle2;;7TNm8){XkZ|n@KGh-Bmlh-JNpYcF@3JfN5TvpR94c4|AHw%!Op1jz#T#2T&%aMnFe=J2FRQY{k|#XSd))F3hqAyS zab}kX!+EWOmidb(-bGV|efne+Mqj51n!tr2besx|k?D@s7hgQBTuz>Y9@bi#YVXYr zgex3e;Yz!0E1qqdl%a=t)^FDsPlhsA+oB!o9C@y@tAf=GUl_hM3@%Km6TAFj;k%l$ zhW4_|iT^GfVultu@W^JULkuRRZ04L7LR;zfNM`q?X5e!5>w&7b|ePd=x2t_ef+rS0?O^q;QCa4Zk8QyoYtck&ydfM02?~g zAn7Lxb!Si)NcRjQ^JpKz1>3D`Pmk`$ILUylxS|G}?U&M(oQWziETjM6@*8`b`4Jz~ zWhM9?^nC#h*acZ<%)C_#zD9JUW>6ozTA6OY{jArUxTWSTrSgP!CIWu03@W;H zrBxa&Ge!qG9^sijHMZ_I_MyoQhK7RYDo4DvihwqdI9xPpj7%d%|0RPyEq|JAYZ3Fs7^_dZ z?d0H~!vYM25$Z?>hMmp$?n%))7~4OaO3maagn+PoWA zJ+0BeTUDl&5(;`eD5JXj()ao@g5ld9zun*=-bYQpA3c7cagcql^|j|0oAQKTJi+>_ zl|4WY-1zt{>(|1J1GN_wnwf}&+nj{97u!#@jgvRO@ei$9@4GlN`)sJf$kPau70_i~ zmENY~l@u+5?=wmv1Lmz6yD925_KAaKI;~`dZxQ?*i5(>|;>uzvFp&n+Q=}r&6#V^# zt$@Sa!$zmk_HAFwZNcs3#Y3&y!u9C>>9z>Kx}uQuF4j!fq*26@^xdT;-`Ggzn~g3t zQQ*ZsbNZd4(fN|C1*Y_2HnFv6~{eH`*`5DjE==QNNQ1)TZu5-H@wD508iknz)5FRy6e>4ZkNk zo{|B0EMO~cJLG8oaS^Y=?;s3}6yG)(-qC*i&oI7p0c=EvTz|ea6Y4m*Mgknr> z@_CWTwasrAwII0fG_ndBU>d;v-=(<^ybJCIX$%(6u+6Xvcs^~*k0&49icB-Y=ukiP znTd-t@b>vRM6_QbsrJNJ&;iaM*-+Rd+jAjcDJ$yb$L{=`i`9zuYiW)BO#gii`ugUI zbGT6*;=H75%e#UoJFAhM)J2qlXiM7(OtAz32mn>lS2eEA>%l_{lWOG`8ok=8<6Etk zf|lRs#h`0{*L&$;kYkrcB2%ZPA?_buD#&hBr_=7K%kJ0erGV0FNr+B&_I%_RL) z&y;(*jj0_dUcHFf4_`PF8-NXUNWuUK=NH7pf3E^LyanM+TF^Hp0t;tast4I-vSKrg zY)p?lV#85>W4g5Z57SqG-4L~v>ifQJk~yu`5*gBlxDOT0|A)Eu{vR4$O;jWYj`j|N zMIOL6v$*_UZB42)7r?^ck;WXs#7$t|ukqI4EJ>z6KL47-8#&@rNhdEcZFkLD-6e(#!()t@4R>QawHHnpBKSs2c%WgVbj(48v2RrIASrdU8g&THJYb*U}eq z!dp&gC?h`i1iWxG|2#*!;OH$!s#5-TpPGOkC)8%pc(T%N`_M;B>(D)R#F?D%PSZX} z=4ECpIrDbIRtj&9RN60|@6)x+hc?~Ybp`%N-Rj{uD_Rd8o2!Eyw+8!=J51i5+Tvi$J9jCmamQbbt$nzeT z-0`LFW8Fp11smYgK8$t8#B? z2oRlL!{)HMcN5@(7y1kU{nfmHU5OcM@Y*5or*9)b#RSQwQAJw8nRvlhW@o$Gab%MW zBvO+lCT=KI)koJrU$e`hC;%SLJ$d|tF+!7dFS>Wb=CBWy@6GRTs*03>cUNqn`5V?L z=|Ay|-QBF}nyc1o|8Keui*_8k7zc1UC`Z~8-gic+(1Olzza<0_JM4SS21tuG^;;>} zVrQKIl$13{dU7HU-#X3d(Ot67hSK{6Zbv^VM-5AS>d?P?PJt9ZmxTFgj<7ja z)+LV?``B|+&OShl*5H+V$!P3+TVRx;ti^k4y3U}Y%YNm6dPmL=4OadV6gg|SG9E$RE&&|SXyQM2Y27l!vvo9)&0&2K^Y=9a)z>7y|pYM zh=kEu2{3C2z$udUyh{G!LPulVDgH14!04}S-C*paeUy#9^7{v4yHC5cR7;9gt3hNV z%YB-5R85*Z;5nA}JV^Uwhl0hBA9JuD)o`-pwoDKFSc$F9%ou;-6u`fL?`2FcIvV$^ zrPdBE_zJsi2jZ`0vVWQ_?f%q||HU&Q9cJGj+u%xNA-}d1ynCNhP)4lQFH4-7@@+CE zt~&a4X&PrLR`1_pPlsf+*148(28X0Iu_Nw@PX5})f?6ucsOjk%J|Dc0!=1o|5coT2 z=)YM%O*^?&cC!yYlSoXIBOmeGfBL0UJATh6(&?;nVN_De{8^AC6c4&0Qkr{`Zr(Ba zy5#};$Kj3n70{EQCecP&4~SjHcDhtbm`z4co+Bh{W?8G?9)?GdzA9Yil7r(iP49bS zYZAW~K)RDro^Q8`pJs8O=I#Db-bjmi9=$a$feT+B6(wJy)!m_+&YCN#waJm!rlqj6 zOj~=2Qt#pqjq8hAFf){yYu>gMXeC!uRgjS$tqZVVLHM z?Z~8PlJWE9agD8Q1VXN5HwP#lHgvd^1AH;Fg;uDs3;7L@vV9j?H>;Fb5YrWNEg+;2 z+D(E9F}OhBFnn+f`ut&N>czp_0kR6x0tJ@WS;1|>6{>MP2D6&xTBpeQDr)EBw-`06 znmu^}h3gB_hHlP5%K9ftM|b9Iwh~8v?u-KPlR6%cl}im+2DJl)@O|9kjB~C4npfWH zcx-0}h5+_M!NK5b+mTIjmCQsUu${V%CF|bn`&Qc!5h)8Iv4b;;l9tE=L-)OSu+vfM zb^e*ld^>wCF7VD6e%S?UjSno5q^U&z#+gl^gF;#^3|byB6XOuAR9*aIXJ37HDk}Xa zOBi)-qqn2!0KA*UfvK0epmYXb^ZcI2-{^GCtgwcUZzS{`<>19JS_(SbZaZU;935yk-(Ue)q}8$@QN8 z2e;98%_m#;)F9@i=&DW>4#h7pHeDK<95HLUHq>vgpK9k-lET%m`$~4+sGt5Z6<|Ks zB^QE`(Tt_OvLiprb3Hq_|C zM5F8RogtT7hQgPgS_H20<#!M}WW8QK+tca?6wf#c4n<-IvLVC?2QVm%5hTjEE!K3 zoX3*JJ`KCwx+s}>GrLMNMTFbK#A`Hj8&n*3oD*$!3Z0h{sP%7tyZrS@7Wzy*Zo+pQD4+4k>Vs0m*|Nwh|4i}F?UQhD{%2_2S%1evlT<(k;O$|Yn4|f z_4_FXSsYKs&L}e{)ggC$$IF6wk^X(WpnUk!j^(|k+Bb7M>;5&m4n8Q;Vf@fR%dO-3 zf%RnvIhU4hGsY&qkC?Qg3fA*|w@f$}H}xeC{7etVoO-V{dWG@+Qz6Rwqhu&!F6-59 zsaxo9l3O)fKiHD6$K?b(;bC)}`oo7)MsUdP-85q_{{}{CPSe{ z1gh4VJziPDNS6CFU@g6p{36D(Wjem?OY=-#cak29A6R-8#PdKcOM*K*nfBCj6<}X( zLeJ1qqIov7)9Ds8kW@6HR>_Fd^$1t)6|t>Pvr-b#LmEo7mKeIM@h=_**R|Do))eRw z3hQ)!{+3@p5xQLZXEJ|2{qE9@@xar%H%r>B*eDda17(-Z=|Cnq9g56n$FB1B4`DwK zlH4G`$UeT%`TQtXR4U?a!&smt0Q@og`xpc`>2n=!#Nrk&b4z5;&a;A)mJ{t}#ZrkK z!OT+cIXsVKf3=K2#bd?#VmYLT6HY9}yqS$p@wczC2fm8L+98oVl#-GI$3&T5g3F!$ zY_bM&i@Fv~o&OpXE2^zrNugEDH4PrjY}2y9v|yD=c^0*=yfi;HuRh!_enlTv9nug? zFCNr}frUskH%7cgxGC~!TGV26SOpJe&TmokqM=`NanVop zg4SCEa&CS2Zel#>QWh$dh(ossM2qKe7g4-BZPu=gFN=cE4~#Q$RaMpBr+SEkMa4q^ zKHK$g$iTjA&)&*k9C%z$jR78uE7{mpuvAO1+pXI54SvncVxx9RDPlvX8$KE%Rohd$ z_@Y!UNWGGg(VNT4F={F^?>I7g{`|KKjp5$UozNn-iAi-JkiE)w=LTyG> zTSqI!w|bdbnNEH;S83wzdyer1dxy^7Nk8XIS2jK?g&I?dS+(j8*;Xri__t(wCtv)` z)(6D&MJok9V)U)c)Ik4wR?YL5$cV_-;ST#|MUO5i$j0bev}3wJXGvK}MR?~(T6^(y zD}bV`67bANEz_kQ^7)uQraSkaVS-lD`j^=+_00Z=n+ecC+WTYjUNdA;a)mFeyET_F zvivvo7cCiYQ)J(*J9L`p>gr6f^<`4RWa&Ka{w3;{TD{JdIjdqk6Kk}z#4J|*NY`;T zQx0(Z;6bqLzt6YEPS-)MQ=7b$Ec#7HFQAN7@B-xMj}u0-Fwp^ZiACCl&%c?2REo-GwJw^V zR4*cE>E$V!oqCP+I*%nQ+dT4nD{F=Z5>J{v`Tn8sQG!!!#92Jb1MD1m$PTIThuN1q z%rOD(gAvc4)oMWCz82`+QX$1GIl=J_(RtRAJSa!6G#YY| z`A4&#+`s5Jp*bp7ZC+hIjr4UTc&KDoC%!ul_h?vBB{4hfL9CalT(xAs8Lq*=SW^<4 ziqbLFERT!l^4n+uS8fuK5FV+Qo^abBDbX$cv6~9gze%L_xn}`#h@X_pd#{d1-}S3s zeA$qmru0WVtxHbe!QIK`oU`Mc@5#aFO%KtC&&#@`i9O}{eJmm ze*|h`Ipkg5ny1gkO`G{heP%;|=ofMUN^hRU>bvjGQ>q{?Fdq~(8d0DpV>a1ml+xL} z;F@&Wk+uAdJ}=7LTV@?eQJI$jj=3DYcPM@G`|zwS3Sj1$wPXpwF?pz4J3RzKB;V)b zh%g{cNMI3`{qoiqy*6qvr!~-(Um}JY+K42{ifq;t0{t4e*;;NIi6yPOICC+An7c?I z7|tbzNO^vO@y{iUnGS{>Iv4$Rp@ZT5+%@PMddl(=iTu#_)B+*HC=ACs9J?R`ZO!J; zsLbN>x^=sdKg$cqBIO8MSy({8HQx~CfvNv;XUOO@YRTu0$Wm&HSBo4p6~b1d*pi*B zNY~cII=vlVTql8}rSIRo?Jlf0s1r{>9~U?Y*5TIcd?l>V)O_x_^*T#~c5Wf?!SHxSF}=0``^7taiz6!4MpX|VNR z=L~2B2?idODQ@}e9eFv}RVieT8Icm!mkj^@Q{#2MxOOuQoE5w3PR^J|CVKVKmeCig zC^|aun@AlUP71I=uisO0CQa_!=TryeT`6#4sQ)=JbGY<>C$<_d zkTGq#2mr|YQvX&Fv~qyxzc^~X`Q!WtFU zMoMjShcaOpV5JLur!cMawXh=zze-z~@d9bF*+?Zbm{wmr%?$9>PegL;RLIA z_On7}sJdhIIe)8EoDb@ zjtDZ`_@1CF>D353o48y*vAnrxV_8{@?7R92gIT@2Dpcq8W6AD0TvZn;5hel71Mvrq zxm&$EHR|_lB8)DssbJ<3`=ixuvoM^s-gahZ|ALbF@&-1Tj=qGoW&K>HAL= ze8rB@VAzlf7LYEPU(f}hIrRWY-alG;dB8CAn0Q8D9dEk=&ffp*aj*ja$2xB$^g^!@ z<)|%JAcJaFg$ndlGVyQ1TIYRqs%Vz)QzBKB>E!r0s)0*GU{Ap$60z?$YKxbpS8A7L z&Y?H)(>K%jiM#4BXwX!xf8iGdBa-j@AH61$r>Ut-5t_6(@6L3RXXv6YhdwJ75fV3J?i*pzpfxx&P(P8gP25!FK* zdM{Dw_bQwX1_upL-^z+baqW7$7;K*|CfKZKga-=KQgdl>FLqxIbfELf+o4`nHbmU& z5Lr$dLV*Hnd6#MhwMxHl=Ny=O6*0Q^>nj)){oU&IKmnS8jRW*fX>N)xgHes&c&>#d z8?A{kmeL}2G{v~*|Eti7QZo!iDIP2F%V;7qV(9wce6Gq5X_Km+Bcs&B<6b)9?WEDV{>><~2!#RWD@$cf>e({8e z4tD*Fj|#q)zwC?E+2hdMbtSz3lsY zs*}bol1x!5(IAuJkpLMkfrAT~dCIW)8PQB*V^z2_wu0~)Ugon(O@{=96RMtq!)tnzCN53ykTSX< zb0p`S@h64xrA0yaM>Fx}z@S&kjF4TEFZK6%RI~FO92Gl#x2Nro3fd4wqr6WLxrSoN zI)a}BR!bixWs6`*q&T*YS<&2CSCB^GV}{ zxS1e8UL^b@I{@*-TwJCF5YQ1>~_W|#l?*-(g!_MmRBb~2J^gl6!q7aCkyEdWd~QJrx;?S zi~JjA#|f>o9<>X^SM1&`?Sd!a6S{E-I^C~D?B#Ym{-sxZ;jTA&K^nRIshdn~!18S5 z5o42*M2uXY#GV=w^Byd{NLPucudrTi|Ie=9sqMwvU%k>!4wb@^G`%T_{ElYmfLLJ> zY$lxEsS7U*2=Jj2s?2rmeM$;T3h*#}jSEsZmR7kxnDE|Y^3#fD^j6ZsZO5fxCi!7r zMTw!Up;*i1iQw%HA=@5bNAt~|F!|u~?}D^3-IN2mmamii+@nI}~Wc zfGcU!Nbp9XB5{9Wouc5MH$Bf2+s$1ot>{PecXy6H=`sTw0&u9o z84{sz^toQ!tEue@E=Iy2YB1~_8_q6`fI|tsWS!=#}cR7XRdZ+S| zPq$@(^5}}(`rAfA8Hp6%0&2z*Ie;MZQ-27i-hG|KWV`e~v&ldb(?w@j{w#&CS=G;wkcbYJJ#<3`+puFL<-%0$Z+^H(eTy4dTY2%LWdiv z0gVO;o?jb13NT zl*2i@&wTJqN!q9aHv-Q1*hKl*+Uz;GmNUk?>U*6y9G5g0!bKUNx{A?*!>QA*pFddB zQf;Ky6riP5`no3q7Ci+qTmH>kyE{6%n-=QdM0+70lgJKa53s!aPNMR{)e;@gdG?$A zaCM{N0UJQg1yV0`IkwDerq7o9x_57J=2{P9vF0|@3gX6SFoq)dQK0oZGbD0^Hw=Or zDK}k{Be63k%F{vFE=HI)NUIq)VTlNo&F*OKNvOH`ot9>QxoELxvKykyfNOiFrOy7s zryrif@hk}z!n5?*mA5;_NzNwt-NPexh?h~kWRa(2_9gQ68mikS{LZ&-yN^%P9t^wB zFUnr8WzoK9cu4u0@s4D%2f@(-wpriHA|oi`ny&ob6@#?X49Utg_=%RR zGNgD@e7(7jnh_)WiPA65R*cZZ9LDvdKJ@|SnX#vZH8%SEk$TX1A~`UtYDdR6`qOFD z`Md7aX2_BX-Wd9IxKWF$TEFe3(d&(=wS$2}CTnbZ7L5e1eWW6n{xe1YXKGB(agPG@y&GK!$)~U;(e^gTSma6DUKWf z150B7#^K)Uokdm(R=Q&{pBfOZ#-dZP>-v^`>qXOvnllPa^$1gNuCd4Z(uq02?P#>} z*{6!AH1t;Lv{~}aboO7Jl?K@lN+v6ATk(_T23ZwFRLeb*hahfvKl`rF{EFu5KAVME zYO~}&@;e#2hOQYNR!Ihf(*Or+beN%|Gd6r;7laAYmZI5tw=bMDyWU7i6N$B`d-7}% z6!@flyI1%olwv7S>SNfVTH}(>1EO$>)mq!1&3b%(V!c5el(td;`2sJ^aT%S#I?&v;9o8xLQhWjCD4`x11*w4)`+?YxVlO@fA`7hXbOoj+GE(Z@PHfVG|IDJXQnm)7Y zLT`d53zsyLXYdl()YEw}#RDjNsbqVlc#1D$*&I5YG#++1IG%3!V*1m|x9sUYPg{e2 zG>sg#F_b|rmFPXD9@@DU0c|u6n!MTM%`Wq-FC0ct8!o{8N?k=37NN zDUMcymY4_GW$0i?ho8;vp3KxLDg&VRvYqQK>jU#dtJE=&G31Ie-dxfkt3#5#P!yL< z9}Dlk{3*!nuMgd-2dP_BMt5S~>m~|zkPbNBke?T%YDX|p8%9!eNn6H(`E25|sFJH*JUc290 zw0XdyT@fLQkW%mJ-)VVmP#V$Qv5pe(*1=Q>V6d5pA%YM@zG5-pYxWD&qrDR*-d=#p0L&RV1(MbGsvuMdJ0e#7lk{PsO$}C4{@i}1 z*V#+_kr%nCBl(mB@Yi0^NU@72Z_bN52c2S$;wcz9!XD7L#yUjcZ9Y^xkgrwCktXxR3Dh8q*t8y+TGkS~18>O4Zz; z<*{=a_4`g<24hnf*4A@YxdRfe82Rt@`2zcA&nj+Z^4co+p-Oz#ttf_h7>TBGGnJ`0 zhHf{;Wp79c6Cs{4)K|O|&tnc5mS<_=d8=J?1&x|(pCDg;b zYehQOip#@qey0ZD>SX!y1+d4cU+0;+RG8*#+dG7f_F>*v$M-9pvJ&z998m z?_#;oD3kR;cXFPgN0F?sL|IXDcEx7MWus~MUf4z5^U#<_RBE$#zdAf#)TpLPQ(pZc zroJl-)m*P*?-|kmIprCVxRB}=Sn;&mY2^B+VWmQ~hFFqq$zLes{q>aFF1jdqhz?q! z3Yx#-ebc98fHHC=p*Wa<9$uy~1P!vbbS0ZM3v%|!HJZYjp2`u$3sm;#It(52 zyq{w2=>w!q^1ku5u4{)+wNKCg*mP6nB6}-<7Gm*y!kD);^jXS*GijR{;RBgXq*KtM8!j*dC`i^mq5VMz3t;WM<%G3HpU^O2=9kFW6!HUh1TwdlzuwfSrU9(CDH4P#RmS&=VZ{Z_ zOLQ3ARgu?Codwbef(jl9^gj1R=`m9ZL8ECjaPfuK+X(>)ibeGwvhI127VnEnSHV2f z_09B>;bl}@^D+xjE2z6XwkRQfqnbU+l2ks!#HW&)Y{juyaK`O4I^f2`^)^K-mYX9`9vn|%^X7>}Z0lxDwH*cH(gV}EQq`>Y7bpnM$&Wf+e@5@o75U-@QI z=>X-HmeSkq2VH*jvBgJ3pRAHp!3YOZH9vJ~%MucLy`uI{xp#Mp0BGGV-KB?Fzy*6g zLrJL~IER44oQo4%%2V|Td5_G1K?{jkBNMCG2brp(C`hM|`!;&62yU49q^Q3M`p}mj zZY|mZL_NSdroeQ*=7)(~Ixk_{Hjq$(+T9!^u`K90qC?}x4dP8cI{Lx>3cVg4uo%+?$>nb?^C-*Ql#TK~73GJ2 zfV$g;t%2FZj|dr1ED6Nk_SZ9xoA)+<@ihM@vH$EzIlTW_0faMu;C8FQ9@#l5_Lu#; zh|yXLs5sriB%qNmF0kb&A-Jsu{jlkW=Mrge<$^_h%ijE;zj1zu=Aw-~3BbX*MnpC3 z+xISN086zmlvqPtGrj%T-i;*H4Qpxq!6XsDw3BLi6d*C);*40#VvL<&;439ZmCa^c zlPIF2(|P?VI^C>WffLy_Jd#> zq|%kbhDM^E^w;{W^Q_i>+oq*qZbfTU3fY2}Risd9>J2m*2^m6khu{u@hpyO3x|Q z8IX+c+4X&z#g!Dvcns$H6T(FAy;{^mVLGD3hj7;YNMi9PbBQM9TP*@ZXi-Pe-XAk0 zuneAK!4OA$K9Xrt;gZs+vX_G5m77ZB?G9s~XJeki}4f_fHnST}5 z2bk!2*(}=%%v6HWH>nwAC2*D~#GDZ>^Ay~-{4-f})WClD}oq9AKcOLYyX?oKUz94pdKxG6=yB!o4GIT)Y9v@-0f^ zxW2hX^n6|`OlI7dN*y3nHQAn96p_q-Tm>Wn%?T_I^9Zio!~bb{{eSp?ZD(uNee5#$ zd06F*PgDKYGn8$boS`QP>Q+>IH#NFjnDaBif86QgBukxPQ)1a_?0pUi61z%_zf_M( z9I}_mC~b)Bd(8t*O1GY7?aSGC1^Rj^qbD6oMXPaFq>M5hXNY-)BaiCVUtZ?Cbn&5- zO#x;nbA{zv&)ncrxWW@_I0_P1Iti#7LskhZJOQk)ErI3XGHxI>tg!qx`bGapl&Mbd ze7Z`}j8e;5h&Z_L3tHzz$(1d~-maZ(K$VJAc)|GIfZ8gPz9@Jd$|7XQF zaP;-zzTzuhf!?#z{ga-Z@>^kiz2G_zd2B^yN9##dKrc{RpHOdE*{C-!lv8tiFoT3q zeO{K`=`QzbM@W6xKi#>zG`^I!5I%QyG;Fc_4Nh;)HN@IKF_LB zXE>?2|LS>+@1Zrr_-MHN7f+*K&%5952d=7?1u~{n#`mUr2NtDkPdW&G@KeyT@rh0Vc@HtskM0i%FBm4)lQHg?_{a+9(`TS_gezDJ){zE$vjWQf{^q1xedR_~8AvC0yUV(txna zFaC?(NaV(@?86@{jjtbqTi;UEb-f~s8x5_ndk#6_)?%b-GiJCn&v~DPpo79K81Me< za~Do~NRfH#PR;*m?>(TJ%=Ug^)T1+lqceyRrO5z-h9)I+EJH#S5J(6R0wbM-B7uNZ z9cM%c0z)uCsv00bLV}be5}JNyI z&QqyrX}Xi&S()rgYaN~bQPg2F z+3+siD>fTYRsskI;^iD%CR{3{lM7uAR<@0#AU06O@9i*Fr%({dEeuh$gJD)tw#q{E zEn2eBCxuUtsPh+6{eByv1v`C=AS%24L{0K0>-!c3VzpiK#-7K}l;fo8DvRku<3^5& z<#we2hfXNN1V2FCJmUDnow0{+%q8k(RU(oa&gQHNi>j`%9bE#$bqP9f>f_?nd_|87 zsu;+{wM*x0i~UZ{I)#>84}Qo4D2$#CI2uNI7LqEi{@9LEm?o`a6m;(O>BW)|;;@yb zNnS{PhWXq95@Y>F!zrRSJNWTTO@o3XWRl9hv#w`Iiv|y#iPUYlcm!0}AB~Gz{=PO^ z2Gg>-^W@-R@tj_ zY9tv@K}gyBAZbzjgjs<*0~Tf`bz)h(xU3zXe1#~1qW)aOawGT6bqce z!5dq$gtT`gsa2W^YsetfcEo-6(<6L{Sl2rhw%T1wm)N%Q<3#(**wov;oq)25XcZ2l z805=GxN(<9e8xqnTFGwYE3FjW0B=jh$3#lrs-3dR*{8HWM5ca1X^ot|veT2_oXxYr z6gT!*qp-9%<1|7Moh;UaCTa0rkvb0|{67 z@Z-?$W7AGH2!!D7-(07t?0yFY6bjz4O~$m@%QloP1fP?TwVNWUklH6KZv8mf7PP0j zFz$uOD&(CABx9mG<0kmyTLzViDX%PMDDMCvI6c5cS*yZzkb8Ha zT2vfs!^n3L>quAV1$BAq)v_4no|7UtchDi31e9-c^qu_KLDI@?YPb-CEppRgX{x3= zS=*+wT&Hm02-}aG!6z#^GCw?7kM9m{H{rn2oSfPbrpW85k6BmY^wD*ad&`HU4jK~{ z!A_p&flZ1O*PkxWM5V0!Arey17M$fIWzpu;A60W{rfXXwUeR5i)u5$kQ73#qz#HB% zM=?n^Y#L@UobE7UYg8e(8+JI#(%AlDh*@#7y+rhG^7(x)FZ^cW(uDhzG;vn*+`=m8 zSfz%G#rzpB{|7`CqTgT{idoh#h-4v_vWJO*lcj@5$M;Oi*g3Fo>xtiY5qO00(JX&y zW?%|w8Kd%4{V9zCh=GQNi=6v5O{p61rwOn>>tvw)mjq;VFjCjiq9fWD+8UZ7I|U6J zXF?~(HeeI!kp?9*bhjLs&AAje@k@_|ZGk%zu9W}_{=d^~XqW>@ZKc?+CoEjtD(7X%^gu|mykW)*UsX3PY&ttGP+ z$PT?6elP%m?74@c zWqjtaeeF%#*~J$a?&evLmwI6nm3GPnz(a5V}VuV^L5aVZO3 zn8jsLuKvk#(mTZr%?>~&(52fP2i;A&2#-!r7HS+QfBp`fw7OZ_f9lGhJSD5tf**|| z`x%s^7ubVhn46SMksGLh0pggA1#{*})8KYoG!!tuopRKlF-n`*Q`osTA|wd#76EF> z$DI1i_3l2+YcTG`*DGi8(2qo8`lJAgy>Wr8hF^YuYG>5#X%kRNlHJAIe4NFcePVXH z0nR3JIL_OwX*}I0veu;GmlByDJ5)>}PEB&Uua}j0YoyGTY-cIls9?ZXn1a#p8~an( z(-HY(Aqw(~ek{8~I)SZnhdH;U7%N`<>%DOB3}wFCX_t!_Qo=g2Y`C!VK9rqA__;{NdZOfNq z8eW$AjlFE0hwb1nBTPCQZnoO6E1t{j^r-}#LD+-yOik29Y=BaNN(KTfc6zdL2MXAG zERXb2Up0z=E*7Trv4;X%(ZIuZB-uLLYzo2_m{h^+o+C<_DF!UUvI41II5LTJE<|7S zu(%>$UQF&N_p?6uT#4JYbC+ESO)?p{fJa`u9Yu>6sV30t!{_!h=N^7sU%2~+NEz_H zq9L|8FB8l~ya9adXcLf^)-p51MaXfrX~p-@y?V)qASilqMfKTN`a73|eqx&Xn_pp=FlT3|pZR1zC*CI+k388{QKr7KuuW5QGy|6StlB)o?>Vt( zn-Fm#ozrYYkza-S{m;z9%uKzxt|q6Li2s;}-ODyAFKx??(^g`pba`dGUNJ=2-DW0H z18`}@^`Q}Azd7=hwDFZ%V`o~uYqO+`d2CN?3kvZ*_+1FxPwJI;cA8slG}U1K?i zq>meU}r! zh}bXIyUXDi()qQNtNdP=FnXcnB) zzR0nTH#&nt{WdUym^VJ2JB8SMkUPweNPig;7qLa&w9qt~&M%hh&}!|Ae>H^gcH!-)?KWO1*SkDr~??$+xgQE-=MFw zXzqSOowyd%E@Ny&90IAqq$blI>b$EKXvSs+R4C}SY7-HyVq|p_ZArdjliMlv0-8$9M9Wy1C$FCdmS;X#v4QXGiqmCU zL<7NNVO)Xvw>FOwTnX7(_6)mFklSp{%@2qJxFf+Ae{;r3`tb%efTx;RZSvG(n)fo` zcH{)6oO_b({N~OF8wCY|E?O?{;4Z_*B`|N23%dH_S@6e+$XL}{d8?yo!tpJytuL{ zX@pm}kSla#pB?$=HVrlXyous5H&FTaO_cP}6Bsc0N>e1iolx5N^vErpUY`eL<&;!a za@VGyZLVZ^c#edTQS^ioi1dUxCN=x&;u>m_ye7m^_>>Xx07$z_c zdXt6V3go3A5gmasV01U;Sv|Ujf`D3rZoo;PUUTiu>E=`%cy;BHo?KbI^h%@(Q{y+A z@TUlYsVY_A&4jTGGC!}^bKq%p$H{An;-Z*$mG@_HA zL(+hSWZCQx@?)k`xiRo`$K?tHUkjQF!Dxh=tK>A&Ywl!>8dmj>9Mp~cZJMXI@O4pA zh_0_|!O!=5E!MH*Z&0hW3b%(g$N<*z)WPwuBLYnG+!xP2YFcwujTm^c#e$3%4%4z^ zX}K;^r&l1{VLAyETb=KlQB-pjaRQ$n4!rf$IJ(o8b>2BSqJJo|inRN-c3ClK)4m%n zs2#d`_&f;G@D|YB;5NKX_BlW4+$`gE|9(bma4M5JImbsSCQmNkOCy3IVj!dyt#q(T z6)s#SM_ZwaT8Hty(19N0Yu(c_b9ocb>vI}QjrL{#@4Mpv$0d8A{YS2|d9Sz>Y8F^N z+!~@om#$c|!FgrWx;;E-cSat4wl(-f#1s=)`xw}$CH9Z{KD*S zXFg4JLg2_66cQn>2!qFp*BG0zXk#@58h_&1_ z4ZcFi+5c!gVSeMQfVng8*H2DU$9IDyz~DqVZ-N$;9HA51l7g)1#$CJ(t^owNygSMtpInYhhylGl*R`@SYgWoQnjLkKYYYTC41a>NWrm^ zrW`-)L9g7m??uJE-!D|4v=Y|Wdhd)73DMRC{_5#n!_{zDl3R;e(-ffEu)_6t%KD(O z{WuDkwE-Sr32)64!awCm*K(IH*xjE3H5_{(otd0=Z_t@LbIvE@Ok%%{|ncEAt0q+h2TkxK3FAYQeI;bqC zhUw_4F*O_Kd37gR;qAv|aA+6^K@&j2b6|FR@U<6)9%+*`ZZ)tYLF2S}d(#EHqX2PZ zrF3(9D-BnC=BBru4n|&H!ST|iJ&AXt5m!#t*!biFMr0c#uhIp~wLqey0$9YNi7X?`{ZCTTU+az#HhiKw3QeR5r!*N>hwOw$kz@&yg^60l?~(! z_t}JnJ!N^=eD_rW6$VF!nk`=}cI{MEpP@-+qT9jHSw^{-HY6Uh(-_m{%fIR3pJs3)3}4Vcz(-bw>`*SG_3c-(I%WN zvCms?1X8(+Z`@+(xpoXk*r*b@<_Zs(fXTU@E7S%H<2fbw-iuHv@_|+Ek%rY+eP#TJ zm^l8c-Yc_lkjbR>x`wl@dJAhy2ektkv?n{@8L_wgW02Gi%-I}mKX~M=od<6b?~N9N z&|#~ym6y}soVpXCeL1jYMExo05brU8IIvvv!PLQVwczY*+l$BhA|bk|X{aFKaYlSV9ugIBCh)i6 zg>vwM(|foR#GYYZlLEJayo4{m-`}2=nJ^wbT{vA{R<8At`G?4jM#LKv6T<0$epYFC z9h?(BP#7H~CN&Eza?Uj^<-l-@6`ml3AGAcV5zXBA^hmhBYnS1*2>yvZmi4WQLLFZ+ zUE{f!5sjHHk|xYK_V{1E~uuN0?=YIf$E zacn#;z8&o!jMhs-sfU9gdUg3i%fR$A?2+KiGx@}n`E}`*P#6Le5dw)ked|<~l8fWL zt1g;Y6V_%Olbv2_4eR;kKSnYZaMEkO^=n@wyo!AX-$O3wWmem{-i*`AxBoP0%;iCGX24$ z9Gy{-b$k8&Amh#EnI7%rHA7G`zg*9ZcPR(%Zo%`4n;J-ue18?}{_}d~_npzEm#=q9 zA;Y;4;rDEFfHaIhiS}>ykwPL?kc`+osxj94`hAlhx7WhDiI?K zUIaf)BKRZ1Uc9jtbs;29s+GR7DY_uF;&|=$ynH9_QS~TU)U~y|FKnn80cs!WXb$df z-lT5v1*e}lEBK%*$F7y#W)dX7sZ%tEHK;}Uj3BX%sk^Ij&mP?RAHP+6`Sl9|Ul90$ zz<*l=_Mao-#gDZHm$SyOw72vWM%WODO$~>_hhutMW>!6pWKT>SI~xd97k%rvzl1Dlz;o+?(1ha>zE#|NsUu!U$T9HJVy>J7Xk$T!fF;u5$ODfqzs&l|?ZZ;q1+HhF^zU=!@oh0#NTmCoW``gY5wf1n zDs2ae2O}cbi5A=+2C_fCY}ifqIdQ^yty9&Gh>4(;ITRpm`@qYzRQPaE1c5*}*mv~m z?J3l zG}UTwL`c((0)fPDTBg9@1Fs%LUlukh%&!F9b6wI+j(d4~`AYkg_mj4TyK5O~C;8=2 z7<_F6$^An_PNVM>f2Ck>=UVFJ_{P3bOaPS@q>X|}$*$u7k6?JraCSd((rHN9RXgx7 z6>spB{q-G{xHabM>J4m=@f;)8swvXIE$wFSN-`5nFYJ%3%wA9J;xgjrN1|e$X zY+8dMO|-*2fR-fnhe$y>pamiT8wE6|48`ml9>8n`v!hjtjJyLO_uuo(hQ!3$0FK8* zF2ZI$zDo5`)yqn|{w33|TQoIHGeWt)RJVfHJYnn;TVTlB|o9zPo*E624P3VAuHn)a&5+<2;Wk&5RIP8G-T zpAe=?5aXf+O-K0)hwy~gx=+=!g8GMe8QrhVmFb-t%JXQ3nE)IC0~Cr)?R40*(Q@-B$W2<2%U9^=oCKc}*+(m0lNVv^rFCye__3Rml|*LbdZhxZ zdL)7mU7cw6lnJO249Dq|2~=z-Sbli;Z#%5x8fImy;SP|}_85q632k}}ia7A(c~AY> zMfcF_i^uaI3l~&Esz$w2dVb;Y$0rMnq2?NTtU1O+9p83-dV-Vbw8PzRv9r&ruS4wn>?{#4 zosYrN6KS~!CfunWWVGQvCLEWFSSKtrpE&H*o!39&79pCch=5eq%nc_+83l+tz`||% z5n?^&^0b?BhIP_4+0iiJi5_eJF1dL=MEyHl49X4?SO-|E28EZ!7mR17DXI7Bjk2Q4 z^L!)fJiBr88*0{3#M$r4E=2GAnvhStpcdu?3wtnDu8}-4)$o%`^UVkN;-W3m*g*1% zhvmUAiF?1jILmlQJ{9T*b6l3H1$xl=fCW{dP4+=24zZ$R zND;$i*P3q(kdBl)mze+rI@rD4vrk;XOo>Q4Z6~TyH_{O>EqO@a`SdEr$(@SHKL*040tOkWnul3iF@jq3sCq6d0BD< z^T7z?MA?BuEo#^Y7b@2jkGeZm|wa+j&u(}=C|7uR^F*k1SEM0nSEZzif$ zt4C`RO{Aw|NFaO30ZL70A)-%yTe9pc+Kod|j%w;E#C|uhXNdBpiuP~o;?>P825Rh$ zBB5}b^m{GRUPtGAQaQ8}9>e+-I~XUM?{0V8ap5zPGry6(7g3|p=h4_z5Dk7X6EzPz zg!=XBRZp^Ytko2U>kqJsj*Dm0+D_J|N6D9;#*5XC^Nx1?mpr)strGpr0=?9N^@6h< zG|JqJOI+&m)m%h1>3apSv~+v>Scx^*>}Hfqwa65+&aed82NeMH!VkAC~(Dy_m0orZN4NdCC++;F>#v97`nce z``7eNbp5A^?~(wsX3I)cH0hOXk+;w({vmQELTai(@w-PeU+49YEvtPwYDdZ+BgmG^A1cAzxj7$VLxbIs zk?5)ymQtsiBWPoEdkJX??(`sSy4!NjbO~I(ac{GXTdDEqh0TX0Xg{&p`UPzrVjfRG> zdwJ{RRZQ{YvJ^iv$L=$*E8|k>(rvLiVG4FJm|B~f(zd17U#STri?^F)I*aq{n#zc1 z?2;2#9~(sATV_{qZko<(^o1eB$6*3M03_ma$byH5;hp66i23Ds39pLoNuISwxAYVe z^73u>JtVk5OiYsB$K>0xM2<$xH5mPNH5!gT_QAuG>o5Wy(V#ooSSrZn;3^z{;p^8{ zf;Su~m!wVS&3ms9d$Vk=KQBX~@=`aI88()|MX~KRSI1U^Jk_)7Mj0M=m6e}W)higr zJQ#4ho=>?p&|EWGF?Y=>Il6c~)yzNxEUJRA(%lCssTXi5k(MW}B~Ll*VBTg92gWa}WsBa(IrIEJ z&M+@K0oq>(1uQnidnUu59mru0jsxydwvy5bMo0JqzI_VlP%DEIS@Z>FFxe(_97xtv z;p!Ty`suDt*BtI>+C`1JF>g{pd8U%VIriDPot#uhhOTiS>DmX3}UEkND&p!3Q zbPj)iklL(KF;-mPs;Z;Gu_7JQlsPmyxU~jfs_(sUSa_^VaH5@N@=clk$CK0Js3yBR z-keH;Ib7S(1Q|-f*>)sP;pGmHqi2)7H&4T)k9CH$YJf5g5deej*|UYj8#nsvW!e*S zbBf@w;RwX}=s!g6`L6NS2j)h#X31UyooH>$(vw#C@-j$ix1yJr+ZZ*2$gb#6M(h1Iu)l~KrRFq%wEv0a>QsQ9-V;D1@f z43G4G5H9{m0Gi{%dP?tR^@b8(^PYA&D)klC!Zyk2o`Z0xg~{bd}yxdLZ`!5)7c zzCb@-%;o7430$K>$Sc*A9nH2fs55`uf|ktsgdsl=!Dx^+xz-G7{!rSk6_FOH)j$`V z`mWS0W%%MR8MvUBfcoyIB42kWET*HicB^qH7t_>N3;Fq0iL%3l_Ky<^x+O`X5f}~7 zR4p8>d_7~o6t07*Gv-zGrs9PaduPN17EUx^zIU*g@^VeAW3}3%mqGDs-hanTjQME# zUUSLWI#CHXW+s?zk19cFBvf^*?2Q!x+eOl|M%IOPXB6*EmBFIrjnKUk{>dLN?H$T6 ztP^+*vVvA_zvB4yYPS$cRX0MyEyos`Cu?zfn3UyutiRuodN`79A~QZ>nwQX(C)jJogl>yN~40Lbz6iZEt?=iM1Yc&}&H^14*DfOadRSPiS-e zPOgoWoLeZFN{H+mu;**v<5g9J0}Lpx5udDXSzh`(#Q!J$iH7wxl=C#NxYqJ9m^}~h zqs(ViKLel1S&b8V0UY(}r1uVQ<@SDRQg^v?=%QUaqrA$Y{Ssf|5xTIQ48b4g4b(Ugo(7{9pjirl8EJ6%~ z-DtjbJ*Q-diB51qTuYNFVeuML3Ep9P^6f^FC6x;4F%~6>50B1XB2hyF7P=>`>&Z!j z(p$^VnF)G=RtJ_CbiF^lm!)q}$`V9;)IFpdg1lZm1(?nDdaQtSgc9G#l|CGhz;^|y z@MSb)AiHq$C~RDpUTfS!h&>MFIxUeKBiTYzpaeQary)zqN}UwOXFHPGPx5f`5bi{N zcSf|I%jNH`%moR)_A7ib6GrncMe15+y*AWwl9#7$8$~0a(5=$#=-)qB%Z7$^?1Y9^ zENs5vE(X>WmL4wj47DsEs?{)^=0PBkM9b4}k;Rn9k zeLpSSF%QsZOr|n$_YGeQ+S8HmMAV804^%X156tkxoD#?bp4pmsyh)Hyd z^)&&Mm*U7IR7jjFd6hU8rb=!PNSL)Z#c+mH$SG3N>Q1V}`qK8*uymvq*RCW-;0fu& z$9IzZ+bueM8oKR6V!^YDb>kZbG~Qe0*%S|gMy99Up;NpoTJQ1hxq`P4UF#w@(o4>G zUxb?UD&UIn&3pMZ&+>S1Qu*!e63NaJe(}Gb_h^Rn1Pk&>m-^psr@HL^Jl`$DT{WC?L7>z=@#%;X;vsrr z*tC1h%#@c7pmwz-rB3mZ_>}DlF&Hr6eaoFi_?z{(m`6X|;X?p+o~Dq4P>Zo5v^p0jaB@s{P5LMUUn+U=S(faqCZcfwo4pKk)h0 zI=*4pI9OH^2gABfMc&<0&k;u#j@O($71ov6V-bsA(*sC1Ig=mU&JC_EbQ+hXpg-Ct zmVV?3goL;L57mO0ft-tYw26ZE#);F%^Hn04pU8eqeE0-6QD9Iy5{=GL-O%;ib8XTF zVZB&9$M&>=M%O@DmF%5J4c>%Uo!+NP*~lwKCA#i~6I%CWGfci_he!unMGC#9PO zY2aAdw?0#JQ1;$+&hv>5CEIDwn}0LMZl4@HNT@A}xbjxME95@wJwnj1u)~&Ty|nwL zq+}*Q3~+8SV}Zs$Xq$A#tT~WYVhg4v)=^Trw={y5`aXT!@!R_V>;>!t2x=57FM&9#J=B{M zdK&K}{}5TTj6Z$fMQhQKNtVo_okM|}m}Igmekzer+Tz5TIKIk|dUVikzd3RqR$f+CoJh{^P3jQo$;==Q)!QSGSnMA3?JjjWU0I`7Yqt6%^zsF zG>tR`*E0|#x8-tf(lq0-QkAD3_(HYE2TQ*~R9uU|#{Ao$m^$AEJwOcA))2ZnVnyFP z`RZCER?pd|v!a7=c4;#^USi=?{!C53=EZ`NBrzIcFNO(oS~l9es&e$cAIo`c!lb(Q zr>J;S6}R5VV1V!#^=MjqW*wPNq)r`fJsDbst9Q z|DSl2{3o~9>^~C~&OCx;^ zq~$HmVT?()jZ(T>1B@#2q3E`xtAA-J#rr(8$vd=yiJ<@g}Ki=v&^c8Ea@$~xY_IKFc_>1!sYX^)^$_gkSZ}vA% znUuo-O`GDIZQs88DaJkdJ+LtbSk!OSOT7J*2bkMvKmStP7woN~1?y$x36E=`n!qFlZ zUnIz;-TW?TI;4HXWWYMG>yb;&foWJF)3O&tFV-AiM9@EGz7_71|C^_3KEV0qp;V=- z64pyssK??eXx!-+siazfgh>Lu?#p)vPoNzwF&wQ5UV1@sEJ;>83LI31c^@&x+*?l? zc`EhDe>qB8LgkK(iSf~QfTR$dXfW(F-^Ryq++!xRsz0MvG2+Z_ZYSi|_SGA+#sq~lv zp_7A+ok~QVAiu5_`U%R?Pg#3VabYBJaDid$ShNr@zHf!*$xr~;U zRw?K4Zj8M0yNj2^+Cy9UFo64~TgtPh4ImCxKI5C6NGRdj>5Qk1$dGuf^Oh#br>U_v ztM*piZL?Su^xepdy+vW%RN_9#y@zFPN?5=Hx8p9IFUv=GuE22e8?e4&pHC_?QQZD; zRGEZi;_`ZRb+yB@VT@nRXhc#G%!$JZQBz{QA3Apt;Kic5C6<&H!M1+>rV_O(;ifc! zP3K*COjZiZlbt4ceVDxWma(n09Th2f*28EK#?Z+Ca^LBf z&QE5$P;M((Yf(BjD#UvF0(2W?;@(uRFPFwy|CFR0W6w-KnVXw5MQFg>ee|Cnmk0hB z67>o84hzTmTnqXf3h~5HZngmecz)q$8*QX9yx^@MlO@PV@Yq zK|MK5%|C@x~F>RRfW=R`G30now& zLV}^!!hPhTOY8KJ=mQ=4OPucKbNiP#Jssd|{v}TT-}7poKmTv(G&QvWg#IHm9q2A@aNzt#&30ASqwB~J76i2Rp0J$-Gx zzxG2Ez9RE{wK literal 0 HcmV?d00001 diff --git a/Atestado_Psicológico_1774873520538.pdf b/Atestado_Psicológico_1774873520538.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5f3d12dead386333540f7f915dcc579c00718ff9 GIT binary patch literal 79162 zcmeFZcU+U(vM?OQvc)&v);Af9L%3eRt)TCzDyvnwd3OYi6yP z^+f)Lk+F)p>Lo#W)>z3W!AsH?r7<2T!ONG`U=g>xrPXe@`+5uVuUPLOoU}T>)>cr> z);k0fiuLpkk=FPt!phsr-~Ct2ZE5F=d<0k*tg4}{`}d-)p)IYWsjjM{^LL!Cj+Q1; zP|Xk%gyT~Vk=FbRr7Kqiy@R}d2=L?kUj-3Vv-A$~#ra9=Y76q~LvUDcceLO|RgH@m zFKU3L!v!x&n+OIAg4O>ITz`n8sja1|t*xnbQ5USOuC1eUNmJ{RwB|)s=}Q`5Rdsdn zCEbgf8af*4>ROu8o@hZevy0M37(wU{LH@?;ZzBEZ{r{$$5J5Eyf3FZ}=O3E0mHtbY zLVu|0hpc~V2>$nmf`8MhF~8e_YQMUNc>jQ;)l9tu!@P0+p6-HbcA*})zi9r4{?!H5 zAVHoOFaIE4X*Gm@(Dk4Y|9?XWsu}xZLvV(E?pS_T_*!!R+ZPQm_&+e$`mZxr`4toB z_0N|;`@bQ9jYpt=a43Jo{j=4e^WPBShZ_E6g6RIIf*A5Am%lgmpRWV;AJgIACe>f3 z_y0N&|Hk`YG!oSRK860#O!zke{$=`C|6#NK>q_|7$?8 z!SEj~?EiT%{FVQ|#we)&=SK0b?9FiQf&QNVg8u*KhVk$H|Cb5zpW8+!_gg+jeEaX= zj^q29|J5x1mt27Qe{THjBG4X~!2etN|GQ0du<<~7d-8qUzYA=J{>w@N;qS$FaQ6Ntgu-E#NjFFK68!~8wHZB2gV`$&IW$PI6-AqIU56ZB&Z zqVwM)6@*5e#jJ)~2()&NG688G{J^_9nRYyl>K zUw`zU|D9hjc>sXU8UP^h@b5T}CjdazJpka$;NNj_&j5gve*gfLoqxyuT_?BPvF?8% zcYuH0@8bgitQG?R#~lFx;g0}-pz~kq_?Lemn>4>lf-e__|MCR{0=xmz05d=kz!RX# zhco~e0qOv)y&-@h;Gn>P0|x{S@-GJu9z1mT*iVP~<>b+$M~(@dJatOwq>zyCX$etb z5iy{U(3xM(h)GIHNl6Kd%E-x_lan|nb?yg|ef*|}4;?=7(@!VPi3o|D`)`-MR{)`( z4p<*37T6~P*e|qCKxp6IJAfo#-TMXh@yoyU7I5hBfrCE@?B92UU+wTSV4uK&ef#(G zk%tZ*Ikf*MVBda$0|yTYojUxBu=-CTK$+`Z8c;iT&(ue!MT38r{nZwp)^o`tlvsX7 zOIrsU68F6Hh@2*_tU?}a7?zjv`rTRkyMGk&DU19Ej(=nQ11t1*IsOeh2skFd7g9(- z2yhj!@Z#U){J#QjyU0bufTx;RQ8FQ!tW_99XQ;@LQILFNLA$;0YWCpJ@vI$9uqNz% ziWT!i57?TvirP`oJn`|M;%o!Eoe=Ze2)#P43)=4eNNVC|DX1S;-uB~2AN`u@Utz_-2}B=JODk`)p~R= z+3NvT$%So|msjj<6@86&C~hpvJwv>>jP@-wj{cd6p}fZ4`-;BY6Q>`wYrGB6ou}^s zR)25yYFXWm)h+EBudrPCl;`9(cB7?>2C<3Eh9c5AHM>`Q5}&hoiBA?QJ2UQ#^>d_B zs^FSE-A1irBG_Oe`OOn35Gv|~jrjIV(&)H&hy;Yw>8`CSWKg$I7*pL!4o(xzNs3D9D(XbUmLs&w@C41!qU=kobNCm zPeU1=((EYy#G3at5T95Cim}M=lxkOZxgR0@!mBc%qEtcgtRYfl$s-2!06TlUMRV2L zp%ziCjP*(nLwb1Bn~NfhQ%gys=2R(F)2C6(uE@^XSovlRktczo-{B+DFf;r6WxCs7 zx4SPv^WT1F9?JivP5o)8hvi+F3a88HP1ibS35m5CIx1_|gm&k;d)0t*6r*m)UnR(x z8sJ0ml!k@->;X)v%?)oGZfRX(!zW)(N#d2(T(A2VS6C)FP^U_gDwa)RfM&qk^j$I6 zNpV{C&9YBtBqEMi7WG6@sm6M7X;p```(&~6jlPo9{#+miQwJ#j{W#Y?JmCEor=v4W z^eKCqgq_Rt0&+kK!9*LyDVDbnXz&eRDC+BY;=VzR8!8z<;-?(qDHXhop9+aWmpO?p zltaA#tGV;fYIF$9*#orA?E#jg_5j_*hqm;1-FtxC@oEcNHiX_tB9HdZaXn2ofqv7v zv4s;#!gv`_xJejXe?0Ger;VH%+_;5}qC{}*ObpcW4qE}+sL}0F{NcLEid_XTSW+2Q zw|EhAuA=g+Az|gPC%KYHVVy!7EmQmqV{K$9EW8oCCf@Mt?h}uy2Ox>Lc?+mxM1FP4 zBQ>W4u{O+cF>+%P3^G^^kC4d-)9>0=I>%k)94WW$wS^*|MT6NYX_9to=cy4&!}u&C zcy|Ckd~w7o*X+wtZL3n7ff}GyjFm#kmHh0$2hmTnX+?v5MKubqPQ>t}>G=ga6A(CD zkB6iDn*G7*8Ij9#vwj+su{)F6}6g+G>$t%_=o{z^Yf{=R8X*Fi3mz6#p zn%zz1RgEa{u*(DEVz0$Y#@DOC^oQ=1L66&e$Tul94pYQc1Li8Y8;f1`n+CIButAUv zDCTBJhQpR!kFs;uAm_1XS#?#F7V!(BvUxxuANydCck{ICH9>g)l8i?)6VsUFpX zB)Tl0y5h8z6=;)bvZFw+(Y>=~rS?@R%Oo$^;)0s;OY^FsKP+SA@+nl0R}Hpmhrn~W z*qVlB@A(5zyVnvw8@J>ok($&KikPHKBcn|jY2Y59pm^tikJQ4<9$@aCRNN@N^5nI- z`LuL1_YyOO1oQ5<`hp_x~P2VN_IzJ5^4|N z?ShZ5tG`f`dyreNn}A!qr5DyaT=KyzOIG!S7lBwtp=K_rpaNLifbn5W5yvts|BH+I z(qlRH+*BGiw4L0a&^Sjov8(PdmXQglf0`2|e}&lheI#Zkzpo@S;$}^+^QR(OrmoXQ zrHOe{_3S!@i5!c9zy>g8vroF4N9>v9Uh1zHQ*%}{-?)UG?2>@<4J*sb3X<1mchxf| zA1|&?A;OAsr9RIEcXx<;V!?}%vftF(>IM@A>|Oxf+>|BNxHgu4!I6RXbe4D{J5}Re zm99+SSK|+$Z}Qk`rxD#6@Um*)Qhx6?@&QNALHRsun5ICPPScbK5J+an%}MbNFY)=Nwk2KI zr6ics=i=?rlot9`%S_he8?)A_FHg00t)cb=yosxdG@d`*yHoWTiI|jk$GF|2 z8(gMKZUlz$P%>a4eeLnY3at$ZH_dl-XD%hGT0!P;P*+7Mh;`Z*wz6QELVTmc(7HsL_X7pOlH`r@%0~n-my^JAW&r{*C{vA>0;v1r%+S4_*CInuhU>UBP zQT(E-7d8+8|3Ef>!tOTR2pF5p949wYo^qIIuSJh5f)3fs$PIk z_uAl#GGgr?sFr)$J24Em)h^^DW{=*FBV=agH%8t{+5?!?Liv7)39)Ut+jX^Y#fdT- z9UW%|a%G!Wv5m@qjjtK+oPC+6)(EFGM20Izet0qg98oIfHoi`ShoK_J8b8UZEM$A0x~_{Na|xCyXw(GCG`4h%ucv`nECa7!QBC^dTbn`ei5hFm3Y zH>O98pn-i3MNxAo*_E3qnppJ}sw_(8d;2s2vXz8$#b;~aN6ajMkeyUJ*w!0%p{0dp zy_08}$3!D|ve)Ly+%wlM3RR3cP5P6&**o5giWWAR{bxD7e+C_~q6wV|EU+ajdC-Xs@7@1F{*OZb zXE6NFMED=e@c(P82GBl`jq!!D)qYLWoGFYJVCJwo?^M-F9Zbw zp~5H8(o2eG}9QXTAEv#l&w z_bakJ?}PNGqJ?St6a|x(*7J~_ClXL@XZQM;++ZFTs7ambS*l%gy7R2R^!?Z37lVon zpj8nbaRUymrZkAGxJ-uC7jRYtihplEyWzaG?9qmKl)jYp+8{5E?sFX>PhfULD`V#% zQC`NBV5?QDUxZ(5-3Tq(11Og|yhxPnGtDFrAK6n{)8gyRNv#>ChyCMfXN{dRrfw^9 zp02)YKi;HAmi=O2>*Vz%vyXE10UZ&Is9)=!(Qhd1^E?5C4IVvbpmJZ2qoKgc@qY3} zmSfQ>Z~WE1Xq(yan`F+h0Awim!+=F_K2ffSaxc}0w;JLSPik1QdmK5&70Ufw=koIN z+DfQQ(D^KPOQj2Cs-d(!m~_TcGN_Wszf1p=ojm$GdXEU`291>KnL)1<>biV zjNUm1jtnEkl3~BR%vlvE|J@m5=v$~8Zf?kJafi3Q)h~x)hZ<)sf#OZ2=XYF!ZV%-&f2(^A6qT~p9viu@`#Jm zf>p~E#@V;OVekEAv&k?~FxPUR(!|Eh8!b_8 zVoxJRpVyLugzPNMW^dPg6KQhe`bN_0Spz*Tzv``n+lU=Vk+Rks3DdOu@+juj#I(@q zgvrBB0*@}*Sm;#qksVYwv#s-2f!2wl5+Hgw2nQ}Iq{z(rEymR0!s zk}HMIDk`!dMSB?Ht&DDm5qx|S4l%)$JrllYmy>8<*P>UP?5e7JT#}ydBvxs7>PDli z7|kBL+_ALWK!q^FUP7JFY1;Z}mbeOBshg1!5_8tkB!);HX3_ zNhb-MnF9{IqQX*m^C+Rf$mH4@d^~|6Kd6v$LDJ`#52s77KSPWCszT``Ax)y^?U%Lo zTs2j_)wAZ}9Ck+S}&Sr9aO=lE#A&p#v-k}Z4i&XToR6%8LFC0;sy+}`5_5gcE zRX#n(9v#g6S>?rbmhj`@5PI4mw&uMiZY?bV)YfKSU8?;D0p%|-0}4Y$kgY>~yX)gR zA~IWlcl=y$BiyHe=rw2+$VsFbH+}Cu{~bFyvaOZ+q|Z-CSeVJdkP z>SSW!N5`8JqLJ`WdsDaDMXzb+-UWPrUlFPrU>Di_KG}Hrb6u>=+N_jIpd=iRI^WIe zi7E0;P&t;8-?cDW-L$A6XI0c=N^Zc1?;rSwKJcGfp?kQe{1;N4cYmifny?~oqLt?` zwBujsc7kseMXW9M07)lmavVOznN3G(=nOKV(xSjZT9o3iePcmwtSQcXN3-b5W(*D2 zZQsLgzcgX)=NaOBD*rKlGVG;H!1#fTZoAX5W_qcAOloP}B8Y*4R8jtt25P5kV%M|Y z#x`0HHZMJ@ssSDxzc;((G#3Uln%rbAnkeiR3y0?BWqU9`gB{PgJ-sCVoQZ<}zM)6S zXd=K+)np}H<{*X@_UjT)y4{{1Cs*_6wP>Tfy-_g;U;b1xmJ}O@?$`soIVj%C*jh2% z9jn>{@ZN+wzR5P<129CcE`E>Q16V4A{=PlTExyqZhP?UG`&`R<)nWGax!v+lm@H0% zs_H5yoxtSXnV(tqJWns%ZT&9JzOdulxSpKcJ{E9${!LJQ`bNX=?{||U!u0j|OEyBAuTkIa?cImj+Nt5+;*larP)>e{HG?7Gii&)9G zD^q)S!l3m+h>gkaOS67|>YXDg z_w>|vMPmzyANzu%O1z;z(-NM*v1PknB;Vg3e1u-(;BE`2Is?4T7ev~Eq^ zqOg1ZZ9rGY;*OWiCoDTU?C3@TdOt3OKsg%CF%R!jmkqhCE5Rl%qBa8kNHhMS1)DNq z*Oos#-vhiqmbjA!-vb+XG~Ov)Kdua&jA@*FcK>Dj>rT@4{o>1h+|jH@G@; zhri@$G28aFVD=w8k6o3i=zAz+)}Ud5~P=3Opn@5_#P zR%P_E^!E4Bl8!gzZT&hv0peDeSNT}vUC&pG9~@&KNhzj-4d!{?Mgg&SZMWTT*(dWE z$SijY*~ly_Nt*kYnCogl@_u7-q3`_%6V0D#en;&-HmK|r%r>gj`|HB{r$Ud#*WZ!q zPKf;zII#z~XMULGw#F{r0|Yu>-E!C1IfhBQ?du$L+kkB=|5D4`>}-9j-TrDjw^liu z20v7tv=Udv^|2ZY(f+C1)`j-9Qvn!$2>h_Ry#NC5mRUFImux2v(5jCU%Gz%i5a{>I zMl6F2(SeHEdy{B%TekF#wC+LDsgMd^TgVwB~5wYo~V6f*-V%gQ=yl zoSt~Oapi1iq%5m%pz1q?`-Sdzz3pAL7|42<2e&l~Bd42sEpeI;tDOMbEO3(`HdNo+ z_8+14L*A9_ZbJi+E@h2t>oTS9Vckr-cBf4V6*d+5J?|Wm)}eU8&7d-`ZjFaQ7KCV6 zZN8w^pRl&JsRmMz92*Pkr;SN^tTBslGE493?5td|aqyiQ*)b+c`wj|EW&9=*Yviyk z-u+Ga^nlb{LI|~z3Jq@q&NPb7cQX^W2|OLf$BmQqJ_e4|xwPrSmH15m8M9%waVJ7e z4(8jJWTK0h+qkiKO~kYQdb3wO3O@sHjZo>6zFgT}Ca;P;FH<6QW zpjA@V42>Yn2fUOOZY(tSP6KW&zg|RwFs;bOJD)Dc=+RRgw+xTh|4u7LPtP^MOZ*$X z0vu)-DiMngomWX6yk8n=={d=ElR|b)RZ8!>>mxS5Z=cyve=xRgho{#w?U21(SzbIZ zhF&usHrP^J<$_c>Xka3BIy8ZW^{0^V({+RVLk8A6ZJe%HMmiyPqTJn)2@VwLT{{`0 zVtM1M1H8SeS@udw)DR2?`3i$s?Yqj`Y(g^)u2gxO9SE$3A~dp^jCyiKlCUfA%{*Np zySe4HOiTM^co^@e+Dh_L6v-onNC;mS*9Vffg(MQT<9!P{9dFJ}R+cCos&WDOi`%Ct z<(@8}W|{YaA6PL&Ow@AH;L)kb#o^qe833L#Ya-2@O(Tp=z^MrT3A0NslaS5%=9FvP)$NohDGWx*-oq}ai^J;UBT(9okrwrF#Gb! z;?27CMAiE5T9K4Ki=zg%5fR|Cg~+C6neeUlt2Cev$8F0nw3y58;AAD?Z_l3<_V-S3 zSyF{~+!k9}&ce&DkQD0>PIU87`P zwRzw(WY;qv2774So(2q2to5?eT`}dT0p0MjMBo*E_Sq4@bDN*aFWy|}w?|t!9|Rj1 zj#=1=T$V zY;d}wr|TS>!=KHnUC{7;6?29KA)RekpF@>w^Sy60zB!p!6ylO=o7Y(g9(}5<7sWAB zcW#uGBjm6?A2+fh3Q6?U=a|TVWGEPmFL1?VbIYv4C&%Bkj6NPh^B(pzYt!oqy^qjD zmy*vF<7{kE_H=fo)0MQbHFxgJ4&~}@*DJ3<2K)BLGo^qFUO2dLqr9vuyPGLb4Ur%< zoD79+^A5a4pK??kV8S)+hO-{zm?|6&sE#aOCSX(cZ z6c^sTij-+<%czT|eu3EM#cDj^-J0936RSg(K4tZ|qz}xD7+EHriZs7p40Jv5BTp?; zv@rnGo_szE|Je{K^(XWgw%l(?4W+A5++Kq^16V=*BCkEs2griX^AG=>mip!Wf3wRnO zok^fy7*h$TB%D}tV1;ngjvu^tTF{hH3X!l}NV@-Ayv-oSGzAo}^y~WF?w+EhrtW%_ z#aq=oQguP*IQR@hE;ai0@;D0_t=(T7bFM^ti5_{~rZ*s4Jg>4FL5s0uxgD3c;avpZ z0p%>OVq!G~s;~W1*XI9-n8&C}Z$^|AlJZ%+uz-5+qjgiuw9;d~?@5JAWI?_{ATm3b zxnW0owpgrM<2OTrYk`3w4ShvVTzZjtvHT2Z1Dm!7knsY%tsA3+i01V3a6 z#=B7B*L5|?6-(I}9s0za99+X8?wS?S`WLUzO7kXg+R^#cwq#=pC5*Ua)W>0kFIoQf zvqi3HWH|PNuokVk*7-Y1mTcaDBw={O`MWP=yRu&vqXVk2VE`Zmk)LZ!L3xvNPs}JY z5Ot$HRI6$Ps;+RWgD%?T`oz8Zfao?xY)$;p@et1}4n=O906~3c5S{AM?i~CBK*OH6 ziz9q@-I~7-G)iru%+NG@?7(gHC|bgz)gC}LHV!v&=yi1su!%$~tE)xoL%p7K?6$S6 zA?BAx6C4EIOqkf*s_Ok{Z!+EkdpGOjD{pOy1?g&K_mnY+!rU2)Th_UKaQL{EnokEp z%tXH~C?bL>>X*sy=<=HI#RGzj{5qWy{-y$rD(w{8nOC8%75DY3@n{^;TPr$Y-TpfB zRYU{8PsdG6^n$NozN5B75PJY8v>UCg@_BBLOkVy=EhFEztN@FZua`qk#Pi;&wV<%a z63?s+rP)Y{Vcf{c8K?8@M!e#C7vi%YmI8oDSN12}w>T)TtZWQ9 zrk;OR+^$uucOhZpmnFc@F7pM*%OBKA$21;94T}Dh7pR=YQAo5zQSyS57Hz<8zw3HJ z9~d(=<1-C;`{*qT_JM>RdCE>;LG_I`5`!;G6x^;AS8tws(0Uf3!V8&gN7+8VNUF}S z_79j#<1bvw$AdW|w9_vI4mJ8R9cV;n<;pqdP!Yu)36US^qhYKw{mM^=hC9 z?L04Z_C4F0d`DQ&;KL-TZ7#h$HpJElrNC$VLcsCC#JZ(Vk7ThF)7JTdCp^TxDCx`y zBP+ud#ag0{n0`QapUd(IZFeJ%y!uGG*{#h2Et3DNlSL_6_e|NHOE&|`xe;`K0{B&Plp6`g|<=%*0VY0 z_gnVa#s? zb$1kgwK)X|>AJEwoAKL$OFCRPn-HgaT&a zlW7`f+$d=UzS9mO*JXgQ28I(;Qzt@e_8_v;oOtT z8JS8atw__j(6h+bFJRS)#Ts}uu0r^CqWB&lew`Om%|0)TNI;(Bsyv?n{24WWJ1Gh2V)z9DH(8d-NW$%y0^Zh8(>#-qiRF>!IRq8FV@rsKhS&^4uxKq2a8k2z0tH%nyt3$ zq{TQ$Ik95Hg?2;XSwV~>>(8i`c}#_9)H#GkYfQ-(D&?jx71}fsTpamz$fegRH~_9y zAHz0NX)m#NSb|rCDf_Cnc|Id{!Px`o9D~cY+T2T7Pe+4n@RuH*Hr4F+fqK7J{&`Fp zc&FS1RGno{l@cFhg`8tAab&*bWjzG^HYtf{UigM?7jZ_fevW*OKPzr>-8i+?HU;dG zBPLs$nXs(8?UZG`^L{~q6c+fVvVPzoaIo`sRX!3PSYx{hR4*NaYz^jTHaNrYNIC7h zc+fXuUL}X4;?h0tSki~rMM_$YE9uu+YoaIy-$RH?o^a8i0HA)f)w+MLB}TZ?F(^Z~ zFs6RxMvLOy%>bl0w2Ng#9D=z@IMtg{NA&lp9ypXmvF8RyFuKQ|WGmU0vlab{$_+sC z$mSHiGhJ{vKFig5JUb&&<08FaaMdL)Ff2;-da|qR-9AT1M82t%roNG*tbKI{mcPf} z7#lv&FGUD(Vuz%O^;%IV>qbgPSOe}v6iB(h-aEFU$G}l#aKL}X-+&FnGTWxBXW5X! zQcCu375ju95xwYS~lfheKQ^;Wl_QQ0qX&mzRBALnI%@OZaOOGXdaNo689j z!OY9UGiqKobvF9!4{`1Olo}*zqTqQ!LU*TTVo_;)7Th**ZVzBj)Bf<#>*IO#3}?T9 z#Z(grj=@xhrJV?g>CjK9uH6Gr7y;0*-`pO3HMFylgj_D|I96iPEEi+~UgU2OKw&W0 zX0F$%RFyLKI)!G{jOs~GxvK8#HpkTU94S6Hs|krfOt|iyg5AjL_|RY3(e+V>D6dmI zIT6yK5EaSkd(tt^V)1l`G>PeIJO2uVOfh@DE!c2Cg_{BU0>rLKm7J<{VsNW8$yXi??oB6{(|t~qD90pyW`kjn{ri^(3} zRMSMq@%%!1<|<8RNV=BWxdza}EJKvq3aSh|=;_TtE1!#rmnRU_9gs-OY*or0;1VWa z&iI#GuR&iOmU52gC7eqmWGZMl`Ih2l$IeyDcHy3e^3NwnU6X%x_w$HxqI?|$ZFxzf zLgO}46PTgzVQ8ad_laOaVZ!zKkqY<`Eh}71W-~m5FmQ`?0+OZG|R@mo#QNC?qIBl`5B zwvH-1Bj7Dp7R50xyZY4fiJTd5J1StrXuPi7hjHcYi)e8nYKmZ+l6;pU*H74Vt;jPE zQT`4dNhi`c-dM`~`=bYdYkyKtI}@DEk`N9WQ3sK)>CU)}sBmhdbE6hnY!yC5-*!oI zQyp4}oQi=m9W?~A4j#Ii>ctV$8IljwGCz>ZhPGA~5JED0LsD5_AYy6%?PL0qZ}gct zDKkI=_$AQuK=N@*3yIQ2?Z!pdMQj8pTI4-IaImT*>@+gKU&Ub^5nE8z_M&f?X5W~T za+n#jPW>iR=e+WX%WxhNMqU*MQWYDT}E~7!+I$3Xs)JYCWQ0) z@^7WTpZkFQ9`wr}>?zI#(Dd`dq?9XqVc>3xZU;XVHEFfjx}shlxZ0dBG^@by4uvCI zZx1c@YR(%VotdppEj^uPBXGFTl?G|~Yk%GxF{Lv;$j>%F5E zq_Gg-g)ua8DMl8AsEK7S^?h>zn->y5|}rb)m~XsiqBgb^$g@ z6t3TeX&Rk;KRx}B(p09u(1RrM^o(wlj6z>=He~`+<2?S-iAoNbowjO(S}A!Yao3m~ zaL6u1@=<6;VV&XYuSb=68(TH-&rPXsm83~roXe;TD0u8=cS5P`9} z#W0~r+#iDCsyx2b+c(YUf!DAtWz|4nDJ8G^1EgEKvY!6Q<=Hb&UIq8=VtHR=j`4sD zvBcch_FYO#Vna+r_sV$Ya+S=6^}BZUd3%+_OTwXV;=Y2s)yvGK_MgdkYjTT|P%Z4n z73W1nTsVDGX^9jXAijJ=&+%7x)3HLa>KgaIXcG{>|-c!rwo^(NBe z#r215Sf`tQM6)5}l$l0(AFxmARHuHoL`b4 z`y>h8t#3+U+tdL=SNb~D%xt!PdVh7Ga}U4D=iV?Zlz zXx)X=I!Za2p}9xq4rWf>Dz$QGr9IS(q=-ExrX5cbLZry(I!z)2$}{S#XhbE2LIta( z)f;v_-D3j?q-)z5azRnzI|!C}$^->RT?#V#I_BN2`djU5GuIsjy-=SzH?*i&01s{- zkt@){&V!MNj8C*g+we{LNTX)1n`cDM+StA~3t?GFgOOYXR=0_x6{hGOgKU};Pnwab z78^4N506o;D%{_N&3B?>^JLDm5ua{4EQbXwD$SK-L>?{FKUd0f(o45&s=GW$VZ$Ic zq~AW(xx8XMPjv0;-x}4d!0OeH*@>Clcu0iO$e5~X$}Ek>_&@qe?cC8(q1^iK;c+=OJ8U(PvG~?CuNSs|;UM~PC{?goFBIJw!9kW(o$dZf z3X?#Ol{W#c1`>QFZ^2-Efdp(&yu??kFI;CTUq?{z?SU3)b^|tIGUaGUQVmgyhsaY) zjZ%I3%tgBRR<7xL0kxLEu%niu3_n*%Sx1SAm1643)NYzB#Apx<*I7jdL=GfA-m<|p zi?V)RbrO7hJpD3M5=+ep?eZA zIyE&-807UrpOX-rjT%{%HeAxh!? zzyJum>krCP@QQ*1wGzKf=zX?sGP%TQjh{9cgl}TI)iQE#s@^(r;r(Lza3|yJy&9Ts+8wZ2 zP{3r_yE0iBoJ(C}au%HbjDx6^wGG_$J4Ar_`u!b3xOJg^*O)4)IHi69e97C=2bB(L zRk8(d{j^BUOr$H0XjjEL}f8M#vM5x+{YYWomdW;qJ3xe zOLYPK^nz8^qYu5+vTWAiZT*CF;ef>^GvcuPb0W(fSIX}IfPXI2U+cLz`ZkpfO~EV6 zP)=>m-qq!;g72nKb%U`Y0coOm5dSGtSGmm2T+i@8f%Vb2yu1K&;~Cwv)*F@_*_S1E z#G%Lz$syCk;UlI>?V|Q*__yQrt+>Y3;h=!5v`50OXz~HLh74#Ejny4?CE;uNtA&~V zjkc|PRGAl0OYm6i`Bu7SaHV;F;G-i~Dfy{rGl>~6X+{Bu0x^aUd=Uy5jY#hQ(&j?p zZPmo6ID4A<7Zr!ou9)ncv6UN2w_2b0(!E?3*f(Ktt*~_tMT0*Ma`WT|;c}rhb=8Uk zm;DQ{;Eu@eqpMNW!leTX?`m8YEAdxdSi_1J6swszV9Sn#r%zb{5xP(<3MoDpD1F-= z@tY^|ROMu43q|>H{x9&txco<{+K&iE9tsN8A3~}MOw^t@hkhrEM2K2D29g?AYa=7G z(vEUZe?@;eYnw#!ps2UZ$mj}bHx46^YXjG~0La(vrRIB$rtOIVRHgaR=^Om?!3PqB z5@Hu*Y@BNwL7~XR_*9*myO;T$SS3y?pFG2!b{J6sOD-iyhuVqr(iOlT0@hZHkl)r9 zo>~9ycw%99n+6VmT zh6a5K;)k$(M}wO%6m%)PpbS(~wXpfY2Z9aM)t=$4a7gcaS05Wk4^PbLxyN00O~u{H zU24sV0FYxoA z_vXq=;1>50Jx;-c7B1{?^IqcF8*4$FHy%a9S^NibjCYnc_ubzAC+{@e^?n{@F@_4| zAB++dth`(BP0DHOu9{sLwU9`@O?-6JP^_lWO=vMz-qg#;s3&@Dy8dQ3Nk&qlIk?uj zIDN=i7MDKnjk@B5s#fr#${5T|xmpG|k(scCdrDYh9h6t?bxI*I`Q{DfvIr%~8~DUs z{xgDnSF!o%#FN9KpZf2G7o_b0)Y~+o@iSI7m^6>_*8)9n?|qmovC;+V~*-cIk_%c$Zot4k~8sf%V6|NE%oU80P0xC#**gh2evoNztLqf0IDrc zY)fcQ$>T!94hP*O8mLL(N45s;-S81L&+@raS!Lm9XUWEuGM6HmP@!Zbg-PbY%6CM+ z?56d9VvVwV(_GX_`zm!%DRg_K=i_wy#RL(I2c5%JhzZ-ja9D5z9aj66^kiYguVYDP zv9EWA)nmDl@7x?T@tN6Dn;HM0$FVSMqXDdU^5b-k+DqIWMZTvsPk`~NtPsp}^B|S+ zn($DJswuwVr0U@Q8NeUYNb~ZEs=j{3MZ_?E?Xz6>M@PRM4+j}Hg%-**Grr+?O~Bed ztpi$Jy$`~;cadd-_K1ShVdhnWisWq&%lF-=O~OjGoegKKaI0m?1X=2be#9y2$5tC2)W?#M4yK6&4z@fe41IF6&2HG@i-N(x86q5>wef7D?#fm{tUeT*3akNz0 z`r$^En5xqqs(m^xr0;Ctb2pPlQR^tUOLB;K;ZP>q`rV)MbJi-JZ!TQYMU5f6=$St~ zCl`&=jM9doOOv!7yR9)1{-Nia=cVb2dT*0ki9cj~;$0$)I#B{x8s@Y-^e{lS# zLw4*3v0~L-PD|+x;J)51Y+GrnKUw>8<-d5y%#8dMVXxdP&kuy5+4$fnybKk&=KbYmlmetR0H*t5Ob^F;5nl?5b}=|W=!D4{v# znEgd3OexJuVI&$qjsyZdR{{u5FSF+%DueZLJrBGGWL0`p4A97&fr-}#$h^mN1lVPI znWZk7AMtdi^0IN}y#P-Jjq#FF88>j-?7Fdf8 z=RX{L7Eq$7HMsS3TwD3(h&mgD?G-PKyq-t}{&_?6e zh0yioJ;153o)leK6Q$e1R$mdaP%zV~XmUiiQ~6t?N(ha|5fg03?J|&sb=zH}l{A5~L29N9!VNx4MYlI+fnv!4^q#jV89~E!1e=9la#E#3F86?;slC zwAE=DHbCTcGH{os&LS3pS1{IdW&!qNl44r)1CRttKZn51LaBKpcN(YsnNEu$%S9DK zev4nOn3!*JZw6Y04A<}jeew9IO-5#7Hid(8@~$58n@>;o>9yN>cRclVTD^_0oU5u$ zT=6YXpcofjv1Jk}L*U*jcur!VOL@UC%l?rYIj5?IA#PElF#cxhSD~K%rOM$yHOhL| zVjf$yW^7T&=rv6dYsY-z@DqLh4$y!y5&_B!_=K}+muy}NH+QBrH3raLLMbXLaEKh% zVt?MEjzJ4$bmzO`)I*QQ^$=y-$ulFF%W{gOY^0&Y+`BEJE1R>rzqoAtu>cDal~*4s zs8pD_78wJ}Xle(_%9bFiYB3m|A6ZrlpEJkk^TiFRHuQw*uOBK`X3l7B4COjYcBef+ zL&TV~z4+&{aF0cx<`O?Id#@1gS;ihnX@~n&bp{&e8(5qB=$%lUdvG;9&eNwo~q-N)vqNEsD=jT$tqBv;rZFrsbXCU z>7aYp&|+Sz`-k$mZ^-M3BO1COKY0gNvU1R8QG6emGG)?Q=dYh(0hj2-V$8FaGG(jl zl}IRzvFB)y|Aj? zF`%m10&2@6fMK3#U0=h!u?s)-A=3i%GmrG5XkI3-Sm!Gafkn>GLJc{M$>qX65K9wG*PC* zpE=-n_PS4H*N|U7r6expP@{PGOc7J)=My(sULuYNNWf|>l`0xq%kGBd2M-|wVebiW z6IuWCL)wjZYOs^kia|!uWX^`oL4-}@@q!al(E03AQGoc|oRddYUl zN_6P1EG2kbnLCfK_y(j}o7517@!4sj@7qHOZL=2?NBpWvv^Y$jI_;89cQv`F#Ehm6 z)t9Mqx~4SB^43;T851hC8ykAy%m^)OL`K>hQ?2_z)&3OE?Dd2Tn@= zaCamCZazmv8)J4-G~(y5~- zwGqbTkYUST2GC;q_a)<9g1F%?Jt{#psc;}@rs~}{qFrkQH6m~9nb9R(jt#Bvm(m}F}S1GBbXnD$r2>$L8QJ<68v&TBJsv%sl!60 zAUMC&NoAel9WH+#|KwpQVt)LcQUA{;%|T9-oFwGwoJ?~Pd8UX=jnf=#wUJneCNq** z_=eZVrI4(IIG4ziG3fGNDqSS8mGydhD90>iIvg{Qw@B*L2IU2JFGKoB_)OPXKgAbM zHL^G{&O$E?=NBC2FY5=Ytlej1dG*Bb+{j@E<=LiGe_EYHJ#Ck7eogip8Vo4rXFsxS zH-DF~92j3>1UJ!6DMl}s{y*HkXH=7E+xP3}IOA9c5l}!KX-a4V5|F0jK&S=`X%Gk~ zorHjd-sDzq5Q4xY7|s@>8_rv>X z^MMsuxh}$Wp4V}m$8r4szmr$(I;%Tu+dQY67hKk{C_k$~d-39ab~-E8^rET&@gvB3 zd9%WoAMcRumhdB|nPX>pW9wk*g*AUGLE2j@{^ewLE1JzbeR^H%k#_}obI-)T#uV0{ zW@)K>`1`)`*R+?Hf<{=MlYGxqIO!bPoa*+uU)sVPDatNsKZYt3G|Tv?4@0VZrRrg> z*4f5qngIqV`{=A)#C4U6U*}PR$XqzOgEWl{Dx|UsCnaoyzXu$CR_l9Z0>z0V(SV@s z3eF&7qJ+e|pG{lMQd{k&$T!|5%0SLF!#2<*9y`3F0 zIKK+$JSF>h{u=^%g^eedqnNdEV929;X9)h3Xjk%QU z(M{_YFZ*))ttc5N@uAWAy9x`~|3IVyqP6ctzMxhhqsSCv+u zYYBZHQB{4ub-^gPgIcA2O!Y{8Gv!ue)CJadlz4uZ z)ajoMIOJ3F;i_4fcuU?ikMOt~6Ua&jE&?w4Am(fjVrPF@95wY zWBLFT)%^R`_JkuFjgO9Uup&0@NXu{jwAi1dETac@MtU7#4@1K zQzzSvoYj1P&d&8_TWLl(afs|f&+u>AxA#*UM++3;z1x4$w8MStz?t@oU%f zuXJa5>59|&3rLhl=P=|%M!5lJRH$vRY;@~Y0x$09W7vj2|E(dUA8%9c=t^{Hjx&G8 zg^x48(4uMg&NGq_1J z=?OrqW2B2JF)HjfsZDV|?R8L11lqNv5fOn`Zdtn?MA6{C1v@CV9vB*C2!C{%3x(Mv z>ZN+Eo!GnO<2g^S*mN|g+=)8h`hs-iV1_7w0Ge38$v60^?k?L}CFmo}@)+3s@tTZ{ zp+Tjq8Xl-Tz=qlr(nJvGr=QQpo)6D0`?h{Coa@Uu5g>RS=AxP^HH7@2`2VpTAThHcvxjLncN@R^Y(7+ zRN9MIErxSPD%V<>TKf7C#>xU-SZd*?DKD&J!6bqdxcC%*41+c79n{zXLq0aTBh<{Cd<94 z7n{jnnw%b5E`PfW=+ps{suikWsM)g}6awnd^B=WF>s1lUm<><0O{yFcb}3-S;y5Od zN-Hc&da?^(6WKB{$Tx7$DF%D1f4aQo{y1bN0uS*_+&4xyyny3B8dWaXVB0WTP@ z)g7NHuR$@i=lse!5SZIpMXMV8-ve*T!GR6Z<+C9LjIEruyj}Uh713_#Tk9+qurR%r z_AP%$(d$buuZ;7ZXpJWCxz}!0eO~QJKp%bC_}*T@DtkQ|BS?2=S}!2& zO6i5mBik3A&KhL7ec4N!aQtE27u_?)o*zE95D%=5LwMSjoz)JA1r#{M@S zNxX%le7;C0u)Xb3td@Xi%;V$l3L43G9=CcIEV~X9FsQWkWB* zP83$y#DR*t&YGhv-`=jtKnKae5$b86P>{P;tO9_R7RjoRo$uFqv}82fh{ zb>0_F_K?z%lc&5S7uV4y$^=@i#s+KJN2DX4d$uuqPN#|>vT8;e|jA<~(q{(f+ilB8>>Wgyi)H(lqA z>LZi~mv5C6XIz>P2gU%YDUJ0}*FxuGUuX2$fipiYWT&Vnny3~O`y7j7v+N&PfzT=% ze@~8DZvpHx9UKe#dz?;HLmf~v=hhdz@%@E>S_v9Zx>w?CHV`!w>gjN>$$jOQP>TH% z)db$se6qXJ0qONvP96TwG=RnpguP@w={CV~`-hoNc1sE4$R<}tSy6^bsdyo#k(v=X zz+H&%!7h-x?iQ_SO%{%ZmrQoPE-&?YKRVFdMtf-zRg}#*Hfqup=HLlc>s&ndI*+{= zY|5D#sWJw87U#mg)X(N^G|oQ^58EqCkghML2BmDYi~g?5?_;~91D^jDTMOxGl0S^H z9m@ch=47E#(gth8&Sf5d7f`;2z?dUi!9ffUxM8TUX)k6m@XP$o+ zC7ga5Q|snUsZ(-;P3dzZtRBVnQW;Z>(xSwBJ#r=!XF}{lnrdPux3mWHzwd*#;7(~e zS$;0f>u@|eR_+vo3pw89lXCpBrz4HljaT_0hy=El`UyW{x!<|s z8=_%n;ozPt>_wb{Qs>v}eJR6o+}_~gS_r7=cz(8gv#e1)S~}#paQL2|&i8$atp-2$ z@pf;q(RnxMFLiLrVdeH*aV}2mVDEi{-&%%BV4W@I+p&5X&?G&iVM~f(}u6Gh{iQ&`tuLP z^At-^{M+BR7cfYVWd}Jvjhd1rbQ_Z*>{c8dF;wNK{-L{rnOhe`XZ)I+;%X5SX;3Gq zjv~K8E&~CG#%#xI61^3Kt^rQd4sNi$v=1L1J^CJi@S zS&m=ACJQ28qFC~qa%Yd}fR*vhiiH3bb_U3b4=D55d@3iO1_wP263BOGGSg1}Src-P zb_*AxLQm?ItGl7onrj(Cy%BGY@@y zDZ3Vn!)_!Uu*HoUtbGH*#D(^mFlgVk$#*k z=8_6glS1L!v@>2?^sfw%68?aVQeyr;rU!spS=D@Q4hpT=Eh-VW$4ES#7}}z4@RMueUybOjS*4=nGw}gr?xzk&nhW=;bCwrFO}Ez5Y`R10kk|z zXcnt10Ed75<%hSzBi#p#VPOwrEjhfn^00E4HWg=Cl=|#B$NFP^ZB-?ThgwV<#SUrY z{`PL!Jvm^iY23)Ifmr%vOLDp_3@XiNkbmHj)=RE)u_yXl$MouT3e#ujf3~I1zDW1r zX?c4xD+7_4S@mTR7ofwjfV`hGTuy$px{;6a0h0#tp@^iAD~s#`NkSuGUR8+YEP5M^@u$Uxzr_ zP4TLY?$CxR#AwnJ=pl*j`#4{-3J-^^$Hg16tFGEpU2n8hMK#UVEm-xDN9E8I~l0&etdB(sapv3p1xLpE1_W_=B-Jx?{uSJ_3o7pa=2zWIe3^kZXeG#YOid5L{m{E}WpCCI} zd){XiEM*za$3eN@aE+8m4mD_Fb5=K}{?;@i|KZ6pqC|J#w&9@uCJ4TFVfG@my?iIks~y%!HNRVKXp`z#jyimAY|cucp9iwB2{-VCJx3? zh640GZRa#VSv_)c6z*FcE`e7tbn8A%eQL^NPDGfB0nkHG=3ALD)xN`!*@fo{ulVR- zL}xV%NP^rDIg`khCg;bbIL)s}po~IKWiT2i+-4trtYCi_wPgo&z79PiRdsDhZ{|{? zYnd+klBm9q+L4_%49v2||5;w(cQMk{#m(RHE<`b=^PCEZP1~Iz@9E>q;drRc>%@oL zi9L|NQY;vzpjZb|azY(?|psok5! z?}PCCDpR^Aq_OY)hH*6z9kv?mtDsw6f#oG*FIK4o#a<&uQxW^P3B zvrU`Iu+6Q3!t*{GcruOR?M25pB7%)`yKeZG+P7h!(_^agFNsGk4OqyM77SCUDfJ6i zFsi6@jEaf|QTEf%=Ds&x4JN4R^-v|}=S|Vf$7#WIN(RM6B04ADqNjq&8ZNYsqdv_XBwFb87y- zpPf-s6uk48v~uiVoTOJQi|4l`R{EDe^!8R60n=NlWTy43?5+Yal+zP{%le38-z;Ig z{$c^r6n%_cu1=BN>?TZqj* zE|^_xup`#YwW4eoNm($So(S(Ebo#ygu$4sUyN)TIs`zq=COR6Q(-qB@G2%K-apDyY z<@i9oKc*tb!y&SxI3eOj#THhHLtNS{WPIPZOzN%+=LeUG79w2@ z6i~Rwf^-@q=`l}>PHZGTtE+wJ8|A?^_{cE|U=^90`jc#u5S7J6g-bkHVcD%%u_H2~7!8z~OXYz^+tG0~Rem1P^ zgYk#;PGDyc@^pt!`p9>rYnfRS{IbNTfQY2+lg%M(5V&V(u8y!!tsgJ^(0uu0e(XPW zz9Ep3N_wSrOXVXgEUKTca*?F+!_Hp*TrX_K9RGLDj}m`1GDZet?n7NRs?yH=vKhLS zlXy0y4j*9y>|pK@q6LKub0<3SPl{exHv-MZ@f%^pxRtOgz`JID&HVKL`ZyHHb!@qA zgN0_tV6604&d&IHdZw;GPd;eM#!-tk-OqKOL z9a+uT3x=C4@tgLlETl84LF&X_>}COtgZ!22hiJi3?sxk<(YDkY0njkCQ`^+T?)4%F zF&nq;`TK=@VqjnGC!Ghhe8b2zg}%&+B1{3aROSgO&(1VIa+ZR!lniv# zSccVR-u1t{qFEqq{KV<5>?O6p=1f)rAp(Zoo`ibs0*Y%3+GA`?cXK^ z*}>@}dS{Xnd|bMY#d)#&;rc6$s_4rPAXHWuq@&*o15jZQ&FejlhsC%FG#DMs6m#YwV$r%@1DiJ zI%!Qep-zmDjfV}x{w*`K&=}ftiEf(z1PE`TALd&3SlX##XLr=FaFU~7^q>t8C0H1( zeaio-;{UF!pZ@&cE9+)1jAg4>-G7w^&aaCdDQg~UpyxJE1?~ar_FL3~afZ;jG(hv#R_jixgw_83pY_pEfcSU~R_c_vKb=?7IK^EG_fqq2PTF>NJb@&+_ z^sd)J7&@AwH^ApuoWVSdn*&u79qbZ+u-FClvK%%3_CKACfa8g{2#bunDU%|lSKh1Z zNas#h!1sM1JE)F%pwfh=Mq!~zLr#_aHD^7ehPP#Pj=&U8+<=1slpWQ4P!8&wC>L_d zGZg7g4$u8M>J`kX_Y$A{z>xqs#de3CqmO-YHUz6)!DgO~6xMiqe-xTzR3W_AkH8D6 z=-R24Hp++Awl`IR4xM*0&FQ1ueJrM{wK3&CUef7(4Fy?Ps#5aiy@0)@>K^VKLJDQ+ z%ScDFkWO&2e&lr|Np`k?DfOS+E+KPWW~W$4YXN9wLpiOYVaBxF_*Z&EiKI-f(ka|V zmCtdAcg$1HcNo)(3Hg?*mB$$lyZ4tnRTJx#fq35n`%|PSQmn`JDyNmHY=FN0p>@JO zB&tkElaV=p4ip@AmbKt{#;hPy5Gyuw^5h1FbK$QoEk6wvpeU&_#Nv$fXDF6O8{>-z zi^gRSvM&d+I9v9cN~*p=5x;Y+i^TODw3s&aki69{U3aM;Tkj3w@{5SG2XU7* zGD$Id3WcS6U$orrLk6~-z1hTapnmO1DkUF5H))*4s8VQy_0rYCr?Q98DwHU;Hstl( zWzF9iYMh5cX1HUrCc|&}O0;%1qON=_e`VH0x6@I{qVVuXt=sII&2T3t$;%rZk*Z@4 z19(dAshsM7d`aYdjL+${2N&*}#_eW^qLMG-GfXAcjzT?Q$|?-BpEP4!tu|*JclLoA zcQnvh1X7MdZB?M+dq8Bl_pznV-bMBT7uV79*2aYC$Zdgz?;ssMK)ED9pft7n?{GXi zQV8q%$`rJo@8(Im60q<-b1mG6u84a0rtQ48;k@^-lJRS4lN3>lEnWeyU;fwhGu~v# z>^ySkZJC9A6;A=-0Kd~bS4s$w0N`yT19gy{7df1_lu-Y<=qs&y$W{2>O_k9x`C3yq zdaSQ+pvUgV_sInwq^o&z23o3kX^QLXVX9kt`$?Rh$Dp647j8hBb`xKIH275dQJ_aB5Vx!`S+GJLZG) zFUR|rN?*F_aGso#DisLi|DjX>|D`+}o;*rkUDFKCC-!H3Wc}JIOH0TN@kyItrYp7- zOGm*e!f>M3m{#I*HS6QNo~K;@X8ZznC~rNhvVISc`+Z-GdFh)k5^To^ZQ1OoF)+3f z>s@Ol`lgp#S0v5jL^Tndp*eZt(m1kOh8yEhb3NNLp#|`r@Y=S%9ls1J>zAo z#;`?YT{T1($!$$AK}*b;-7?M~jU|Uvj6Ljv1@xvuA<8;IS*lR*)C9QRmY&-5o56n+ zHUHiF`9nd&WaJ#;wpX*{0zJZafqlF*9*1dpmqeWolOo@6a=>EGiID$sTr@%m=`}DG z!e8GIsRcpGt5jurq--DXH+qGSQ_8td|2cENi-DW^7H`fH!(1bzft=_Am*2#hYew+Q z@G54t&GnYS()TGU%K~csjEp^W{t{Roz?79B`hm>EE5$BDTrAm;E*MDEqweNtz6i$2 zs~)sK!(;h6Xv@!71i*HqRGWoVDQtv5s3~+Vd_MK1@r!-?`XLh>`}g#Eg)-)J)QkJ3 z1WMvBv!7d}kdOxQ71mxc9Qw~+{pahz8%|Y5Dv?*kI_%f3>Xq(GJe?7j1IC1O!Q)io z|JeA?yZ-t9QEX;sZ+EmY7jcKAj8zA2Y$>{ zz;HX@M`VU)z{Ef*V|Wz*!{Z+c->wjp>57tEw1p?lS-{IStwZ$_Xz$S$y@4GpFEiW( zH5Z1)ncZvr|Ni_pjXv$$ClF2;5wr=TnK1Y=*WEiCg^Fz1&=M(Lly>a_K-s|Ij$CO% z3VyLK;J1#2KL52V(A#u}Bw$^RLK`#Wer28Hot1~%WX+0@y?nKxBLHwYaM=_U%O!A8 zh<-tjlHyA!-V<+Jhm0S}g<(|I!L0fq;}?o)`3^puiO7aXPVJkzVP_=d`e%y~fOT$S zLkcsPco&en`9tmhbJhNjy!ydyP4WA_ekF8Alo<2n$k}}PvLrYW;m}<&1VP$1W32n( zFfk*CvsNVEEL1$4k>=2^MLw)nv%(|U+nYEedYnb(Mx2Y^Oe*kf$la37nIzC3*%`&Y zRZ|I<>ZhUz+oUevS5+T7xSmox;wd}rw3g?Dr8sF*bp5J+&=GBJ>{WN|y-plMrKJ5L z*V)-gu~~M4Q1%Y%)-Jnc90*|vYpA?GTwV=(x=wR%B0OLseK^P zE1c+{$jbX%FKzIebFBJkd5GPC4-vr7_qVlg6lc(RNj^`tG}3BKywD6)CmSW)$5g?k4>}v;vAai2k8QvgLU?=)iMhjdJ(g*Rt7t4mATf%?CpJ!IBC_qdto* zF6KHp9c$)8S|)<3Y(BMiooR>2&E&#BFmWj-_dwA5PU;norw=FMr_uBtOcQL2tdl_Y z!}P(l;HY*tK!sXlO$In7%F@3-ZYm+@m4(p$nS5se!O+vAjXpWFBorvNXhG7Q)ZzGw zA<&Xs6pP{K!q(9^|JTQIBdsi5!CEaHSR78W7JMm8&G2v(*F`R=4Q#J&96q#=fa+j` z1?PB#xh~4daNoKeWRm-rp+z!E$444&!6F6lh(`L-+_CwDTdxGG(zG|<86p-g`)1n* zx+=4%McFPBSom@sT@W6OCAN=_Pc@x?&$uedHqNxn8>*bgy{blN0u~msee13t5YW0j zn~sq1AcTr;L!I8!6NN2R|6UE2fLUeoJPU zT4|cM`4`# zi4uUm);Wcr8$aj5$>z7yS;1ll+R&B4;~*%mp&4Wu_9yUy|4W@a;Y3_lFuqU&$Lqf+ro{x z-EbPvgD8i@AvlI`vBoz&9PM75WG!STe>OJPkKMO5#J5zUP7amZ===#%MKKX8d$n@n z_(MtehqkS+llYSsDqLUJ1Ycncm9;YH3bE1F^_;o3@l?+3fMUND;+xcd1fP$09(Pr> zkiiKxwG~d)w}z#!+^ouQlB4Dv$Izty;OOnafte;csNC!LIbY?8vojm z>~hA@IpI^WfWveub>+Nb$(J|NoI^Xm$mR-Uhl2&O*uhul&-~>51pPHGS+$RQ1kma4 z6U-f|I0ASrJK=aEiTI8>-IPH_2BbWXNeh;MzP(Dm(WbibEUTA{I$m77iq&Ba1<;8jbwbv9Fmwg#oeX=ZD1YpV!>B z4()F!Qtu49(p;u=Pa7GI3XnmVi|0D%?mu4(-`0EORhQ$8 z)>XgTh??rq_-sMR5ta~q(sQw6ARATqmD%>1k=}^G+rPJL<%TEsKI!vAxM;2@TR#_S zU8uF_NoJ|1d$eCbUR1LQh4B}l-}kAl&Ngq*UC&ppC<WW)A=8_7%d0Rfc(XWDyiI6qc>HKA zTiDgVeYUUkiIHa#pIgB92_3pLYT4k-TPK8?1v;xU?&}zquvsn?^2GPGPmA zwAQOu;f?5mH?i};Z$5Tz6av+8PP)gu3RPI!rE4Vz{QvTcNg6eIb$i&f z)-?~9N4z>0)?f-MOIo?8@uqFOFMrQM5TkNo>Av4n)#})1C(Jp6njw#h*0xORy+>-; zFY#lOSf7n2Q!I`EzIV-o5EIe}l-zZQiPVd23mtm;1y(GK4ROuSC?FOGdrQYoOHM{k zId_2gG`Vl&>$&n;DsXeR^L&llf>W&q3MX+5;V$Boek+?OPm3R3%zC)l z{5$;kG!X&qgn#mw6CE@3bd2$IS+6qMwP(S=JLd1j+qKALH{ZKhPr|K=Ld4cEe>B)N z_l^stf3aGxPWuri-i>K&%d^h!xj&JUXd~$a0Z-K!i`|n;XDyxFPBxEmO$Y4X!SEWC z)fG?O8J_i$sze@~GEkTi8TV-j!F6o(<*kkNp zJz4LIBNA#`t@Q~4ZSjTcdO43%#5kj?13#-4IS8&IRhbZ3c5=SUCMzB&zN#$BYV#Wrd(~UuK9zhU z?Z=w2$EUxJtbfp1796KmV9^yaC)KS|S^T;9obd^Nw{o}u9nxJwD`X@OhraA}ZbrPU zQ;lmXu_zYJLOonT!R#WG7(Ojm{Mz7L{$!5npo2Vx#OeBjY)GzDC96iIWLTw( zg}qzC+KnGHI===L)T!4WdZ)cEepFaELSEw!yk9Mj+CaqYHI40Xp!o$$57(xcjb7RhvT&?;S=K55dR<%Y${#v)L*+?JU>p-J1<&m#zOC-AoN6?Gpw&9>l787m6w~Tny}}GT zH{p4(9KVU<2g)s?>k}vxcs&M!punu)aOs_wxW8dFhBJgq__b<~6)pz@5}Qr0<`*hR z1Dh`qio>gv_CgY$oYR#It8&c(x@VcPFd=&Qtd1O$q+)8{+UPOyh5my2w6tSvF$l`u9zU%VfUgBR~A5WbwQ5oNp#dyYt_mB!!^v|U!hXS-*atG8$ z2HFqR7AG#OKAIi>=6y%2qF*V!_x+mh+tH+|g()tjtVHzwsfD6; zWc@<^z4!1~cDyl%mT6^OSP`C86*cb@q0*Uqt@w%ho4M=o_@?mz`y=V%{8XCSSG54z z#UCAeVsYjT1F`!ZviwZ$UIX1~Y5cP*y1OEi{IV*~=l*>So8i7|q`H|Fwp$HIOkckm z`o77K?JwN9MukNx*hSxW#gW&2r7g&16|J#2bvP_CCxFU@IjN`$j~oe$(LeX1>mGV3 zn-L3p8~PFs)dudJqF9lMT10qPm`wD7oOH`R|5eKCdu9gro6gKEY$%gR7aN0f?2RGC z!T8WmEf$h#dSN{#wRmg$Ua0$AAd@4>P4c110ks_|{uNm+{ctH}8!rdX0n8ou#AIG- zo{uK;nw|62kEx68hTWjYw=K|$MXD1cGlUG`cE!7@%SX)c%8pC7!x@L&14>Gkfjs4p z%+VDQ;?uWo2ZFc!j;YjhenTqh47&ID24&DH>!v`}qQzG9P1S{vLTIRAj<&WPTX&-P zvPJJUZJZV&Q-cjp_4W@3D+H>op{%lItR7{K#0{lL)6kUXi(7B2&FRlAZpc#Q=WZyN zQf!2S*afF70e#$ScK)-*P-|@fJr^du?`B!7rtl5=Mg#Ln`%!bhCtXKBc zRQE9Y_DP#?w&3ch>mMSv5+D~~#oH>BSk#^6Z8?>TR`(Bva$Krx7h!TRr`$`gdSo#d z!ZDk}Dq0@>cnvXaf+n)4;U?yG#=i+Mj*Wd*TDQotxjyCqcTyEU6P=>iou-Y} zQbOGJV;}TKTt%%?JI^Nd1W?F6sq4Pf1#ui8y?# zIGN=t@eb2`GSy0*j8hVISPZL8pGA(Y#f%6^HDb&mYH^vS@AzzB&NY`*aiEu#X$CkVk<_aczyg zR}VM->r3a#Tz2TpL!ysyfaY&>a<$czb7fchNmJG_zDYNFmd4xj?{R-hVMX=H)xf;p zm$^M~9wOhoG0iFZ+rzDccytj;x@RPPUG4OU zXY_OG#=OVISg2rXPP{QyxkF^Z`p8DPRvv@qIzoViRXgq6-wS_>d-XAZ=EZPvCpM1qi+U?y{5lg2 zsk-&U(IXmYLHyXzeAXeh7h?V{(n4o8F2YiRJv{)3e%=c((x`V}t~lT3KkEu}zgtKz z9hyV)M_qLecBJ-9e}Y-_R#cnHr5o#lwjNh(C6awCuDqN5Jt(bAgzIK~Xf7>gRt+{{ zvN@;l#lbd3MAb;CuZ+#i*VnMSA6|5cH{e{Aqw`+x1S#VlNp;twu*B_@{)%IF7%D}& z0muA{klzoB=Z8NcN`ENCyg+t!D>TC$cy>A0~s4&pn$@OvB?r*6?9h+?Iu z6P~`$D7hWyBGRymy4dK(Wc9V1r!Q0u~+u-z^>VwSNd`$sCZw zt1P*Vq-a%cf8V#0ADf3=SMM2^3o$m)3`~IUY0t+q5hjm>QXgC|MB!HOiu*P_+uT|A z1#JA4*jqb8Q?f9k-HG=(&ewiMj8}_lYpn?~4dPR2k$MMo+g{#OW@CHPx%iONPYB7zG z5lY1ZFbBc3I9OFlCKVt5>^KdzPPbD)c}dG1Dsz~B`6B&dS{$;`^9jbaF0OXoPc<2f zwPp4*&mpbB$(P1pct&I{zD~9&XP|*mAu&I#W1j!MB{5KFV^G3}w{^xJ1x2k6^4pj& z>+Qyz;6+-YoF9`HUIu8VGAfTI@7``i`*1%XT`%q8maoDGcgs{9@MX*N zppyNxExjiep%0Ybp=84Fvz^5{&y-ZSKEFh{lQ%B8E?%K5z9hy@mxU(cvVSkm4iCS!z5^Cg7It&faU8n7|Klz`I z`R4~qQ!K$fl)<`<^aN@@Y8+L2)u*P5XQ+mq?zdT>mEjm7qvtV~|LZX8>b9_le16vh zgv%I%09yW3Z=>?oOwPQ{W=UVlyGfy~706Y6j$v+?uqP9&>s;1+md|3@qFlfm(YX;V zLy|+8Nn5U!6-FgyH?I2c|2pme^5Ot8wzYgCH;KiYwwaR^W8_Oy5jI6$WE69{Sx#}% z3O=BQ4hbwV`q%$*rbX{EWX{Io~gOJv7UQBROanEM)AW z5YaW;R&fPc1XE+S(Go$o{##%e7ka0x#gzH%wsESG13H|`UBkLs^p__ij7j>)SrdW) z52@Y%>+Jznc}2}Pa7sOJoAxJ^-?aTpWQ$E1sqxJ_4Hg4sfT zEu*DYTP-aB+obG~NmbFsn=gm(({LQm^Hu?)q{P>~vR&!>zTU=;mStFQ{v;lts#KfM zGH`-(wKG+$bJ8q#hI+d!jK}ttp*MY#!&JRrx^a>D_V2Z+F(R{3P55ndX^J_z_G)QK zxcBAart!_+cSM+{k4DCr6k-JAKq2W&;TE~KP1q>ABuo$5MbGxj_173oeBiKHW7|Hz zZZ$Lv-)t>X9x*pc$$&Xx7njDYtJKSK_(k##PRZ;KE_3oLmEVdFGru^46D!zO(67CP zBAbK910x7O#V#P=cYH|nxDKJ+Vr`-b1V<-e=s%X}N?M&l9qJbp-jhyi6=ALhc-?mu z{^+Sr^v05|8E&B#hDL@A%7aJK_WBVXGb&Zlb^xSyTct_H7GU5af27Yg7w*w^bNI)H zwCb^qhvREclzg+lx-KBxXCyx5YngIw6Hi@LRMv%Ao;FtboqR&wmKi7&<}3e^Wr(aD zVzOb7=$a25?RhFvZ(rmT5I(~k>GQlJyQ4(?7>Pr|ZPL-tT_JW#J`_z&jeA|*yfi5~ zxkjol#0FdV%#wLq+d3X@wjI<`TlH8Q)fWWN9%I_N9PBaN>rUAu#y;vX3_lZmjEJIM z=nT|vUpyrzj5)Un@P>uSf@8zdXC|B?;E_BJyH~AX?Yuu5S5<}stC8V->lm)E(n76} z?kb}!u#*SbMQ>6k@pTca#JC?~e4YlIWY~rHuFM@f?C!Dd%(II6nQ^+x1U~Z&l_I1s zbfkXl|Gazi@0GzJj@wVy<=`z>c07^sC*Mq0Z{Ju}9Kmf5)(l9Ysmm?UGRw+N0SGu* z!=6>nG9D!qc$b7xCPs53g|rKEu+X=HtB;W_Eu6AJD0GvWzmdFow!MBU$Mz?VXyAxp z9;SYL4e82nms690RB{MOv4V0bP#0j+F552CS-NY_^O2%wB-XM?g10urQSTDQQ^GnF zbd)T6$7xPG6xeOO?imA-tsqmX;e-b5-fbY)@srCyNVu3vlkOOGH0KwLVnz*zZ<84ev&xC4R2 zNzvAu&rKXHeVWUwJ@86o3-0~1s#8y;JDHGFV(1mGWiiEOg-@8%XRwur*JOQ zdv-x5?zd8MZ*6XLBOJw=UjXbxEnAqjXAK4Qjmw+q04oRF)_OH*7#5`bFJ~cN!!l6rugXfy^|&4c zUh5cK80BVeZyKkDtLX=J$BB6tfnU~pVjxC)n#JNsF{O?c!Wp%DrkJDB^{I`eTUddA zdyE6HDoynUYfz?5ARTF|&zfv%-({_K4zDIjiqN8tK{uj87xeF_#7{pK8A>vKe(0Wy zYr~(WF%Z|}`j@ZkSP`qYor1vl%BHxQpriD>mCdeOVdR)4MAE zIvQd?nAu6N%U;WK$PnI!2PXCyndgK?A0 zoj+D3t&rb`#6j{$F{|SCPw#bB?2i8B@MB8zbXk!))}1Nc2}TCGq<(PKNt zMVEg?fmWY&B~-i>({P3B*V0%XwB~rvBZ((uX9Io_p2+ixsR5oGSD<2rPlqrnpEgU9 z?kbPR0vASd+#I)@tzkK(P`wgs^EsOn?hXe%ISo1?uP1sAyc=lEUz5hd)DYLv(9GgG zw}m4Bq*33^m*t*dg}0u1O>X;Tu$>u4#C3-?cxbBV3gFOc zBCO#KS25S`fZxAh%Eya&?RA}n@1=C{k!B5Z#cG$Q3bX7=1)syMg3cN@nX@F)Gp=JQ9|k7Sbj~l zX)a%Z>ez~@yb`Q!Z*85!jhV<5n5!W+uPO~vzb)l#i7x~6-IJS>BO(|68rJ_N36XE@ z($UEy8C+7wUGD8hG{PZ0j)2DPxm%7Rp)Td>k|pK_D@TakRKGe#Rz+G+rz-hc(fSfX z{v7%2u&kTKEy;l6RL6MpScfJ$EZg_K(I!6H**=e)o|~CG9l{|FN^eoF1Xwx@o^h^u z+54vL_Deok3DRT@av7?Sg00!`Q6W7jb_w^QU^R|?-NxaR%mr+WFrNTe%H` zdzHgx-}m{WuWhO7Z}~26{kaBV%}?T(ZD~`qPi9SD_M=nm{O@{a8$3{}A7XgXlC-Fp z$+#D_b>4%n`?Vm71I8mBfW`0oAO*aLv{I7YotHLbKCkxC0`$7m|3+Gy2@sbU{tzsExK4?=$?My1K=I2}(@o-pgBGgu$XLD+rcJ2kQ-W830SPMA^{wlQ^ z<{ZmzEhe}Oe|UC}-R}63sTgM=>$zhSa^UU2T3x8{U)6_NLi>+iI~-viqqCx@DM4-> zj%@i>mbD}`9`?Yt*NOsDOR%9>hv4|em`err`5t#K{y5px^nVfeUSUn9QTs2WGFF^H zh)ACCcVpyBM3pjpaJO`AV5NbL;{i!Q0ZMth;-?_mrw-% z?|l2Z_Tm2aKH4Wa&6OwbyPj3GZjtx)O^X^zB0jm7 z;E4nLA>iPR0CUkABl3V?pZWH`fUxBZc}f_5v&X{p%_elMwPgsrnxG*Bl}0P186?`z z2TkR>F5hddH9RNGH4uiP6qHdnA-$2;^&8p#QAeA#qhS^{%biuiA-_X27nz_;L`|$b^RYMzg67(Qzic_EMVjT=ku*!_Ab?|VfbXn#H zz*?v9c9|F@QF=Hj1khy9y_is_D`tt#IZVGh%rc3(_};#huemJRux-(w8L_)@(0uvq ztEr07tia6k1nda!4%bZi2bJ-8-d6vd2^wlsZ%GD}dwn`n*_Ab)ZFjXFtS$A2lOlb# zS{WHSx;Z%pQqSDGBk@jnN^s4gd%wf&y50!*-p=je96?G_aBnLP0ax)eBIi3zIe`*` z4?KX(6@LUX3#r^k+Rm+3Wm!wNd}810EVG@+HwrE(u~f2YKsjO$TFFP7qs3Kn#2y$B8(kFnAj8@VZC)N^#?O ziHVQUkS6m)k6K+|(t4y|;&LyS>flMR?HxwQ6Bg)0kXD6Hbqi#Q9|CUZhPB#z5?^bOqpV= zRShjgE9U@g)_DzW#pFKM?S?m93qmHW`!ANyIN~0UmkRa$R10rB(ttKa^T^X`s z7Z>X*NbR(2{GVnEo0=a?b|qeiPwZthB$5&ag8zF^IeisLu>{I;K;{WkTyx(b2?`3# zjdsdbtdwKQ&4GHMC2b{EjcV4u#i{DLAR-La?q4M;FoSE&k<1Ou0q_V!N-0*2pMn6mS%p^%Iz%*WK%G~bk;{Dze4A@O~Wy4w0zT0KQ!k|eT3 zN;-xgWy zlhZrIk3Nah#Gxg}jlDH>P$0fYAN~3S$>Ahj(|yi%aKx70m|nKwLUy<8M1 zse|YvCmeIXV)}uc2KxF37u8dx-wy#RpdqlL@FYqH9+rm-q#$<*1Td%~qW^rReVJTp zgKujw-~u#)K4FA2detKHeAVwYpdC?P(D7gWamN>ezWYzmPEu!6oftYO`jmj3u%x6l zTZ>YeQo1RBZu$H3%ZZg&c)jP74t}$E-S!n}RZEyWs#LhmdFmJ{(r#(2Vb43?I_huC zij@Jn4$>8^JGTig7|E;A7ny#n2$+!JHc8-RZjn{6a$qVaiq)WN4T50$Wf~;Nc>(Oi zNn)uQ1ZRF}gt-D1OCe(@|kr*Em?wNm3z8s}0lc6Y^(XJuLqFQh?HS_M5M z-v!k1=s&E&y;ao??q5Wi>|e36@s_%|djt-#3>AxXuvw*Q+5e3HT`1M53XiYMa$V$W zjd3WkzLbQax6E*$%}JQOV5II!jOTM3O8XY6i=3%@21#Szq2MlM>f*>9(bzQ~&dOhM zB_;XH?Fn@Oad8`1O7BgoYD{VF&LQ8{Gal=Z*;gQ=1Q-z z%!AUXQ=Bj23@W|E%%pM6BM= z>Y;A}MRtg;5n$`_)T4rq*u8quRkUwO=&x$HThr<~+wNL&Ly*QR`CmrBcR?fM+C5}F{y%J&PyLq)rm!q&VO zb)&GUw7_YQoVlU7dgj>Z+Jt9T;u4Du9e~FQr#Xe;i+$d9jOI)man>U825+lHyDa%j zzO4oxt-6(!Ugch)vsNL~p~Bd-Y!bEG*4ZUpe^smyT55hXDVg?p zsMbWb%wtY+O@^39dVE*ZT&(5NG~R{{msoXhPMr;HQ_wVc!PDfU>WPYC)PYbzGt?MN zVOr*=wV158S;3=5Bw+Hp{QWdlJ8d$IC~q6k=<{vZ<86xs&W5A3&H}b_zE9~xpHAyQ zF28MYPwp=KJ!nyi`Tc5;&bcJQ5M)4##(3Y{Y)A1-`PJa1i3*}Q*fMz5NaO!*IrC>+r40qX#_@4H{LHG5bIaA;SNXEWs%k^~YQoIj4P zpgsVGtE_fFS)*<)mTC6%Ru+4in+s$~h&9g@HNO^lZhrw`K<0%t8UcqYOGNvI_?Iyq z<_FY9%r*M@w|%7Ml1vP-aRfJuswaT?0uEsgAx&htg-v)mjk$%9u$K`g!{&~$|XkWZ_otTl` z^Isp!YSd$$8!}XtmK7v)?kh>Qm}Wc`h({Ta`Ba{?Xmsfu(ewrEmV+JkIgj%?i)glP zR=1to^E3)r{T*QuKi(7R`UbD`$6;by$bft#3h5i-@stX%LRRu$Q$|dK>XIw1(I(9n zX2dsIXG5QiZG(nToF?1jCyD4qE#rA%yn`3|l;<2+X%Lj;o<^Hr1?GS&{~1XI7S%Y{ z-dH?oszk2eNvMDeKs;Ut@gsWSh#%3Tawh)DyRZi_k2Y+_<`w&5KFF&VdZ(mcUxSuf z!&jrvJL*SA=LQ7Lkt!PII#?dN5%(K%hs5X;`?GPY0u8$x8=JfCXTdE&6{E{YuB~p6 zW)R=~usV%D^vmYCVBr|GZ#Ij%gk=k%S=-h~0>Z6OL*NmqlZY&3-qGz@`uiK#zd>*i5-wV;EP@(N1<*rCjcVb;{Vs zbfud+%rAH$*J?aSNPFOWEzx2htz zo5#K;l!&XQ?CHC>qKp^ur2bw)g9Kn~&u^X}QTeaqh-QO73=~A!(Y(Q+_cDe_8jmw? z6r{|`QDd)%`b$|fvwO<^yDynhPTe8H}fseE7+8j8^XSf!6@Kz3%#A8dQgjYK^{G{%SWp%CLAxdgA{OoA&CLVNJ){<1Ae^h792R&_U#d~4M!qu``tf8e`j4)* zieEgh1lnA^v35_U+GR`M#6OytmG^2bQ!3*4IoNfB$mRWbwU34`w_gzV#3ypPJz5A+ z-hfjMpV(F}BL5dppbtLLle2;;7TNm8){XkZ|n@KGh-Bmlh-JNpYcF@3JfN5TvpR94c4|AHw%!Op1jz#T#2T&%aMnFe=J2FRQY{k|#XSd))F3hqAyS zab}kX!+EWOmidb(-bGV|efne+Mqj51n!tr2besx|k?D@s7hgQBTuz>Y9@bi#YVXYr zgex3e;Yz!0E1qqdl%a=t)^FDsPlhsA+oB!o9C@y@tAf=GUl_hM3@%Km6TAFj;k%l$ zhW4_|iT^GfVultu@W^JULkuRRZ04L7LR;zfNM`q?X5e!5>w&7b|ePd=x2t_ef+rS0?O^q;QCa4Zk8QyoYtck&ydfM02?~g zAn7Lxb!Si)NcRjQ^JpKz1>3D`Pmk`$ILUylxS|G}?U&M(oQWziETjM6@*8`b`4Jz~ zWhM9?^nC#h*acZ<%)C_#zD9JUW>6ozTA6OY{jArUxTWSTrSgP!CIWu03@W;H zrBxa&Ge!qG9^sijHMZ_I_MyoQhK7RYDo4DvihwqdI9xPpj7%d%|0RPyEq|JAYZ3Fs7^_dZ z?d0H~!vYM25$Z?>hMmp$?n%))7~4OaO3maagn+PoWA zJ+0BeTUDl&5(;`eD5JXj()ao@g5ld9zun*=-bYQpA3c7cagcql^|j|0oAQKTJi+>_ zl|4WY-1zt{>(|1J1GN_wnwf}&+nj{97u!#@jgvRO@ei$9@4GlN`)sJf$kPau70_i~ zmENY~l@u+5?=wmv1Lmz6yD925_KAaKI;~`dZxQ?*i5(>|;>uzvFp&n+Q=}r&6#V^# zt$@Sa!$zmk_HAFwZNcs3#Y3&y!u9C>>9z>Kx}uQuF4j!fq*26@^xdT;-`Ggzn~g3t zQQ*ZsbNZd4(fN|C1*Y_2HnFv6~{eH`*`5DjE==QNNQ1)TZu5-H@wD508iknz)5FRy6e>4ZkNk zo{|B0EMO~cJLG8oaS^Y=?;s3}6yG)(-qC*i&oI7p0c=EvTz|ea6Y4m*Mgknr> z@_CWTwasrAwII0fG_ndBU>d;v-=(<^ybJCIX$%(6u+6Xvcs^~*k0&49icB-Y=ukiP znTd-t@b>vRM6_QbsrJNJ&;iaM*-+Rd+jAjcDJ$yb$L{=`i`9zuYiW)BO#gii`ugUI zbGT6*;=H75%e#UoJFAhM)J2qlXiM7(OtAz32mn>lS2eEA>%l_{lWOG`8ok=8<6Etk zf|lRs#h`0{*L&$;kYkrcB2%ZPA?_buD#&hBr_=7K%kJ0erGV0FNr+B&_I%_RL) z&y;(*jj0_dUcHFf4_`PF8-NXUNWuUK=NH7pf3E^LyanM+TF^Hp0t;tast4I-vSKrg zY)p?lV#85>W4g5Z57SqG-4L~v>ifQJk~yu`5*gBlxDOT0|A)Eu{vR4$O;jWYj`j|N zMIOL6v$*_UZB42)7r?^ck;WXs#7$t|ukqI4EJ>z6KL47-8#&@rNhdEcZFkLD-6e(#!()t@4R>QawHHnpBKSs2c%WgVbj(48v2RrIASrdU8g&THJYb*U}eq z!dp&gC?h`i1iWxG|2#*!;OH$!s#5-TpPGOkC)8%pc(T%N`_M;B>(D)R#F?D%PSZX} z=4ECpIrDbIRtj&9RN60|@6)x+hc?~Ybp`%N-Rj{uD_Rd8o2!Eyw+8!=J51i5+Tvi$J9jCmamQbbt$nzeT z-0`LFW8Fp11smYgK8$t8#B? z2oRlL!{)HMcN5@(7y1kU{nfmHU5OcM@Y*5or*9)b#RSQwQAJw8nRvlhW@o$Gab%MW zBvO+lCT=KI)koJrU$e`hC;%SLJ$d|tF+!7dFS>Wb=CBWy@6GRTs*03>cUNqn`5V?L z=|Ay|-QBF}nyc1o|8Keui*_8k7zc1UC`Z~8-gic+(1Olzza<0_JM4SS21tuG^;;>} zVrQKIl$13{dU7HU-#X3d(Ot67hSK{6Zbv^VM-5AS>d?P?PJt9ZmxTFgj<7ja z)+LV?``B|+&OShl*5H+V$!P3+TVRx;ti^k4y3U}Y%YNm6dPmL=4OadV6gg|SG9E$RE&&|SXyQM2Y27l!vvo9)&0&2K^Y=9a)z>7y|pYM zh=kEu2{3C2z$udUyh{G!LPulVDgH14!04}S-C*paeUy#9^7{v4yHC5cR7;9gt3hNV z%YB-5R85*Z;5nA}JV^Uwhl0hBA9JuD)o`-pwoDKFSc$F9%ou;-6u`fL?`2FcIvV$^ zrPdBE_zJsi2jZ`0vVWQ_?f%q||HU&Q9cJGj+u%xNA-}d1ynCNhP)4lQFH4-7@@+CE zt~&a4X&PrLR`1_pPlsf+*148(28X0Iu_Nw@PX5})f?6ucsOjk%J|Dc0!=1o|5coT2 z=)YM%O*^?&cC!yYlSoXIBOmeGfBL0UJATh6(&?;nVN_De{8^AC6c4&0Qkr{`Zr(Ba zy5#};$Kj3n70{EQCecP&4~SjHcDhtbm`z4co+Bh{W?8G?9)?GdzA9Yil7r(iP49bS zYZAW~K)RDro^Q8`pJs8O=I#Db-bjmi9=$a$feT+B6(wJy)!m_+&YCN#waJm!rlqj6 zOj~=2Qt#pqjq8hAFf){yYu>gMXeC!uRgjS$tqZVVLHM z?Z~8PlJWE9agD8Q1VXN5HwP#lHgvd^1AH;Fg;uDs3;7L@vV9j?H>;Fb5YrWNEg+;2 z+D(E9F}OhBFnn+f`ut&N>czp_0kR6x0tJ@WS;1|>6{>MP2D6&xTBpeQDr)EBw-`06 znmu^}h3gB_hHlP5%K9ftM|b9Iwh~8v?u-KPlR6%cl}im+2DJl)@O|9kjB~C4npfWH zcx-0}h5+_M!NK5b+mTIjmCQsUu${V%CF|bn`&Qc!5h)8Iv4b;;l9tE=L-)OSu+vfM zb^e*ld^>wCF7VD6e%S?UjSno5q^U&z#+gl^gF;#^3|byB6XOuAR9*aIXJ37HDk}Xa zOBi)-qqn2!0KA*UfvK0epmYXb^ZcI2-{^GCtgwcUZzS{`<>19JS_(SbZaZU;935yk-(Ue)q}8$@QN8 z2e;98%_m#;)F9@i=&DW>4#h7pHeDK<95HLUHq>vgpK9k-lET%m`$~4+sGt5Z6<|Ks zB^QE`(Tt_OvLiprb3Hq_|C zM5F8RogtT7hQgPgS_H20<#!M}WW8QK+tca?6wf#c4n<-IvLVC?2QVm%5hTjEE!K3 zoX3*JJ`KCwx+s}>GrLMNMTFbK#A`Hj8&n*3oD*$!3Z0h{sP%7tyZrS@7Wzy*Zo+pQD4+4k>Vs0m*|Nwh|4i}F?UQhD{%2_2S%1evlT<(k;O$|Yn4|f z_4_FXSsYKs&L}e{)ggC$$IF6wk^X(WpnUk!j^(|k+Bb7M>;5&m4n8Q;Vf@fR%dO-3 zf%RnvIhU4hGsY&qkC?Qg3fA*|w@f$}H}xeC{7etVoO-V{dWG@+Qz6Rwqhu&!F6-59 zsaxo9l3O)fKiHD6$K?b(;bC)}`oo7)MsUdP-85q_{{}{CPSe{ z1gh4VJziPDNS6CFU@g6p{36D(Wjem?OY=-#cak29A6R-8#PdKcOM*K*nfBCj6<}X( zLeJ1qqIov7)9Ds8kW@6HR>_Fd^$1t)6|t>Pvr-b#LmEo7mKeIM@h=_**R|Do))eRw z3hQ)!{+3@p5xQLZXEJ|2{qE9@@xar%H%r>B*eDda17(-Z=|Cnq9g56n$FB1B4`DwK zlH4G`$UeT%`TQtXR4U?a!&smt0Q@og`xpc`>2n=!#Nrk&b4z5;&a;A)mJ{t}#ZrkK z!OT+cIXsVKf3=K2#bd?#VmYLT6HY9}yqS$p@wczC2fm8L+98oVl#-GI$3&T5g3F!$ zY_bM&i@Fv~o&OpXE2^zrNugEDH4PrjY}2y9v|yD=c^0*=yfi;HuRh!_enlTv9nug? zFCNr}frUskH%7cgxGC~!TGV26SOpJe&TmokqM=`NanVop zg4SCEa&CS2Zel#>QWh$dh(ossM2qKe7g4-BZPu=gFN=cE4~#Q$RaMpBr+SEkMa4q^ zKHK$g$iTjA&)&*k9C%z$jR78uE7{mpuvAO1+pXI54SvncVxx9RDPlvX8$KE%Rohd$ z_@Y!UNWGGg(VNT4F={F^?>I7g{`|KKjp5$UozNn-iAi-JkiE)w=LTyG> zTSqI!w|bdbnNEH;S83wzdyer1dxy^7Nk8XIS2jK?g&I?dS+(j8*;Xri__t(wCtv)` z)(6D&MJok9V)U)c)Ik4wR?YL5$cV_-;ST#|MUO5i$j0bev}3wJXGvK}MR?~(T6^(y zD}bV`67bANEz_kQ^7)uQraSkaVS-lD`j^=+_00Z=n+ecC+WTYjUNdA;a)mFeyET_F zvivvo7cCiYQ)J(*J9L`p>gr6f^<`4RWa&Ka{w3;{TD{JdIjdqk6Kk}z#4J|*NY`;T zQx0(Z;6bqLzt6YEPS-)MQ=7b$Ec#7HFQAN7@B-xMj}u0-Fwp^ZiACCl&%c?2REo-GwJw^V zR4*cE>E$V!oqCP+I*%nQ+dT4nD{F=Z5>J{v`Tn8sQG!!!#92Jb1MD1m$PTIThuN1q z%rOD(gAvc4)oMWCz82`+QX$1GIl=J_(RtRAJSa!6G#YY| z`A4&#+`s5Jp*bp7ZC+hIjr4UTc&KDoC%!ul_h?vBB{4hfL9CalT(xAs8Lq*=SW^<4 ziqbLFERT!l^4n+uS8fuK5FV+Qo^abBDbX$cv6~9gze%L_xn}`#h@X_pd#{d1-}S3s zeA$qmru0WVtxHbe!QIK`oU`Mc@5#aFO%KtC&&#@`i9O}{eJmm ze*|h`Ipkg5ny1gkO`G{heP%;|=ofMUN^hRU>bvjGQ>q{?Fdq~(8d0DpV>a1ml+xL} z;F@&Wk+uAdJ}=7LTV@?eQJI$jj=3DYcPM@G`|zwS3Sj1$wPXpwF?pz4J3RzKB;V)b zh%g{cNMI3`{qoiqy*6qvr!~-(Um}JY+K42{ifq;t0{t4e*;;NIi6yPOICC+An7c?I z7|tbzNO^vO@y{iUnGS{>Iv4$Rp@ZT5+%@PMddl(=iTu#_)B+*HC=ACs9J?R`ZO!J; zsLbN>x^=sdKg$cqBIO8MSy({8HQx~CfvNv;XUOO@YRTu0$Wm&HSBo4p6~b1d*pi*B zNY~cII=vlVTql8}rSIRo?Jlf0s1r{>9~U?Y*5TIcd?l>V)O_x_^*T#~c5Wf?!SHxSF}=0``^7taiz6!4MpX|VNR z=L~2B2?idODQ@}e9eFv}RVieT8Icm!mkj^@Q{#2MxOOuQoE5w3PR^J|CVKVKmeCig zC^|aun@AlUP71I=uisO0CQa_!=TryeT`6#4sQ)=JbGY<>C$<_d zkTGq#2mr|YQvX&Fv~qyxzc^~X`Q!WtFU zMoMjShcaOpV5JLur!cMawXh=zze-z~@d9bF*+?Zbm{wmr%?$9>PegL;RLIA z_On7}sJdhIIe)8EoDb@ zjtDZ`_@1CF>D353o48y*vAnrxV_8{@?7R92gIT@2Dpcq8W6AD0TvZn;5hel71Mvrq zxm&$EHR|_lB8)DssbJ<3`=ixuvoM^s-gahZ|ALbF@&-1Tj=qGoW&K>HAL= ze8rB@VAzlf7LYEPU(f}hIrRWY-alG;dB8CAn0Q8D9dEk=&ffp*aj*ja$2xB$^g^!@ z<)|%JAcJaFg$ndlGVyQ1TIYRqs%Vz)QzBKB>E!r0s)0*GU{Ap$60z?$YKxbpS8A7L z&Y?H)(>K%jiM#4BXwX!xf8iGdBa-j@AH61$r>Ut-5t_6(@6L3RXXv6YhdwJ75fV3J?i*pzpfxx&P(P8gP25!FK* zdM{Dw_bQwX1_upL-^z+baqW7$7;K*|CfKZKga-=KQgdl>FLqxIbfELf+o4`nHbmU& z5Lr$dLV*Hnd6#MhwMxHl=Ny=O6*0Q^>nj)){oU&IKmnS8jRW*fX>N)xgHes&c&>#d z8?A{kmeL}2G{v~*|Eti7QZo!iDIP2F%V;7qV(9wce6Gq5X_Km+Bcs&B<6b)9?WEDV{>><~2!#RWD@$cf>e({8e z4tD*Fj|#q)zwC?E+2hdMbtSz3lsY zs*}bol1x!5(IAuJkpLMkfrAT~dCIW)8PQB*V^z2_wu0~)Ugon(O@{=96RMtq!)tnzCN53ykTSX< zb0p`S@h64xrA0yaM>Fx}z@S&kjF4TEFZK6%RI~FO92Gl#x2Nro3fd4wqr6WLxrSoN zI)a}BR!bixWs6`*q&T*YS<&2CSCB^GV}{ zxS1e8UL^b@I{@*-TwJCF5YQ1>~_W|#l?*-(g!_MmRBb~2J^gl6!q7aCkyEdWd~QJrx;?S zi~JjA#|f>o9<>X^SM1&`?Sd!a6S{E-I^C~D?B#Ym{-sxZ;jTA&K^nRIshdn~!18S5 z5o42*M2uXY#GV=w^Byd{NLPucudrTi|Ie=9sqMwvU%k>!4wb@^G`%T_{ElYmfLLJ> zY$lxEsS7U*2=Jj2s?2rmeM$;T3h*#}jSEsZmR7kxnDE|Y^3#fD^j6ZsZO5fxCi!7r zMTw!Up;*i1iQw%HA=@5bNAt~|F!|u~?}D^3-IN2mmamii+@nI}~Wc zfGcU!Nbp9XB5{9Wouc5MH$Bf2+s$1ot>{PecXy6H=`sTw0&u9o z84{sz^toQ!tEue@E=Iy2YB1~_8_q6`fI|tsWS!=#}cR7XRdZ+S| zPq$@(^5}}(`rAfA8Hp6%0&2z*Ie;MZQ-27i-hG|KWV`e~v&ldb(?w@j{w#&CS=G;wkcbYJJ#<3`+puFL<-%0$Z+^H(eTy4dTY2%LWdiv z0gVO;o?jb13NT zl*2i@&wTJqN!q9aHv-Q1*hKl*+Uz;GmNUk?>U*6y9G5g0!bKUNx{A?*!>QA*pFddB zQf;Ky6riP5`no3q7Ci+qTmH>kyE{6%n-=QdM0+70lgJKa53s!aPNMR{)e;@gdG?$A zaCM{N0UJQg1yV0`IkwDerq7o9x_57J=2{P9vF0|@3gX6SFoq)dQK0oZGbD0^Hw=Or zDK}k{Be63k%F{vFE=HI)NUIq)VTlNo&F*OKNvOH`ot9>QxoELxvKykyfNOiFrOy7s zryrif@hk}z!n5?*mA5;_NzNwt-NPexh?h~kWRa(2_9gQ68mikS{LZ&-yN^%P9t^wB zFUnr8WzoK9cu4u0@s4D%2f@(-wpriHA|oi`ny&ob6@#?X49Utg_=%RR zGNgD@e7(7jnh_)WiPA65R*cZZ9LDvdKJ@|SnX#vZH8%SEk$TX1A~`UtYDdR6`qOFD z`Md7aX2_BX-Wd9IxKWF$TEFe3(d&(=wS$2}CTnbZ7L5e1eWW6n{xe1YXKGB(agPG@y&GK!$)~U;(e^gTSma6DUKWf z150B7#^K)Uokdm(R=Q&{pBfOZ#-dZP>-v^`>qXOvnllPa^$1gNuCd4Z(uq02?P#>} z*{6!AH1t;Lv{~}aboO7Jl?K@lN+v6ATk(_T23ZwFRLeb*hahfvKl`rF{EFu5KAVME zYO~}&@;e#2hOQYNR!Ihf(*Or+beN%|Gd6r;7laAYmZI5tw=bMDyWU7i6N$B`d-7}% z6!@flyI1%olwv7S>SNfVTH}(>1EO$>)mq!1&3b%(V!c5el(td;`2sJ^aT%S#I?&v;9o8xLQhWjCD4`x11*w4)`+?YxVlO@fA`7hXbOoj+GE(Z@PHfVG|IDJXQnm)7Y zLT`d53zsyLXYdl()YEw}#RDjNsbqVlc#1D$*&I5YG#++1IG%3!V*1m|x9sUYPg{e2 zG>sg#F_b|rmFPXD9@@DU0c|u6n!MTM%`Wq-FC0ct8!o{8N?k=37NN zDUMcymY4_GW$0i?ho8;vp3KxLDg&VRvYqQK>jU#dtJE=&G31Ie-dxfkt3#5#P!yL< z9}Dlk{3*!nuMgd-2dP_BMt5S~>m~|zkPbNBke?T%YDX|p8%9!eNn6H(`E25|sFJH*JUc290 zw0XdyT@fLQkW%mJ-)VVmP#V$Qv5pe(*1=Q>V6d5pA%YM@zG5-pYxWD&qrDR*-d=#p0L&RV1(MbGsvuMdJ0e#7lk{PsO$}C4{@i}1 z*V#+_kr%nCBl(mB@Yi0^NU@72Z_bN52c2S$;wcz9!XD7L#yUjcZ9Y^xkgrwCktXxR3Dh8q*t8y+TGkS~18>O4Zz; z<*{=a_4`g<24hnf*4A@YxdRfe82Rt@`2zcA&nj+Z^4co+p-Oz#ttf_h7>TBGGnJ`0 zhHf{;Wp79c6Cs{4)K|O|&tnc5mS<_=d8=J?1&x|(pCDg;b zYehQOip#@qey0ZD>SX!y1+d4cU+0;+RG8*#+dG7f_F>*v$M-9pvJ&z998m z?_#;oD3kR;cXFPgN0F?sL|IXDcEx7MWus~MUf4z5^U#<_RBE$#zdAf#)TpLPQ(pZc zroJl-)m*P*?-|kmIprCVxRB}=Sn;&mY2^B+VWmQ~hFFqq$zLes{q>aFF1jdqhz?q! z3Yx#-ebc98fHHC=p*Wa<9$uy~1P!vbbS0ZM3v%|!HJZYjp2`u$3sm;#It(52 zyq{w2=>w!q^1ku5u4{)+wNKCg*mP6nB6}-<7Gm*y!kD);^jXS*GijR{;RBgXq*KtM8!j*dC`i^mq5VMz3t;WM<%G3HpU^O2=9kFW6!HUh1TwdlzuwfSrU9(CDH4P#RmS&=VZ{Z_ zOLQ3ARgu?Codwbef(jl9^gj1R=`m9ZL8ECjaPfuK+X(>)ibeGwvhI127VnEnSHV2f z_09B>;bl}@^D+xjE2z6XwkRQfqnbU+l2ks!#HW&)Y{juyaK`O4I^f2`^)^K-mYX9`9vn|%^X7>}Z0lxDwH*cH(gV}EQq`>Y7bpnM$&Wf+e@5@o75U-@QI z=>X-HmeSkq2VH*jvBgJ3pRAHp!3YOZH9vJ~%MucLy`uI{xp#Mp0BGGV-KB?Fzy*6g zLrJL~IER44oQo4%%2V|Td5_G1K?{jkBNMCG2brp(C`hM|`!;&62yU49q^Q3M`p}mj zZY|mZL_NSdroeQ*=7)(~Ixk_{Hjq$(+T9!^u`K90qC?}x4dP8cI{Lx>3cVg4uo%+?$>nb?^C-*Ql#TK~73GJ2 zfV$g;t%2FZj|dr1ED6Nk_SZ9xoA)+<@ihM@vH$EzIlTW_0faMu;C8FQ9@#l5_Lu#; zh|yXLs5sriB%qNmF0kb&A-Jsu{jlkW=Mrge<$^_h%ijE;zj1zu=Aw-~3BbX*MnpC3 z+xISN086zmlvqPtGrj%T-i;*H4Qpxq!6XsDw3BLi6d*C);*40#VvL<&;439ZmCa^c zlPIF2(|P?VI^C>WffLy_Jd#> zq|%kbhDM^E^w;{W^Q_i>+oq*qZbfTU3fY2}Risd9>J2m*2^m6khu{u@hpyO3x|Q z8IX+c+4X&z#g!Dvcns$H6T(FAy;{^mVLGD3hj7;YNMi9PbBQM9TP*@ZXi-Pe-XAk0 zuneAK!4OA$K9Xrt;gZs+vX_G5m77ZB?G9s~XJeki}4f_fHnST}5 z2bk!2*(}=%%v6HWH>nwAC2*D~#GDZ>^Ay~-{4-f})WClD}oq9AKcOLYyX?oKUz94pdKxG6=yB!o4GIT)Y9v@-0f^ zxW2hX^n6|`OlI7dN*y3nHQAn96p_q-Tm>Wn%?T_I^9Zio!~bb{{eSp?ZD(uNee5#$ zd06F*PgDKYGn8$boS`QP>Q+>IH#NFjnDaBif86QgBukxPQ)1a_?0pUi61z%_zf_M( z9I}_mC~b)Bd(8t*O1GY7?aSGC1^Rj^qbD6oMXPaFq>M5hXNY-)BaiCVUtZ?Cbn&5- zO#x;nbA{zv&)ncrxWW@_I0_P1Iti#7LskhZJOQk)ErI3XGHxI>tg!qx`bGapl&Mbd ze7Z`}j8e;5h&Z_L3tHzz$(1d~-maZ(K$VJAc)|GIfZ8gPz9@Jd$|7XQF zaP;-zzTzuhf!?#z{ga-Z@>^kiz2G_zd2B^yN9##dKrc{RpHOdE*{C-!lv8tiFoT3q zeO{K`=`QzbM@W6xKi#>zG`^I!5I%QyG;Fc_4Nh;)HN@IKF_LB zXE>?2|LS>+@1Zrr_-MHN7f+*K&%5952d=7?1u~{n#`mUr2NtDkPdW&G@KeyT@rh0Vc@HtskM0i%FBm4)lQHg?_{a+9(`TS_gezDJ){zE$vjWQf{^q1xedR_~8AvC0yUV(txna zFaC?(NaV(@?86@{jjtbqTi;UEb-f~s8x5_ndk#6_)?%b-GiJCn&v~DPpo79K81Me< za~Do~NRfH#PR;*m?>(TJ%=Ug^)T1+lqceyRrO5z-h9)I+EJH#S5J(6R0wbM-B7uNZ z9cM%c0z)uCsv00b8d5?eG#%+h1QJ3=kzN%ERh;kfymP*F&YW-E_1*Q}``-7iWfqHP zXJ^;nv!Al}{{Mf!5txn9NEi(hkV4Gr3Oi$twb)qf!4x)Znsm_VadM?~{kr67HSX)3 zr&80>bUVrV(vK4UZkI$gK%J_4N}XYc)uXqA&Z9$cV?iAA{0v_RMra=! z|E@QSms1;@_H2jg!*L6aP=UmmF1+=T>br>~B}*`LmF0oAP+ND@WbK;zt%Kk3mflJ) zyo4M*(!Y~c#@>|d@Uv5e6RW{ryQlybE%nP#-}G#=RU(bzPc)a^J4^GNDy`E@nfOCw zS)VO(T-hq@V_4CHiq|HWOR}Hi*>IksEt!Zk_Yf}%8_`ksLY%I`Db;Nx4-UA61}1CF zx59BM_q~rB$Z$Nu^pu_OQ$+9T;KwsH4GNBsNow_-bv;8`GfL}CnIh{cq$jMO%;8MJVN6<`Nw{LcYG1Tl`Q2Rw_6Ui@eeN<`(2>7)EJIM*vH1eiRny<+rCXGyC#dudv0)hc(KH#Cx=P%A|@%8@@8CspXYkJGT zLEzFi+=Wq5+cjSJRU*={np`Or*HZIgD>r=Zm@F7@$X6EPXDQDY=!Vw*cV#Q4Bk9$e zp&V4Ns=(E+`^XhdN~lv|7lw_dQU{l}wTfMX}X#^0Q37fo`2 z#2{-8lrpWy3<6>(#5xJapwA(_fYuOebU=IQxi^&Tmc`i4s~M1zm#WZUKs|HVK*AM1 z{5bUc*tC-k0wK8jH`gg@b-#lG3I*@jCS%&{Wg8d^!RI7o?WTw-r1lAmTR%>=1?{OW zjC&!n3VA02$(ZQQxC#FFmO+JL$}5W*$~!;^P7iQV)+%=$ra);0^DX zqgbRHHVv~FPInlwHL8%?4LclVX>5Nn#H_g4ULtxo`TV|@7k)EwX~KO>Sv)^~CSH2t300XqLY; zD=>w$j8S>2{**=m#6UyCMb3Sjrc{OZ(*)R`bu!TYO9C=F7^&-M(Gl$nZ4FJ4oq~pq zv!Ih>8?cG=NQ06Yx?2v+=G>0R<9CdThm`A1*9`Um-|EzCmP;U~#laGy^UgvT4Hc$H zGtz0(HsGi@G)J-4$LZ*k^*=B~2QzAmu zRIk29zW3vVPs5qthcd1@m|H{c1bt9*=xR~0PiIS(tmBG(U;`xG66l(7{A^nE*$T4I zdV$1_e4tWP@}~BAUT&Z4OZgFPS0IrypOX%Lj3pr4JI{;S&AVidm&{p))SU9 z;THP~p997mVf4n|fpv{4M}=O#n2B`gp1jg>zs4UToF4*i8QcX?xEckGSG1DjxRix1 z%;K^rSN~)g>78PRW(Ob>=+bSDgYG6>gh!_*3pEauJ%0yITHUPeKXqkLo|08+!H>p~ z{R~Rd3+zELtWCdC(8VZ=-PC4q&7^O|@DeT-E5fTJ=ivYFc zV@`eMdUv1ZH5m8e>yepONq>n9V#XfrzSbw*BK?=8Yy!n+gS=X%9-#LmS8me#{QJ* z>4nd6Dj5i{*y+i}9VlS$ zu{_d8ebp!ex>%UfS3MNiiUuCOBgxj`W>b)Afk`FI?m41_m14jqEGv-eg(H(l=R)*F z4~r}EWyR!kD`P5McoC zD;i>p^D@C)#2diJjy3^#X)QBDT!b7~n^t@e-K&>;2!f&)S5%*UrN47Y=qJ{>qa6FF zshC~|w`!~RQ79y(P&}>p$SbpKF?!Wx$~YfcfyMaXsr-YD7n?s=@4Yxv?RB9E-@uyo zDj}acTR4^G*-;)eE?!$qu1;$F`PD5^$3l3VVeKrlesajxgCs!BO{EEfA$X9^6UYM3 zxzExysXIpF1Z@vAg!TQWRe%=WzRNoX;mK5?38cA#TW3pPy*H8aB}d=A;8e5g5%Wtf z1GfC`M}&aD%F+zdso8s3(GE{mtfn*NyT7gKlb1L8wU<&LQ)K&csnKDs>Xx+7Jf?Bn z@BpvssT)1S$<@{B0V9$yjJO@WaPun+3xq}gik|<AlF&#KKc{GJn= zwh0j@(mBmW6!}%C-~Y@!%*@oA>uPd}h4_zo*u89{veLHfIBg|XN|#s0>lH(U-ECG9 zH2{}ZTpt<{_M0P5NgH3GHFl=u>}eH$-&91jVqy~lE1SCGI`Hc0v*T>or!oEo+BLRw zNQ#GF%K}&hyE4w&xRe%dEhU$-0-c#g2HjW|`~LZhN)-*OwWZF}JwDm5I4V<{C?D*6 zVJzfTM~b^zm}_ke9+;U-Q#R#J>00wT5gQd`e!m4yCDPBH-#`9_idf1CWMp!|>bsl( zX2gE6-dzsIkj}5AT;=z|jNvNB@%9WHBj7-9nUhAu8q{1~oh0VypFEh{>$Y;D(JVNp zeUW1wZ*&HQ`fXqYF>ic4cM7rjAa|G_k^V9yE@F$kX`yK}onI{1q1D!R8pEi z>HyTay>2v%8pIRX4cIAKV|Sp4LHzU6b{>M!{hVT_Va}_LovG5=&({jg4(SnEmWSw> z%;P&&;GpB1^vz>k%qYmL9Q*2J^zjYJrUDusDt&U39u<_#9=<7~(dr6Eq~H3VnALwt z`UQdieF#W0D)RS`UfDTPH4Z3sEfrUQP7t5>gcH!-)?KWO1*SkDr~??$+xgQE-=MFw zXzqSOowyd%E@Ny&90IAqq$blI>b$EGXvSs+R4tG8bZA7y+wr({Qr|Y1ra2DHP10)7 zCz41GTSVH{b+gvpq!mhyTS?=Qeg6A7^G=q;PyDHqNBxj?p|vhqM{G- zJSG`|?QJXJE>C}SY7-HyWM*{}ZArdjliMlv0-8$9M9Wy1C$FCdmS;U!v4QXGiqmCU zL<7NNVO)Xvw>FOwTnX7(_Ds7_klSp{%@2qJxFf+Ae{;r3`tb%efTx;RZSvG(n)fo` zcH{)6oO_b({N~OF8wCY|EyP3B1FkMp`|W9K?i1Ige!4cUUfl^fytuL{ zX@pm}kSlboK0ET!Z5nF&c@xEBZlL1tn<(j{Coo|0m8M93JE64k>5*GHy*>{ZWt3D^ za@VGyZLVZ^c#edTQS^ioi1dUxCN=x&;u>m_ye7m^_>>Xx07$&d` zdXt6Va^$5T5gmasV01U;Sv9(bf`D3rZoo;PUUTiu>E=`%cy;BHo*biIdL>eYrSY3h z_)~alCXhbJJ zhok`u$+FoYa%14>j?3i;z7{kUg3$;!SIKFl*WAe%HLUC(Ij9@?+cZya;p?KL z5M5u_f}ijATC8Kq-=J1$iwzks9HwQ- z(sEs-POm_?!*miTwkqE@qp0R6;sicD9C+)gadf9G>%4PvME_7^6>0Zv?XqIfrhPYD zP&;(>@OcoV;Vq!M!EJb(>~nt7xmm{T{{4*B;8YfMa*mHuOrBi6mqr9b#6U-O-V-5GiG+1B6_5mQKPx7Ovb26vi@qMvR(N>t_2YVXQotwOaO> z)nQ}~Xn_n#Fe2rbJbwEk(Y!C~&25&|S^DDqSwy1isM zo^8z5VhBm>3{^e^=(9H&wldup*Y@ylrq?o@mtE8nDw<;oe0&-zAgMPzs?3$&+s*-6 zYVZ|8&i+U13G*9Y1I=&ku0R|_^c{`MkKzIBTXK~}1m^#jcZ)Ry3cXW;Q z!0YEv@Q081sAP{D4=FfS z(3IncJ?Itt_Pr>-_xpu%lvcv}TJN0^A|cwkz+XMRYq$yyOLA*5YnlR78&00jc1-tuGpoU{Fq%)J#?hQI~r|x;V4#<}sAAXNmp0NrJ zPajp|>xdgH6Q~lu3q9xCQem)xPbSZ1!QV=N{t!v87%i&k?Z!nIbN(HvWh8g<%1QP9#&t{j9xZ%rRJgvvtTRvR-zRbvyKJr-!WJZraGqk$~Fu-s_Utgc;Nc0{G$yJ+|_j z)4;AhZEUx+G15KmR-#aM+6LD+`LyW5Y5xwKWVVa?@%c#7DNA!HduDi#tp*ZIqsS{n z&eZ-0>BAiE&ge7T4B_^@)!n9*x6%BHS|?+}KN9g~HFJBxHsIZ1U<=-}?WJL;Uk8=# z)G!@gIi_agJg@FVE4=-f0f&Zx5HtZKJO^gC2VZ+(=#e&A<5mMZ5;RVmw>MqDI|>jt zR!TRwx6*LMXKs4i>0spL6&x>J+LL%U8gb=Rjg3z}U_`b-@+w`xTni*RDu5Nus?Xpw z5^|4l=el@SVu$W+snJFNzx)Sa>=|u^`gYdkuQt1JcitN7dknwdGB5 z)dK2+8JKs~)t9?PM{M#EM}j(1%X^UyyCr;hCa{sL9FKx+J=DK@WohH^jyEI{yI~%c zss}q5w7?lSZQB~B)WpL&cQ0}aA{ssL`>%zPE!2eCP2&#c;`uF$-S!}R(XieRN1JfA z#6EAm5lF=@zHy7K=h`tGVWUdqnkzhD0Vd~qu235+jOUcxdoMzz$Ol%jM;caR_c8bp zF>(A?y;o-AAd^Y$bq!}*^%mBa4r&K7X-{^*Gh%P~#~`U4n6o+Be(=a!I}hF>-Wx3j zp~F^ZD=w$MIdvyO`*L8*j!LQ*VqpGF{zQ0DLyGabUaVgQx^TLTQKt2g^@qrfM#LKv6T<0$es*bi z9h?(BP#7H~CN&Eza?Uj^<-l-@<(?pfAGAcV5zX58^hmhBYnSP@2>z*hEbChpg*v`u zy2f)cBN{7RBu$ue?D6l02dsmt8vv3L4goy8kcIBB6b!h|`$m66xYcF8e%MCZxdMNd z_jS8thCBN{<942SW0E2f!hr7Z5m(2&OfFK`UL2!?0{{)*bU0GS>1E~uuN0?=YIf$E zacn#;z8&o!jMhs-sfU9gdUg3i%fR$A)g!@~XYz?D^Xt+rp)dp{A_Nk7`qrr|B^Sqg zS6wu*ChW~RR&{!*HLT~ClLHWD?1#pkbVw}VYf~X#mN~xf*8wlyCcS%da)gD99#xH!MO|CV`oe~q5uo;=j^^O* z=1uArUvT<~vw{!0V(c2@Hj5znO`W1StU)c(X9S6DOx<0Ld-mYg|M;!q%dcM$_=3O} z1peD1u>TwpFMg~wxQso9rM;!6FvEsG)zok(d^o1JWoFgmNcP0Up#yu<0RPqBn16^U zelsWM(eUk8yB*V?zCnGm*n9WW#c`y@JFAJcOZm4C?!JC@vySEQn$$Rz_9fdFNdE5~ zB?d{eCkGsnkTb*zYQW>PrWoHx`47j&tCLkElgAu8z3=_Ey`uk18n*BF^XQa>SrA83 z)&2eQH_NGJ{7ODG`2_+gADNy4TMfa+OnuYnO-Fdk+vnT=Q-(}6yTuM{5or8J`MH2ns?q1`?jqt-x4 zuwjEDQp}Z;0BtPD5U@l#9C_f;<(FAsxqVnlyTJ9#lm30KEWRx!3yE~!)9et0B0|>F z*`@6u@nA$mb)p6Lhk@*mFB^7~eNLQkUh7n~BVru?Igbp z3WKkWAh~~t$Z7PQ;;$48?p#Z~9N*YCiV2|7g0xXEDcN-#;1LXu8P4uUPC5-KyJ`m> zrs560vcJBg61T>BUA2J?GM;0`S~W!)xTW3fT}ftv>4p7~71`^lU0i1Td|dUzl0k?X zIh)pCNE7Wa51=JU{UK724rqZ0z(xTLDnl{*h6gZP!PU_!MMmC%ko)g>WI!bbC@~B5wGEbh}C&TY&r-~NLK+2fesWTqQ6627ad)5JMr*` z6x_+8SoMN9W5(uSPMH0QrzX;(!501To5#~7lZ3nK|f=+4uX^ePk0e8SRV zGgfkI#@n80sPJG`X|&8d3UZTHdP-%1RHHQWJG+8zVZEul@XK@kU@JnyML zyXYQzeerl6WZ{BJNad(^O3yDm{`h2pG1Oc`k3GknsN>ttPfu_%opw0kCpT z4$dV$F$%Q0(NoeSn-Jk!FKSXp@;fM&^A2&~@$<{p0`BhJg1AVBrRwan>gy2uK08aq zOXp*-^h8=Nf(3V~2N`X+j|s=6BGw5D%_k0fb?5buxJ8I&Dk2~iHFLvBQAPpc4zO^W zeuP+$xjgNroMD}GO?EU)c%sMJze{eO4^jUP7lX2c1l9r8szKq5_=54wG$r+3y-{{_ zS)Ol1oo6?0enZVVia7fn<3jY#uL=3Y3u<9bu&@VXWg5vNQw=}4G~axHFD}|5jSVEP zcvv0`leqWWi{tFWl6!XV0R5i5&LWkS1pJ5`;8US~Fvn%7N}vaw4_HtY+GHPe;tWZ>RR)S0n(8&XNCztpo87pJ^REJ%#w(-({`dNbt4@CQnyURkjxz>x`k$EkwTvvJ@(jixpDoD58G_{>}GT^PGFv9vL6Zh0J7ohMD^0MRz z)`Jn|3C4j#Eo#^Y7b?<=ixeZm|wa+j&u(}=CI7uR^Fs=e;LiSVxT-b_@h zQjgXonn+K_kU;j51C*N1LPVeZwj|>#+Kod|j%w;E#C|uhXNdBp^7e1*;?>P825Rh$ zBB5}b^m{GRUPtGAQaQ8}9>ekqJsj*Dm0+D_J|N6D9+#*5XC^Nx1?mpr)strGpr0=?9N^@6h< zG|JqJOI+&m)m%gs>3apSv~+v>Scx^*>}HfqmBednE83%H!VkAC~(Dy_m0orZN4NdCC++;v2dEp7`nce z``7eNbp5A^?~(wsX3GjxH0hOXk+;w({vmQELTai(@w-PeWf+CgnKdWP1wJ&IQjk-$ zR8pAH`-jNEZcOD;Ep3~WHDHwtxX?PFav%21FA!+V4;Xn(rmZUi&C00WvDnx3>5lVD z^R!8WT#2RlWel0(KXAyYxBWm7G-a;NelE9`78U0$PQ9Vw;JJRihrv4`#b+%eum8M6 z&Cj+9p#2236JyLIvW7z{qBiZ4hJQM`uk-rHmesx-wIgMZ5oF8d4;5hU+?)@wp~3FR zNOa{3OR3Y%5wtP7y+m~~w(M!OP1vHcHFo*w@_02d0jxvM&nH+a6g%?p&|EWGK6lM3Il6c~)yzNxEUKKa(%lCssTXi5k(MW}B~Ll*VBTg92gWa}WsBa(IrIEJ z&M+@K0oq>(1uQnidnUu59mrt~jsxydwvy5bMo0JqzI_VlP%DEIS@Z=~Fxe(_97xtv z?&=z<`suDt*BtI>+C`1JF>g{pS*DV~IriDPot#uhhOTiS>DmX3}UEkNDUw!I< z=^Xz4AhlVee5|;>RaHlWV?{cqDRXFaaBB^|RNs5yu<#f|aH5@N@(n}(`7@Hb%`LvhDS08oYXc zk*VPOz}~@y`_-caI+f_Dn{+mJ<~ht_x`=A-NK^1oalZE+6#KE3#ja36U%;aV==mX8 z;a+N8@~M6mL$EVvpz!r2tl-7+>w4M5iMvD^Jrfgoaz8Pr6n(id91E_q4+nTiQYPlb zsx}t%Ua0n4mG#|Vcs4b=OCgpBc?5HcL@bBOD0ylB7v_x7a80 zMkC*&X*IEecJbslFr50Qx5&H4M zLCbrChClPavp{KFWt}`|b~uQwB4b2zotw^cVfU?JWfbxnj3yIPY!{~+%Kz;K_+J+B zgNf%$%sZol2Y@#-(307oFysd!7!A@U*P2PqA4=P`BGMwY8t8&k z-<6uB3}5^u0~Zt%P~ZJjK)#dNgRZWRvYVw(DDAwSjYkd?4Xs~uQ-0a+ATy<<&BVV%dv&#$y%HqCS|!E>+d(D9*(4&$c&E|`B4~mGM+2M z=C&&J-W+}8`?>1<8a(64-d|t!TY7-dLHO{7gAHS*63LE8<%cJU7u>_wwO7tRh?Cv`cWlZ`Z_;-8YIj&R9B~ z86>9j$o8^Luh8pa9XjNR=boZ<_mTWr2-m8x?aj|UvDRY_dM(LgAPJO*N#Miv32lzw z$+fYPa|MnNfxY(d0F-9x$|$m>;8fZ1%X#|lVCDDjP4>B9jDd{>YP zUq(X)vI{ql!p3#!wZ<)k*yB*H(-OHck}Wg^N}yA88nT2@>ZC9}+mY0Ml82Lra3}J+ zGot-mE`N7rE=cgTU*U_HFq(HMQr9x;wV{raygYT=C>jBUZk29F|Ng;RHZ-hbCp5Ht zVe<`lF|e+%^l+hPsAU0Bt%l_^4+4QCdbSQ7#VsWtYF0fNaWP|bF*xhseDw~N&2M+K zw-oPn-vrYz<`5=4xPLNd4H5@!(b&^z!h|tmN>1Tl(q5(HSELF=uEwxI$(ZSOizBLfh*T2q?@0eUQ}uh?;>m_5Z%$!dt}P`Oo&m_)Z& zUlTxiDUM7+g~Yj%SBYa`s^s>7gjsu23};A%oFXNy?xaerFKu59OGjF9?MiY4o{&C# zd?&fT-J;W{q1!$r7Cft1H@;y&o!9^&an@D|idhwJvfaz2uDd zMW{)y0ffFT#a!P zmlfTI0<~kz`7V<4pyWMqk7h_uuppmwssHVEs>|-r^W8GsRl_M41WN4_pN=RY9-J%@DPuZRjg8>uXx7=BTzgdrqdGymAJ_KOrX$mnQ3rIHt)Wy#KJA{qy<*~{QKt6=r5yNEEtA+MUT9)0mNh{%D_A z`jICP65jehR10DSaxUW0CJNpgCr%&FSBYSKBKtM*;S=0MfkEj=G&)ChL)UZ9wMiR< z^v}Bd-{h=(-n9Xx%&3C`rzd^OmhYIA6I|OjLS@ zr8qYbv0OI7XdArm?cB)CI(J6R8S19mg7x^{M(#N;eDA zz_GG#eWvK3?7i!p=Mx=Dw$q+B|7MQeJ~?=hP+Jsn<*j^I$bI&EgrH$zr&^x<((ap* zl9>Q8z`4bY4H_ervK>QSS*rnJQ|m`0+J>YTl?}J2vv?Y z1vA+bEYN+SZPFRD=0IABEtr;AM@i}4(g<4W`}A?gZ|?)J7qAZ?s8OuA1mdjrP;XA? zX}pvCLuAb|{`7qptwl!`Su%@u4h3#vk;$(3sYF6)ixYd|_$pKC(LuZY=E!-J18K$l z!Y`ndjTwUrX-P&zBxiMrhuFDa{qA?2Tk&&fSHU$|0Dnv!1fkI?og0Duu zCty)i|4J}4zq4u|R!rCpofjUdsbKea2NeT8UuNoYA%TxG;sLUg6hj8CV(IH>kkj@unhD@=gK zoO7~K@SMuaEg7KljlfHr^GZM9ni?^2MJ9?3f;Mn)_$St;(LIOI$R(z)@jef2@(wNMVkMIbp?0_BWpC|E3FEv%pFQ)a zrq>Z^-<9igo1)Y%)!$=_O}D?9^;--3o|$IJ09z*!$Z+w}Nse_8cmB$Kj-jaQoY-=@ ze94^(O%mtYY&omO}+ZpmVf#F1%WRJd_mv~0$&jLg1{F9z98@gfiDPrLEsAl z|7{W2_w#bJM(du@C)43|kF=f0kGFabeZ`(@JiWfU{T;S9{^I<^+5zK}vI5G-oBfSb zCS@={)28@l+qdt2ig8bV4{VG97WEtT5^q1{0p>Q^&%YG+1-mZ;<;zv^<&N@SaQhVL z`EUDN`Oh^h!2=Z-aif99VTQ+d+J0-L2L~H9x$j_+9<5Kr&zmwL1A4#$Hk?L$qW>2_IM+Dn~%3!E~(Z-Jz%86#Ejo>6oKAzU7@aJ3!R5| zz}UR;Z@LGPK5M#hfH>Y9{juyH2 zB0)Co=66ZcA?+h31J;3Ek6dyNOv4IUmc1Z)vF7+9g8nh{t#F_G-#k_G0nRTErAl3u zuwJ@CJr-9^<4(UwCDj5XOcLmIU%oqd0_|vt;b@if(hG`XNwVTm;2;L(eZ&}RZ#`+` zsnjF?749i+9#?`W+%-zRo_&#F1?Dcaq;5N; zQl>ImsWzZw`k1!?Y@ix$sL%wK$Z(Z3`g z*v%q;O#wur|8|9-q?FwP5o6kwiW#RpQRo|5EX6VfhrLhCwAmvjn8M5>-wu|93gbE_m3TAG*VCbjqpH?RoW2-YeJ6HAOzSA$A zpUifl+*Y#IqI7JOi}myc=r+p4y{TGXE{(JPDM>lTo|S$wH#cdD(15x7=s!O$5BxDC z>J#i87LN0|7W6q3;)xN}k~=T=`Dkn`s&N$;;^pWQE(e%QLCxee;669P?HZx5*2vCXx5Ym#<{WGL3r}t+_M^69G5Jb-4&ycR1=J`K^ zdUBeYe+KpCG(R`zb3q1jn%aK`fs~p${|IZ!Y5t=wAg$&eYF8fpoY^dBt%egXde0m0{{{1XP=crPEE+*wT6XB2eQwbV7wiE5YvpoIs7 z1VgWd`^cSz8fu-_(mk)Oeg3?G7DP)^?Yy?~`SZ%>{<+l8z4}L~@TluPavG-B!mkB} z_yIrzkcP?q*=@V)KEap0K9?bZgQ@fAHUr==x&{{Q{R=|Ujd5IJADf5d4S0R8^_kqiDaPETL+Gm`%j2YmVe zh|`1sC*?2cv>^cUe~Hu42RihZINi_Z_AhaII>6cdOPv0{=hZxa{@>DRYH9-r{cB!L zE!}_Xho;W?f2$Yh#$Pcwuc`a*{m|6Y|M&6?K98t>trr*oz_|HKoaW~d`7d#L`r3Mb z?T4Pej_yC@4aZ#z2=u}I{StHxi1zt>E;SrNLc-;={v0Bf!M-7KpR4*(s4LG*Nggr3*Z(YtJ>3(@;g`^O(47ykbwL4O`cVd2+s;eQ`RK+kn` LM9-Xo*_r)6tuc;K literal 0 HcmV?d00001 diff --git a/database-novo/README-GENERATE-DASHBOARD.md b/database-novo/README-GENERATE-DASHBOARD.md new file mode 100644 index 0000000..7621ef3 --- /dev/null +++ b/database-novo/README-GENERATE-DASHBOARD.md @@ -0,0 +1,96 @@ +# README — generate-dashboard.js + +Script Node.js que lê o `schema.sql` do backup mais recente e gera um `dashboard.html` interativo com a visão completa do banco de dados do projeto. + +--- + +## Como usar + +Coloque o `generate-dashboard.js` na **raiz do projeto** (mesma pasta do `db.cjs`) e rode: + +```bash +# Usa o backup mais recente automaticamente +node generate-dashboard.js + +# Ou especifica uma data +node generate-dashboard.js 2026-03-27 +``` + +O arquivo `dashboard.html` será gerado na raiz do projeto. Basta abrir no browser. + +--- + +## Fluxo recomendado + +Sempre que fizer alterações no banco, rode os dois comandos em sequência: + +```bash +node db.cjs backup # gera o backup em database-novo/backups/YYYY-MM-DD/ +node generate-dashboard.js # lê o backup mais recente e gera o dashboard.html +``` + +--- + +## O que o dashboard mostra + +- **Visão geral** — cards com os 9 domínios do projeto, quantidade de tabelas e FKs por domínio +- **Tabelas** — todas as 86 tabelas com colunas, tipos, badges PK/FK +- **Foreign Keys** — cada FK aparece como link clicável que pula direto para a tabela destino +- **Views** — lista das 24 views do schema público +- **Busca** — busca em tempo real por nome de tabela ou nome de coluna +- **Sidebar** — navegação por domínio + +--- + +## Estrutura de pastas esperada + +O script espera essa estrutura para funcionar: + +``` +raiz-do-projeto/ +├── db.cjs +├── db.config.json +├── generate-dashboard.js ← script +├── dashboard.html ← gerado aqui +└── database-novo/ + └── backups/ + └── 2026-03-27/ + ├── schema.sql ← lido pelo script + ├── data.sql + └── full_dump.sql +``` + +--- + +## Tabelas novas não aparecem no domínio certo? + +Quando você criar uma migration nova com uma tabela nova, ela aparecerá no dashboard na seção **"Outros"** e o script vai avisar no terminal: + +``` +⚠ Tabelas novas sem domínio definido (aparecerão em "Outros"): + - minha_tabela_nova +→ Edite DOMAIN_TABLES no script para mapeá-las. +``` + +Para corrigir, abra o `generate-dashboard.js` e adicione a tabela no domínio correto dentro do objeto `DOMAIN_TABLES` no topo do arquivo: + +```js +const DOMAIN_TABLES = { + 'Agenda': [ + 'agenda_eventos', + 'agenda_configuracoes', + // ... + 'minha_tabela_nova', // ← adiciona aqui + ], + // ... +}; +``` + +Depois rode `node generate-dashboard.js` novamente. + +--- + +## Requisitos + +- Node.js instalado (qualquer versão >= 14) +- Sem dependências externas — usa apenas módulos nativos (`fs`, `path`) diff --git a/database-novo/agenciapsi-db-dashboard.html b/database-novo/agenciapsi-db-dashboard.html new file mode 100644 index 0000000..fda3c58 --- /dev/null +++ b/database-novo/agenciapsi-db-dashboard.html @@ -0,0 +1,489 @@ + + + + + +AgenciaPsi — Database Dashboard + + + + +
+
AgênciaPsi DB
+ +
+
86 tabelas
+
82 FKs
+
24 views
+
+
+ +
+ +
+
+ + + + diff --git a/database-novo/backups/2026-03-27/data.sql b/database-novo/backups/2026-03-27/data.sql new file mode 100644 index 0000000..01d791f --- /dev/null +++ b/database-novo/backups/2026-03-27/data.sql @@ -0,0 +1,1651 @@ +-- +-- PostgreSQL database dump +-- + +\restrict rLNwKN3lgxiGbLOVrmaPCsUPxow34x8FOOeMLqy7UR7fEtGHvkAU0wfwrrWEI90 + +-- Dumped from database version 17.6 +-- Dumped by pg_dump version 17.6 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET transaction_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Data for Name: audit_log_entries; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.audit_log_entries (instance_id, id, payload, created_at, ip_address) FROM stdin; +00000000-0000-0000-0000-000000000000 f3a9bb5c-a48b-48da-83b0-5e5f71cc1278 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-23 10:58:23.661651+00 +00000000-0000-0000-0000-000000000000 015ae6c3-f000-49f5-a05a-7e43256a5ead {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-23 10:59:13.173068+00 +00000000-0000-0000-0000-000000000000 2222f6e6-4a78-4c44-88cf-65b677a0bdb6 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-23 10:59:17.817071+00 +00000000-0000-0000-0000-000000000000 12c8d5f5-46f0-4e67-96d2-7722167c7b3c {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-23 11:30:05.361177+00 +00000000-0000-0000-0000-000000000000 4aa98446-9aa8-4442-985a-aae3fbd28cd2 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-23 11:30:08.832248+00 +00000000-0000-0000-0000-000000000000 4e5e241b-0ba7-4772-a492-5dce48836688 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 12:28:24.672784+00 +00000000-0000-0000-0000-000000000000 12c376a9-7e40-4701-b163-173b66bbcc05 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 12:28:24.674266+00 +00000000-0000-0000-0000-000000000000 54e723ec-3977-4d92-8595-ce7ab53dbd86 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 13:30:01.59908+00 +00000000-0000-0000-0000-000000000000 de3c62a2-5771-4cb3-8bc5-a0f2c5cdf7bd {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 13:30:01.600518+00 +00000000-0000-0000-0000-000000000000 970016c6-0f0b-41a0-b4eb-5da3a5064f8b {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 14:30:14.581786+00 +00000000-0000-0000-0000-000000000000 a951c68b-1c3f-46ca-82d1-aead121e268f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 14:30:14.582962+00 +00000000-0000-0000-0000-000000000000 34299538-d49f-41a9-b0cc-2abbedbae26e {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 15:28:29.566693+00 +00000000-0000-0000-0000-000000000000 276722cb-b0ee-46a3-93b9-f3d966f16c26 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 15:28:29.56777+00 +00000000-0000-0000-0000-000000000000 bdc467e8-37fa-488e-b21d-e12bb16d5225 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 16:26:59.228619+00 +00000000-0000-0000-0000-000000000000 f00324fe-263c-42fd-b885-1309db0c76d0 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 16:26:59.230039+00 +00000000-0000-0000-0000-000000000000 4e468b7b-c2c1-479f-a6f5-9d7aea99aaa7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 17:25:28.767876+00 +00000000-0000-0000-0000-000000000000 b1e6f592-6413-44bd-82ce-eb4ae4d3873b {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 17:25:28.770199+00 +00000000-0000-0000-0000-000000000000 8ab04c44-66b6-469e-b869-a69b35c1d35f {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 18:25:01.605164+00 +00000000-0000-0000-0000-000000000000 00c18ea4-a6bb-429b-9d0d-7dbddd7b3d94 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 18:25:01.615437+00 +00000000-0000-0000-0000-000000000000 53a407ef-2f1c-4bed-838a-997812acb065 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 19:25:02.095787+00 +00000000-0000-0000-0000-000000000000 2c310c64-0933-439d-81f9-74fb6ffbf68d {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 19:25:02.097455+00 +00000000-0000-0000-0000-000000000000 2720bc5f-9871-4c13-a725-b0c640a04698 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 20:23:51.534132+00 +00000000-0000-0000-0000-000000000000 19fc4616-6242-4e56-b340-48467d9be144 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 20:23:51.535311+00 +00000000-0000-0000-0000-000000000000 1eed06b7-6fa2-4d66-ac2c-3d3bcb5b0592 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 21:22:21.898206+00 +00000000-0000-0000-0000-000000000000 920acd65-8146-4b35-b6d5-5281f4ea1ed1 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 21:22:21.900821+00 +00000000-0000-0000-0000-000000000000 73a31066-566b-42a1-a281-0ec8fc71a2f5 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 22:25:02.312362+00 +00000000-0000-0000-0000-000000000000 44c5a6ee-6bd3-43f1-9a3f-7c13ef1a83b5 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 22:25:02.314918+00 +00000000-0000-0000-0000-000000000000 a28430ae-7a7d-4ce8-9ae3-c83f7b3f8f5d {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 23:24:14.39221+00 +00000000-0000-0000-0000-000000000000 b4f2883f-f222-4862-8cdf-135563aaa257 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 23:24:14.393364+00 +00000000-0000-0000-0000-000000000000 2d425bb6-1ab6-4d54-9502-1132ff7fd14f {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 10:17:05.380495+00 +00000000-0000-0000-0000-000000000000 48dac6fd-0dce-42e1-ba70-3b235a0e1161 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 10:17:05.385002+00 +00000000-0000-0000-0000-000000000000 bb85f6c0-d96a-4eb6-a8b7-5170b4ea74a7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 11:17:55.391428+00 +00000000-0000-0000-0000-000000000000 b063d335-22f8-470b-b0ed-df6f9d68a7ba {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 11:17:55.392611+00 +00000000-0000-0000-0000-000000000000 29529cd0-0680-4d1d-a5ff-e43736f70555 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 12:17:55.375751+00 +00000000-0000-0000-0000-000000000000 f1dc7c02-dd21-4882-83e1-5e83d60156f2 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 12:17:55.378137+00 +00000000-0000-0000-0000-000000000000 e0ce9d96-5ed7-4342-bcb0-13e420b91523 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 19:20:20.869716+00 +00000000-0000-0000-0000-000000000000 0a93156f-a22c-4d18-81f2-54367a3745ba {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 19:20:20.936303+00 +00000000-0000-0000-0000-000000000000 1ddab552-8e6f-4ea6-8cc1-f7d00b92abc7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 20:20:04.2956+00 +00000000-0000-0000-0000-000000000000 bf82dc60-43b8-4d3c-8cb1-01bc4fd4a341 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 20:20:04.297562+00 +00000000-0000-0000-0000-000000000000 c5a9e22f-feb2-4383-b4cd-7910f2266978 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 21:18:48.738276+00 +00000000-0000-0000-0000-000000000000 fb58f5f0-7279-41de-98aa-20fa277e3d84 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 21:18:48.74055+00 +00000000-0000-0000-0000-000000000000 0dda5ef1-f572-4379-b734-f65b9e044830 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 22:17:29.961966+00 +00000000-0000-0000-0000-000000000000 8762f6f6-d22e-4c60-b2a3-c5c8bf99d548 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 22:17:29.965545+00 +00000000-0000-0000-0000-000000000000 51cea4a0-f2cc-4e9b-8b1a-8b2c9cdf8b47 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-24 23:01:52.959606+00 +00000000-0000-0000-0000-000000000000 61e26d26-ce04-490d-802d-4f67cc379fda {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-24 23:01:56.400534+00 +00000000-0000-0000-0000-000000000000 8c73c9b9-2da1-42df-b1a6-f92b98ab9300 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-24 23:07:05.837147+00 +00000000-0000-0000-0000-000000000000 05bf6f2d-60c0-4e5a-8ec1-6f2a7403d0c2 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-24 23:07:09.755293+00 +00000000-0000-0000-0000-000000000000 8d63e967-c616-42b0-b1c5-c8f54f54beaa {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:01:49.505958+00 +00000000-0000-0000-0000-000000000000 cdde610b-ac0f-4d61-91bd-7af34f23acf0 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:01:57.651986+00 +00000000-0000-0000-0000-000000000000 ef89c4e5-e092-4c12-a3b5-608b0e79bd65 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:03:22.169295+00 +00000000-0000-0000-0000-000000000000 ba251517-d704-4aa9-9992-16a8590582e0 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:03:28.975189+00 +00000000-0000-0000-0000-000000000000 b9d81262-4370-4712-8db7-81d51e27fcc8 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:06:51.450521+00 +00000000-0000-0000-0000-000000000000 26ca7ff5-87d3-48a2-ad9f-4ac67a216da8 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:06:55.726041+00 +00000000-0000-0000-0000-000000000000 f8c943c2-6dc0-42f6-87c9-adffc9a336c5 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:07:45.021178+00 +00000000-0000-0000-0000-000000000000 bba67a2b-049e-4d1b-b6a7-286d2b9c45f7 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:07:48.235967+00 +00000000-0000-0000-0000-000000000000 151834d9-2232-43c8-a0e6-a39c6617246e {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:11:54.788614+00 +00000000-0000-0000-0000-000000000000 e903f726-e5fe-4e06-9051-f36dacc780ce {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:12:00.861214+00 +00000000-0000-0000-0000-000000000000 e300a60b-b944-4c72-bb52-9ff7dff08dd2 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:24:51.770222+00 +00000000-0000-0000-0000-000000000000 4580e71b-e19b-4a74-ad85-f08f74791e53 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:24:57.09306+00 +00000000-0000-0000-0000-000000000000 a12cb7e1-768c-4ca2-96de-d68ce160971a {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 01:25:50.776943+00 +00000000-0000-0000-0000-000000000000 ce559936-37f2-411f-ba99-401e6ac4d94f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 01:25:50.778414+00 +00000000-0000-0000-0000-000000000000 46ebfc8e-e65a-4118-8a42-1d873b1436cc {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 02:25:50.797262+00 +00000000-0000-0000-0000-000000000000 d09f6b35-52d2-4f5e-80f5-0b188fef98da {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 02:25:50.798466+00 +00000000-0000-0000-0000-000000000000 7e7a2284-bb63-4b8d-ab19-186394b12f8f {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:13:42.627459+00 +00000000-0000-0000-0000-000000000000 cd92d32d-ac1a-4681-b957-11d8d4bd1a09 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:13:46.932353+00 +00000000-0000-0000-0000-000000000000 54013ee2-2dc3-4e36-8a35-09eb748ccba4 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:20:03.154923+00 +00000000-0000-0000-0000-000000000000 598cd35b-8c6e-4d41-9ec6-c25dcc4c6715 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:20:07.255873+00 +00000000-0000-0000-0000-000000000000 520167bb-ec1c-4e67-8c07-8d2ba457ec3c {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:24:18.554167+00 +00000000-0000-0000-0000-000000000000 4dc11041-8b36-4703-b5c5-2411a13b96da {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:24:24.699031+00 +00000000-0000-0000-0000-000000000000 9cfd2eae-f911-4dc7-9c5d-78499fd608f1 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:25:07.736803+00 +00000000-0000-0000-0000-000000000000 258e6ed2-5959-4893-8433-caa4b9ccb106 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:29:21.141199+00 +00000000-0000-0000-0000-000000000000 c76a59d2-0829-456a-957b-c623b14ae03e {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 10:28:55.843362+00 +00000000-0000-0000-0000-000000000000 f96b1f88-1f48-4a96-bbfc-b8c6527a31df {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 10:29:14.90848+00 +00000000-0000-0000-0000-000000000000 65f06c2b-5ee5-4bf1-a6ff-21dbe657e0b4 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 10:29:19.403154+00 +00000000-0000-0000-0000-000000000000 9e2290b7-dcfa-4bf5-b244-1f91011bfbe3 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 11:27:32.781897+00 +00000000-0000-0000-0000-000000000000 9cc32bd5-ae94-4014-90cb-b468e133cc10 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 11:27:32.783409+00 +00000000-0000-0000-0000-000000000000 2e244065-7456-4aba-a841-0920875e7f1b {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 12:16:54.862937+00 +00000000-0000-0000-0000-000000000000 f7cf0072-bda9-4b59-9532-7354a79c0306 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 12:17:01.538685+00 +00000000-0000-0000-0000-000000000000 5d8407ed-be29-482b-8877-42cf6e78f6c3 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:05:17.486922+00 +00000000-0000-0000-0000-000000000000 a8a0c3ef-6a10-4c8f-b53c-830ee3c2cfaf {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:05:21.871823+00 +00000000-0000-0000-0000-000000000000 2dfcfe2d-3770-42a6-8ceb-f16bd2e52151 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:05:31.190404+00 +00000000-0000-0000-0000-000000000000 c9e57ce5-33e2-4e79-b0b6-204dc22e4c8f {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:05:54.861933+00 +00000000-0000-0000-0000-000000000000 f62ee264-f036-46b9-9bdc-8bababf26492 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:06:00.886992+00 +00000000-0000-0000-0000-000000000000 ace96eee-7f68-4139-9f73-c3aa21d5e1d7 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:29:24.160199+00 +00000000-0000-0000-0000-000000000000 75b60071-d53f-490c-be4c-69f3c6f08da4 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:29:33.800526+00 +00000000-0000-0000-0000-000000000000 4a736318-9980-48ed-b13b-b47e38be5d41 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:30:03.032141+00 +00000000-0000-0000-0000-000000000000 a61bb073-c754-406a-b123-0b40c22915a3 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:37:21.932796+00 +00000000-0000-0000-0000-000000000000 80fd895f-8963-40f4-a9bb-4c59d95e964e {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 14:40:14.190475+00 +00000000-0000-0000-0000-000000000000 d2392bf0-18e8-45af-98f0-1d70e7cbe6f0 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 14:40:14.193129+00 +00000000-0000-0000-0000-000000000000 3fcfde42-9951-4df4-aae3-4abf256ed25d {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 15:38:52.104451+00 +00000000-0000-0000-0000-000000000000 e4c92ebd-11df-4e4c-82a1-7af12b797164 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 15:38:52.121069+00 +00000000-0000-0000-0000-000000000000 e6d4fb6d-fca8-4ee4-992a-59661b86b3d7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 16:39:45.201853+00 +00000000-0000-0000-0000-000000000000 f2606751-7081-4463-8c3f-985860702b7a {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 16:39:45.203238+00 +00000000-0000-0000-0000-000000000000 0db3c68a-49b6-42e3-8df0-a3dd1daa805d {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 17:39:45.220264+00 +00000000-0000-0000-0000-000000000000 20d02e8a-bdae-4c3d-9629-e0c1bc5e9d0a {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 17:39:45.221411+00 +00000000-0000-0000-0000-000000000000 58afa2b2-3d21-4d56-9b99-f96ec194170a {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 18:32:34.529973+00 +00000000-0000-0000-0000-000000000000 4afd9a79-0d8e-462e-8439-8c86ae984aa8 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 18:32:38.624903+00 +00000000-0000-0000-0000-000000000000 ee9fb7dc-392e-4fb2-9ee1-5d6fd7f632a8 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 18:35:17.643808+00 +00000000-0000-0000-0000-000000000000 aa72f08d-bdd6-400e-8798-3ad17598c5a8 {"action":"login","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 18:35:21.349866+00 +00000000-0000-0000-0000-000000000000 7ffd90be-aa82-4fa0-9a6a-6015fccedf99 {"action":"logout","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 18:35:43.306104+00 +00000000-0000-0000-0000-000000000000 ce068f66-0a8a-47a0-9fb1-2c3f727409f3 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 18:35:46.92007+00 +00000000-0000-0000-0000-000000000000 76e76323-7144-4ab0-9350-b1b009a5688a {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 19:33:46.819266+00 +00000000-0000-0000-0000-000000000000 f4ad35f0-f7f4-45c7-9e6f-b76d7075bcbd {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 19:33:46.822119+00 +00000000-0000-0000-0000-000000000000 d8d7a18a-1cd7-4694-a709-1c25bafd47cd {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 20:32:08.57133+00 +00000000-0000-0000-0000-000000000000 c0efd1eb-d3a4-4588-a6e4-6c6316afcbfa {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 20:32:08.573939+00 +00000000-0000-0000-0000-000000000000 663aa064-f89b-4629-9853-6921cec5f658 {"action":"login","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 10:13:28.062141+00 +00000000-0000-0000-0000-000000000000 bada5aa6-4962-42ab-90ac-f10ec7ada174 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 11:12:40.283978+00 +00000000-0000-0000-0000-000000000000 0284d0a9-1045-4d63-84c0-199f8fd57463 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 11:12:40.285424+00 +00000000-0000-0000-0000-000000000000 7fe49307-b9fc-4045-86e6-77ec5b6724d4 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 12:12:40.261029+00 +00000000-0000-0000-0000-000000000000 1cf673eb-6fed-4b60-a756-e5d9d8164ab2 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 12:12:40.262344+00 +00000000-0000-0000-0000-000000000000 89931cd1-3362-4153-9e26-c3a3450315bf {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 15:51:12.813868+00 +00000000-0000-0000-0000-000000000000 d4ae8391-85ed-4d02-be33-fc9895d10993 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 15:51:12.815012+00 +00000000-0000-0000-0000-000000000000 26bfda48-9d72-443a-8324-1944ebe4a701 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 16:49:43.315+00 +00000000-0000-0000-0000-000000000000 0f7adbbc-aaa1-4f3a-b04c-a6845b9e0ca6 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 16:49:43.323835+00 +00000000-0000-0000-0000-000000000000 d9baf33f-c034-49ad-8f16-902bea98ec5e {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 17:48:58.248187+00 +00000000-0000-0000-0000-000000000000 ca2f00e5-300f-4a17-b87e-ccbec14f2c7e {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 17:48:58.250458+00 +00000000-0000-0000-0000-000000000000 fa02ec44-7c86-418c-99c7-de2622acfb31 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 18:49:41.829309+00 +00000000-0000-0000-0000-000000000000 136da06f-a9bb-4523-bb16-47717edd8eb5 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 18:49:41.831653+00 +00000000-0000-0000-0000-000000000000 e8a3a012-3e8d-499a-aa7a-4cbca7999463 {"action":"logout","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:46:23.049908+00 +00000000-0000-0000-0000-000000000000 c31208e4-7589-44be-9f3d-5f3f201a4df6 {"action":"login","actor_id":"aaaaaaaa-0008-0008-0008-000000000008","actor_username":"editor@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:46:29.068361+00 +00000000-0000-0000-0000-000000000000 81031375-449a-4a69-b4dc-2ee999a654b2 {"action":"logout","actor_id":"aaaaaaaa-0008-0008-0008-000000000008","actor_username":"editor@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:46:38.212531+00 +00000000-0000-0000-0000-000000000000 5bcaaf60-9f35-4d7b-a54b-b491ac07ed15 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:46:43.300041+00 +00000000-0000-0000-0000-000000000000 6e71806d-9b38-4aec-af46-15f9aa5b5375 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:46:49.885518+00 +00000000-0000-0000-0000-000000000000 ab45715c-877c-4cea-9249-5e02e163d77a {"action":"user_signedup","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"team","traits":{"provider":"email"}} 2026-03-26 19:47:30.494975+00 +00000000-0000-0000-0000-000000000000 98634ad1-21f0-448b-81a4-6686bbfc9932 {"action":"login","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:47:30.515066+00 +00000000-0000-0000-0000-000000000000 e0613923-7e54-45b6-9861-14fd4a03f8e7 {"action":"logout","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:48:35.667525+00 +00000000-0000-0000-0000-000000000000 11139b73-efc6-43e4-b228-2ea2c4da475b {"action":"login","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:48:44.25225+00 +00000000-0000-0000-0000-000000000000 cf97f01d-8b2d-45a2-a571-d5ae4adce236 {"action":"logout","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:48:53.19869+00 +00000000-0000-0000-0000-000000000000 51c6d2fc-9a32-4449-91b0-707a74077e92 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:51:35.028402+00 +00000000-0000-0000-0000-000000000000 6f152cff-1e9a-4f0d-b2aa-d556ed20ad19 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:53:14.391225+00 +00000000-0000-0000-0000-000000000000 4dbde0d4-78e6-408f-84dc-ab6c246168b0 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:53:17.60162+00 +00000000-0000-0000-0000-000000000000 719ef7ce-f477-4de3-85d4-cb39b346bbf1 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:56:14.350054+00 +00000000-0000-0000-0000-000000000000 cd55882a-9e54-4730-b063-c9b4887c0e0a {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:56:18.624252+00 +00000000-0000-0000-0000-000000000000 232c3b43-f160-40a1-be37-2da29fc997bd {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 20:57:32.788753+00 +00000000-0000-0000-0000-000000000000 8d6852ac-b3b5-4543-bfed-e2369a73f211 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 20:57:32.794287+00 +00000000-0000-0000-0000-000000000000 f860fa47-b12d-4463-ab9a-46cbf022837e {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:19:25.905866+00 +00000000-0000-0000-0000-000000000000 449ddd1b-11e8-4b1e-bdc0-abc398dca77d {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:19:52.487097+00 +00000000-0000-0000-0000-000000000000 61bf81aa-1454-4723-af1a-fb00b2203c72 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:19:54.580581+00 +00000000-0000-0000-0000-000000000000 74a985d1-14b7-40c7-92db-444d0ba62ffb {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:31:18.590523+00 +00000000-0000-0000-0000-000000000000 17d9c260-731d-4fa7-9d69-9c1241c30bd9 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:31:20.209708+00 +00000000-0000-0000-0000-000000000000 6ec569b6-f0a1-4a0c-8c81-54fc82f6be93 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:31:39.762447+00 +00000000-0000-0000-0000-000000000000 75c4095e-994d-4ce9-9d77-59c514d88653 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:32:10.888074+00 +00000000-0000-0000-0000-000000000000 8ec7110e-415d-4627-935f-f0327b1dd58e {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:32:12.867178+00 +00000000-0000-0000-0000-000000000000 8350236f-d596-4d1e-8925-ae917e599ad4 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:33:05.320834+00 +00000000-0000-0000-0000-000000000000 25a7c28b-8ee2-4b94-bf6f-d046387fcde2 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:33:25.418988+00 +00000000-0000-0000-0000-000000000000 ce951d2d-488f-4757-92d0-3460f5bde396 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:40:53.407118+00 +00000000-0000-0000-0000-000000000000 39056ca1-fbd1-4e85-88ab-355c01a969ab {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:40:55.222474+00 +00000000-0000-0000-0000-000000000000 9591ecbc-aad0-4d5a-9076-413ed1e585ff {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:42:37.889869+00 +00000000-0000-0000-0000-000000000000 5777adf8-b362-496b-899f-5b05c0d93721 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:42:53.50928+00 +00000000-0000-0000-0000-000000000000 6a1e5a17-c7ea-453c-8626-d5022145ddf7 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:46:43.464106+00 +00000000-0000-0000-0000-000000000000 d28f02fc-a6ff-4aac-b49a-4fda2583bf17 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:46:46.362488+00 +00000000-0000-0000-0000-000000000000 b0e12d07-d349-4e14-8a3a-6ad42e747b21 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:52:49.203802+00 +00000000-0000-0000-0000-000000000000 c95a6365-3083-4f4f-a910-48f1405b272a {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:52:50.894594+00 +00000000-0000-0000-0000-000000000000 a8da7e38-f276-4aac-9a34-704af25a8716 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:53:20.228765+00 +00000000-0000-0000-0000-000000000000 1630012a-3347-479e-ab2b-76996c192c36 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:53:40.261757+00 +00000000-0000-0000-0000-000000000000 a8db2936-54c5-4614-94c5-4a13e6c83654 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:59:17.880911+00 +00000000-0000-0000-0000-000000000000 3d4fb0b1-d8d1-4beb-8c3a-53a22607cddd {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:59:25.603132+00 +00000000-0000-0000-0000-000000000000 10ef60d6-f55d-4370-b320-78c7b6561104 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:59:34.821814+00 +00000000-0000-0000-0000-000000000000 efc9e3f6-c034-4a04-977e-b81d445763ed {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:59:55.339526+00 +00000000-0000-0000-0000-000000000000 3b3a02dc-067d-42cc-917c-7811c4fd6347 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:02:44.995107+00 +00000000-0000-0000-0000-000000000000 c7cb137e-33a1-4fb2-9960-4bb015db752c {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:02:46.891055+00 +00000000-0000-0000-0000-000000000000 9bd389c2-f3ed-4197-a22b-bab553eb4e4f {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:12:46.756181+00 +00000000-0000-0000-0000-000000000000 bab2f893-dc20-436c-8363-d33a4aee8d06 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:13:14.788124+00 +00000000-0000-0000-0000-000000000000 651c44ae-e597-4840-ab53-1e6732543227 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:21:43.325253+00 +00000000-0000-0000-0000-000000000000 b6c79412-3e78-40e3-b079-555f9240682f {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:21:56.512281+00 +00000000-0000-0000-0000-000000000000 8a4f0ef0-c0ef-4278-852c-ca0ca0393fb9 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:22:05.26663+00 +00000000-0000-0000-0000-000000000000 1811dd26-c005-4aa5-b35d-f1d3a6668be7 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:22:21.864228+00 +00000000-0000-0000-0000-000000000000 5b46125b-ec57-4907-90bd-0f797cf56bfc {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:48:00.736884+00 +00000000-0000-0000-0000-000000000000 45fd6936-faf4-4df8-aac0-263d13f756c8 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:48:53.242588+00 +00000000-0000-0000-0000-000000000000 2a2f7b53-e0c5-4cfc-9b38-cea9d67ad2ad {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 23:32:17.947743+00 +00000000-0000-0000-0000-000000000000 8e3139b2-1c40-47ad-a247-3ccaa94e08ad {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 23:32:44.158626+00 +00000000-0000-0000-0000-000000000000 c0f1f8c1-5da2-4171-acf4-f15f3fef837b {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 09:06:02.093059+00 +00000000-0000-0000-0000-000000000000 0f53bba3-4d36-4bd9-bc78-4e5ce659129c {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 09:06:02.096051+00 +00000000-0000-0000-0000-000000000000 e2ea64a5-54a5-4b0a-95b4-cc26be5dc85e {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:27:06.674863+00 +00000000-0000-0000-0000-000000000000 f008faaf-004d-4d13-ba4d-cec5a113cfe8 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:39:56.661836+00 +00000000-0000-0000-0000-000000000000 5d36ee39-6793-42c6-a157-31037e7ddcbe {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"asd","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:52:45.795272+00 +00000000-0000-0000-0000-000000000000 98a8a1e1-8a81-4837-ba3c-c149d8d0be77 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leoardno","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:57:12.890495+00 +00000000-0000-0000-0000-000000000000 7a5d36c5-48b9-4a8c-b5af-af0fee6b61e2 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leoardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:57:34.211215+00 +00000000-0000-0000-0000-000000000000 4f5a2ed6-3898-4274-a852-519d71b71b21 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:58:48.279047+00 +00000000-0000-0000-0000-000000000000 5404eb4e-93f4-41a5-a86d-4250e2301115 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 10:05:13.289504+00 +00000000-0000-0000-0000-000000000000 e1e7b9e4-86c0-4d43-b108-ce91cfa7305f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 10:05:13.291502+00 +00000000-0000-0000-0000-000000000000 51d32ba8-701c-4bcc-ab72-74c02b06f37e {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 10:05:38.42979+00 +00000000-0000-0000-0000-000000000000 04b8022c-7a17-466e-baab-8355226f82f6 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 10:13:27.443108+00 +00000000-0000-0000-0000-000000000000 c59f34c3-6d7c-43b6-8335-5605d7914abb {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 10:52:34.48919+00 +00000000-0000-0000-0000-000000000000 94735b50-0e6e-4c02-8e0a-7ed9fe148337 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 10:52:36.530338+00 +00000000-0000-0000-0000-000000000000 e2b764c8-79d0-4e51-95f0-b980ccf2d799 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:03:42.792326+00 +00000000-0000-0000-0000-000000000000 3e674f0a-99ab-4c1e-81cf-467b04d50628 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:04:08.619676+00 +00000000-0000-0000-0000-000000000000 b2e669ff-00d5-4de8-a7a1-212c058b997e {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:04:18.215073+00 +00000000-0000-0000-0000-000000000000 efd87e91-95e7-4d43-906c-8700b9763e5d {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:04:41.642564+00 +00000000-0000-0000-0000-000000000000 0e3b31f4-ac2c-427b-9d35-3e9888bea099 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:09:33.113539+00 +00000000-0000-0000-0000-000000000000 c66e88bb-5e43-4d64-9941-bf7b87df1f57 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:09:42.050633+00 +00000000-0000-0000-0000-000000000000 f0d83a39-f0b6-411b-99cd-522f82a7c614 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:27:39.615562+00 +00000000-0000-0000-0000-000000000000 df8b1dd9-47ac-48a7-a1b0-49d7fb81e65c {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:30:42.202869+00 +00000000-0000-0000-0000-000000000000 825d4eb3-8690-4d89-89ee-36235b27fd2b {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 12:24:04.81973+00 +00000000-0000-0000-0000-000000000000 5449ebbf-358c-4daf-941a-071a5aa38352 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 12:24:06.324772+00 +00000000-0000-0000-0000-000000000000 5241335c-6b1e-42cd-b5f0-cf43ae4eac42 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 12:36:58.667642+00 +00000000-0000-0000-0000-000000000000 f77cb484-a8c6-4f12-b203-e08788747c89 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 12:37:01.302944+00 +00000000-0000-0000-0000-000000000000 7f91383b-30cf-4e95-ba47-b901f59133e5 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 12:37:09.603409+00 +00000000-0000-0000-0000-000000000000 de46c96d-1bbd-42c1-82d6-d28eb0cefd38 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 12:37:21.704034+00 +00000000-0000-0000-0000-000000000000 107aa0d2-82b7-4bd7-8672-96a43308cce4 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 13:05:48.781671+00 +00000000-0000-0000-0000-000000000000 cafae40c-1851-459a-8015-bf579bf00dfb {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 13:06:26.132815+00 +00000000-0000-0000-0000-000000000000 a24e3b39-ec62-480e-a8ab-1abe7cf5b6d1 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 14:06:35.696602+00 +00000000-0000-0000-0000-000000000000 a3ec0876-8151-446b-8027-f98fd9caf51b {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 14:06:35.697965+00 +00000000-0000-0000-0000-000000000000 c4557809-e407-495e-80d8-2de06b0c3cf5 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 14:15:40.80509+00 +00000000-0000-0000-0000-000000000000 c12e09c1-2140-4214-b3a6-b3f865941b29 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 14:15:43.617516+00 +00000000-0000-0000-0000-000000000000 fc7e22db-e27e-422e-ac30-93d657d23e78 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 14:16:38.012591+00 +00000000-0000-0000-0000-000000000000 c4b30fe1-40d5-4c74-aa66-0736bb7a3fb8 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 14:17:04.239061+00 +00000000-0000-0000-0000-000000000000 1608005f-42b8-4860-b87b-0232d72312a6 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 15:19:35.676083+00 +00000000-0000-0000-0000-000000000000 8cf0e884-4e73-47b2-8ae6-6b782ef8ad10 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 15:19:35.678353+00 +00000000-0000-0000-0000-000000000000 f9dac421-76ac-45f4-86ef-8d3e6d5f98fb {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 15:59:56.791407+00 +00000000-0000-0000-0000-000000000000 33eed8d8-66e0-4b32-9911-9a51a021903b {"action":"login","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 16:00:02.074346+00 +00000000-0000-0000-0000-000000000000 78d20dc8-f2fc-49b7-b0d2-ab87d74f7c29 {"action":"logout","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 16:00:24.342213+00 +00000000-0000-0000-0000-000000000000 d0bf8715-a43d-43ac-88d9-e3258869de64 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 16:00:29.89578+00 +\. + + +-- +-- Data for Name: flow_state; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.flow_state (id, user_id, auth_code, code_challenge_method, code_challenge, provider_type, provider_access_token, provider_refresh_token, created_at, updated_at, authentication_method, auth_code_issued_at, invite_token, referrer, oauth_client_state_id, linking_target_id, email_optional) FROM stdin; +\. + + +-- +-- Data for Name: users; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.users (instance_id, id, aud, role, email, encrypted_password, email_confirmed_at, invited_at, confirmation_token, confirmation_sent_at, recovery_token, recovery_sent_at, email_change_token_new, email_change, email_change_sent_at, last_sign_in_at, raw_app_meta_data, raw_user_meta_data, is_super_admin, created_at, updated_at, phone, phone_confirmed_at, phone_change, phone_change_token, phone_change_sent_at, email_change_token_current, email_change_confirm_status, banned_until, reauthentication_token, reauthentication_sent_at, is_sso_user, deleted_at, is_anonymous) FROM stdin; +00000000-0000-0000-0000-000000000000 aaaaaaaa-0001-0001-0001-000000000001 authenticated authenticated paciente@agenciapsi.com.br $2a$06$ipEc7puaVhQnpusOGhAYgOcrVHq4RnqZeDooS8FaehzHhueScf9S. 2026-03-23 10:46:29.876072+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Ana Paciente"} \N 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0005-0005-0005-000000000005 authenticated authenticated clinica3@agenciapsi.com.br $2a$06$L3aFykCSdduzTHEKsEQ3q.GdHTb5EJBvbIit4k7ZgnbRd5BCGuTxu 2026-03-23 10:46:29.876072+00 \N \N \N \N 2026-03-27 16:00:02.080329+00 {"provider": "email", "providers": ["email"]} {"name": "Clínica Bem Estar"} \N 2026-03-23 10:46:29.876072+00 2026-03-27 16:00:02.084768+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0011-0011-0011-000000000011 authenticated authenticated secretary@agenciapsi.com.br $2a$06$O7HeygRYgJViriMFCImLZu7DD.3A9wZWb9y3c5G2PIURgJ65UnqT. 2026-03-23 14:18:06.087973+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Gabriela Secretária"} \N 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0002-0002-0002-000000000002 authenticated authenticated terapeuta@agenciapsi.com.br $2a$06$CztXijQkaPZa6pUwXmMHWuzSF19GiVtRBdMLp.k4iWf7ftGWNBIg6 2026-03-23 10:46:29.876072+00 \N \N \N \N 2026-03-27 16:00:29.897844+00 {"provider": "email", "providers": ["email"]} {"name": "Bruno Terapeuta", "full_name": "Leonardo Nohama", "avatar_url": "http://127.0.0.1:54321/storage/v1/object/public/avatars/aaaaaaaa-0002-0002-0002-000000000002/avatar.jpg"} \N 2026-03-23 10:46:29.876072+00 2026-03-27 16:00:29.919654+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 384a69d8-b7cd-40ac-9d3c-764c93532b66 authenticated authenticated terapeuta2@agenciapsi.com.br $2a$10$MBE/uZcT1lpKira6nsTY6OzrUabKwtOrm.QvJzdy.IU95tiX3M2ia 2026-03-26 19:47:30.496218+00 \N \N \N \N 2026-03-26 19:48:44.253788+00 {"provider": "email", "providers": ["email"]} {"sub": "384a69d8-b7cd-40ac-9d3c-764c93532b66", "email": "terapeuta2@agenciapsi.com.br", "email_verified": true, "phone_verified": false} \N 2026-03-26 19:47:30.478057+00 2026-03-26 19:48:44.258995+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0007-0007-0007-000000000007 authenticated authenticated supervisor@agenciapsi.com.br $2a$06$.kF47/tagPNwSpgGM4ryZOu01L0ykU2IXakM8trZ.Hon1TTUDeqYK 2026-03-23 14:18:05.215881+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Carlos Supervisor"} \N 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0009-0009-0009-000000000009 authenticated authenticated therapist2@agenciapsi.com.br $2a$06$16hf/nUbN0lElm9l8vQI4ek8vIM2T8ymiJTQ8CHXXw/jD1gMuDFJS 2026-03-23 14:18:06.087973+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Eva Terapeuta"} \N 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0010-0010-0010-000000000010 authenticated authenticated therapist3@agenciapsi.com.br $2a$06$sBJPPHRI/MsrCTEeCK5/vOhASc/SNLeO.B/QEE2MZNWEP8FamyCXW 2026-03-23 14:18:06.087973+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Felipe Terapeuta"} \N 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0003-0003-0003-000000000003 authenticated authenticated clinica1@agenciapsi.com.br $2a$06$cxZ2uXWIOS9MgzoyzSla8Oocid6wKtEBPA4k9QyC8DvwzmOsI0co2 2026-03-23 10:46:29.876072+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Clínica Espaço Psi"} \N 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0004-0004-0004-000000000004 authenticated authenticated clinica2@agenciapsi.com.br $2a$06$ZSW6FPPCmhO8EkfSM4/QLu/J32HRe/87zoNLvPtCbqTdNBbanaLPi 2026-03-23 10:46:29.876072+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Clínica Mente Sã"} \N 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0008-0008-0008-000000000008 authenticated authenticated editor@agenciapsi.com.br $2a$06$lcF3sQOKaQOMwo5OTPPpcODcMtjDoUpHw3rOBhJMYow15LoJFLvH6 2026-03-23 14:18:05.215881+00 \N \N \N \N 2026-03-26 19:46:29.070205+00 {"provider": "email", "providers": ["email"]} {"name": "Diana Editora"} \N 2026-03-23 14:18:05.215881+00 2026-03-26 19:46:29.07828+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0006-0006-0006-000000000006 authenticated authenticated saas@agenciapsi.com.br $2a$06$QsvWGUd7HQTv6kSQDbRsiOkNcLM4O2BnQflXPbx3MK9E4RPGz5FvS 2026-03-23 10:46:29.876072+00 \N \N \N \N 2026-03-26 19:46:43.303588+00 {"provider": "email", "providers": ["email"]} {"name": "Admin Plataforma"} \N 2026-03-23 10:46:29.876072+00 2026-03-26 19:46:43.308732+00 \N \N \N 0 \N \N f \N f +\. + + +-- +-- Data for Name: identities; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.identities (provider_id, user_id, identity_data, provider, last_sign_in_at, created_at, updated_at, id) FROM stdin; +paciente@agenciapsi.com.br aaaaaaaa-0001-0001-0001-000000000001 {"sub": "aaaaaaaa-0001-0001-0001-000000000001", "email": "paciente@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 977a296d-b7ac-4151-8017-3099808e1f7f +terapeuta@agenciapsi.com.br aaaaaaaa-0002-0002-0002-000000000002 {"sub": "aaaaaaaa-0002-0002-0002-000000000002", "email": "terapeuta@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 44ccd811-6c51-48e9-81a8-77247022c693 +clinica1@agenciapsi.com.br aaaaaaaa-0003-0003-0003-000000000003 {"sub": "aaaaaaaa-0003-0003-0003-000000000003", "email": "clinica1@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 433175a2-4065-47ab-96dd-32508c9a7389 +clinica2@agenciapsi.com.br aaaaaaaa-0004-0004-0004-000000000004 {"sub": "aaaaaaaa-0004-0004-0004-000000000004", "email": "clinica2@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 390305dc-1f61-44e8-9c95-bd9de9591e5a +clinica3@agenciapsi.com.br aaaaaaaa-0005-0005-0005-000000000005 {"sub": "aaaaaaaa-0005-0005-0005-000000000005", "email": "clinica3@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 f669ef52-181e-44d5-8649-833d6b133313 +saas@agenciapsi.com.br aaaaaaaa-0006-0006-0006-000000000006 {"sub": "aaaaaaaa-0006-0006-0006-000000000006", "email": "saas@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 e6af6a5e-55ce-4872-a745-40d688d08982 +supervisor@agenciapsi.com.br aaaaaaaa-0007-0007-0007-000000000007 {"sub": "aaaaaaaa-0007-0007-0007-000000000007", "email": "supervisor@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 8dff38d2-1b09-4b88-9038-28b747358645 +editor@agenciapsi.com.br aaaaaaaa-0008-0008-0008-000000000008 {"sub": "aaaaaaaa-0008-0008-0008-000000000008", "email": "editor@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 a7856eb7-e96f-4752-84a3-b676503b74ae +therapist2@agenciapsi.com.br aaaaaaaa-0009-0009-0009-000000000009 {"sub": "aaaaaaaa-0009-0009-0009-000000000009", "email": "therapist2@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 8129cc38-db24-4f84-b48e-bf0b46235811 +therapist3@agenciapsi.com.br aaaaaaaa-0010-0010-0010-000000000010 {"sub": "aaaaaaaa-0010-0010-0010-000000000010", "email": "therapist3@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 754b015d-3c88-4104-b246-eede6fffabba +secretary@agenciapsi.com.br aaaaaaaa-0011-0011-0011-000000000011 {"sub": "aaaaaaaa-0011-0011-0011-000000000011", "email": "secretary@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 a7aa272b-659f-4bc7-8667-10cf49200410 +384a69d8-b7cd-40ac-9d3c-764c93532b66 384a69d8-b7cd-40ac-9d3c-764c93532b66 {"sub": "384a69d8-b7cd-40ac-9d3c-764c93532b66", "email": "terapeuta2@agenciapsi.com.br", "email_verified": false, "phone_verified": false} email 2026-03-26 19:47:30.489663+00 2026-03-26 19:47:30.489705+00 2026-03-26 19:47:30.489705+00 7e35a3f9-29d8-4f8d-bdfa-5bfe1328293f +\. + + +-- +-- Data for Name: instances; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.instances (id, uuid, raw_base_config, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: oauth_clients; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_clients (id, client_secret_hash, registration_type, redirect_uris, grant_types, client_name, client_uri, logo_uri, created_at, updated_at, deleted_at, client_type, token_endpoint_auth_method) FROM stdin; +\. + + +-- +-- Data for Name: sessions; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.sessions (id, user_id, created_at, updated_at, factor_id, aal, not_after, refreshed_at, user_agent, ip, tag, oauth_client_id, refresh_token_hmac_key, refresh_token_counter, scopes) FROM stdin; +2dc73205-6913-432b-8eb7-e8b609e5f0f4 aaaaaaaa-0002-0002-0002-000000000002 2026-03-27 16:00:29.897964+00 2026-03-27 16:00:29.897964+00 \N aal1 \N \N Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 172.19.0.1 \N \N \N \N \N +\. + + +-- +-- Data for Name: mfa_amr_claims; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.mfa_amr_claims (session_id, created_at, updated_at, authentication_method, id) FROM stdin; +2dc73205-6913-432b-8eb7-e8b609e5f0f4 2026-03-27 16:00:29.920845+00 2026-03-27 16:00:29.920845+00 password eb1238d8-fd33-4b72-b879-46e82e0abe4c +\. + + +-- +-- Data for Name: mfa_factors; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.mfa_factors (id, user_id, friendly_name, factor_type, status, created_at, updated_at, secret, phone, last_challenged_at, web_authn_credential, web_authn_aaguid, last_webauthn_challenge_data) FROM stdin; +\. + + +-- +-- Data for Name: mfa_challenges; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.mfa_challenges (id, factor_id, created_at, verified_at, ip_address, otp_code, web_authn_session_data) FROM stdin; +\. + + +-- +-- Data for Name: oauth_authorizations; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_authorizations (id, authorization_id, client_id, user_id, redirect_uri, scope, state, resource, code_challenge, code_challenge_method, response_type, status, authorization_code, created_at, expires_at, approved_at, nonce) FROM stdin; +\. + + +-- +-- Data for Name: oauth_client_states; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_client_states (id, provider_type, code_verifier, created_at) FROM stdin; +\. + + +-- +-- Data for Name: oauth_consents; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_consents (id, user_id, client_id, scopes, granted_at, revoked_at) FROM stdin; +\. + + +-- +-- Data for Name: one_time_tokens; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.one_time_tokens (id, user_id, token_type, token_hash, relates_to, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: refresh_tokens; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.refresh_tokens (instance_id, id, token, user_id, revoked, created_at, updated_at, parent, session_id) FROM stdin; +00000000-0000-0000-0000-000000000000 137 ow4odpbr72tv aaaaaaaa-0002-0002-0002-000000000002 f 2026-03-27 16:00:29.902122+00 2026-03-27 16:00:29.902122+00 \N 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +\. + + +-- +-- Data for Name: sso_providers; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.sso_providers (id, resource_id, created_at, updated_at, disabled) FROM stdin; +\. + + +-- +-- Data for Name: saml_providers; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.saml_providers (id, sso_provider_id, entity_id, metadata_xml, metadata_url, attribute_mapping, created_at, updated_at, name_id_format) FROM stdin; +\. + + +-- +-- Data for Name: saml_relay_states; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.saml_relay_states (id, sso_provider_id, request_id, for_email, redirect_to, created_at, updated_at, flow_state_id) FROM stdin; +\. + + +-- +-- Data for Name: schema_migrations; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.schema_migrations (version) FROM stdin; +20171026211738 +20171026211808 +20171026211834 +20180103212743 +20180108183307 +20180119214651 +20180125194653 +00 +20210710035447 +20210722035447 +20210730183235 +20210909172000 +20210927181326 +20211122151130 +20211124214934 +20211202183645 +20220114185221 +20220114185340 +20220224000811 +20220323170000 +20220429102000 +20220531120530 +20220614074223 +20220811173540 +20221003041349 +20221003041400 +20221011041400 +20221020193600 +20221021073300 +20221021082433 +20221027105023 +20221114143122 +20221114143410 +20221125140132 +20221208132122 +20221215195500 +20221215195800 +20221215195900 +20230116124310 +20230116124412 +20230131181311 +20230322519590 +20230402418590 +20230411005111 +20230508135423 +20230523124323 +20230818113222 +20230914180801 +20231027141322 +20231114161723 +20231117164230 +20240115144230 +20240214120130 +20240306115329 +20240314092811 +20240427152123 +20240612123726 +20240729123726 +20240802193726 +20240806073726 +20241009103726 +20250717082212 +20250731150234 +20250804100000 +20250901200500 +20250903112500 +20250904133000 +20250925093508 +20251007112900 +20251104100000 +20251111201300 +20251201000000 +20260115000000 +20260121000000 +\. + + +-- +-- Data for Name: sso_domains; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.sso_domains (id, sso_provider_id, domain, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: job; Type: TABLE DATA; Schema: cron; Owner: - +-- + +COPY cron.job (jobid, schedule, command, nodename, nodeport, database, username, active, jobname) FROM stdin; +\. + + +-- +-- Data for Name: job_run_details; Type: TABLE DATA; Schema: cron; Owner: - +-- + +COPY cron.job_run_details (jobid, runid, job_pid, database, username, command, status, return_message, start_time, end_time) FROM stdin; +\. + + +-- +-- Data for Name: _db_migrations; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public._db_migrations (id, filename, hash, category, applied_at) FROM stdin; +1 seed_001_fixed.sql 87fc24517f6446f7 seed 2026-03-23 14:15:02.14603+00 +2 seed_002.sql b05d565b35c97300 seed 2026-03-23 14:15:02.45035+00 +3 seed_003.sql 257ef8bba4e319a2 seed 2026-03-23 14:15:02.755322+00 +4 seed_010_plans.sql 0de612f2301e27d3 seed 2026-03-23 14:15:02.974622+00 +5 seed_011_features.sql e7326ac0e33e4fee seed 2026-03-23 14:15:03.261589+00 +6 seed_012_plan_features.sql f0e1b4ab684383f7 seed 2026-03-23 14:15:03.553899+00 +7 seed_013_subscriptions.sql b61e4af59262f3ac seed 2026-03-23 14:15:03.816997+00 +8 seed_014_global_data.sql a7bc086bc6f052ee seed 2026-03-23 14:15:04.080095+00 +9 fix_addon_credits_fk.sql aaff13facb98e4d8 fix 2026-03-23 14:15:04.372331+00 +10 fix_addon_rls_saas_admin.sql 84d85284eb441afc fix 2026-03-23 14:15:04.630692+00 +11 fix_missing_subscriptions.sql f5740f6eef9e5405 fix 2026-03-23 14:15:04.916745+00 +12 fix_notification_templates_rls_admin.sql ede371cbce54e13e fix 2026-03-23 14:15:05.15764+00 +13 fix_seed_patient_groups.sql e9b870ba0ad5f359 fix 2026-03-23 14:15:05.485322+00 +14 fix_subscriptions_validate_scope.sql c814a90d768d339c fix 2026-03-23 14:15:06.461275+00 +15 fix_template_keys_match_populate.sql e0fdd2a420abaeb8 fix 2026-03-23 14:15:06.977464+00 +\. + + +-- +-- Data for Name: tenants; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenants (id, name, created_at, kind) FROM stdin; +bbbbbbbb-0009-0009-0009-000000000009 Eva Terapeuta 2026-03-23 10:47:12.826179+00 therapist +bbbbbbbb-0010-0010-0010-000000000010 Felipe Terapeuta 2026-03-23 10:47:12.826179+00 therapist +bbbbbbbb-0003-0003-0003-000000000003 Clínica Espaço Psi 2026-03-23 10:46:29.876072+00 clinic_coworking +bbbbbbbb-0004-0004-0004-000000000004 Clínica Mente Sã 2026-03-23 10:46:29.876072+00 clinic_reception +bbbbbbbb-0005-0005-0005-000000000005 Clínica Bem Estar 2026-03-23 10:46:29.876072+00 clinic_full +a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 Diana Editora 2026-03-26 19:46:29.161046+00 therapist +1e98ca49-a46c-4701-847b-145a14d53d19 terapeuta2 2026-03-26 19:47:30.68134+00 therapist +bbbbbbbb-0002-0002-0002-000000000002 Bruno Terapeuta 2026-03-23 10:46:29.876072+00 therapist +\. + + +-- +-- Data for Name: addon_credits; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.addon_credits (id, tenant_id, owner_id, addon_type, balance, total_purchased, total_consumed, low_balance_threshold, low_balance_notified, daily_limit, hourly_limit, daily_used, hourly_used, daily_reset_at, hourly_reset_at, from_number_override, expires_at, is_active, created_at, updated_at) FROM stdin; +0f2ed178-d2d1-4bf0-a58c-206be0183c1c bbbbbbbb-0002-0002-0002-000000000002 \N sms 320 320 0 10 f \N \N 0 0 \N \N \N \N t 2026-03-25 00:15:28.741153+00 2026-03-25 00:15:37.596479+00 +\. + + +-- +-- Data for Name: addon_products; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.addon_products (id, slug, name, description, addon_type, icon, credits_amount, price_cents, currency, is_active, is_visible, sort_order, metadata, created_at, updated_at, deleted_at) FROM stdin; +a6cfa47f-e26d-4835-9c14-4629e8afa331 sms_basico SMS Básico 100 créditos SMS. Ideal para quem está começando ou tem poucos pacientes. sms pi pi-comment 100 2500 BRL t t 10 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +56d95e40-9e32-491d-969d-8970f51c05ed sms_essencial SMS Essencial 220 créditos SMS. Para consultórios em crescimento com envio regular de lembretes. sms pi pi-comments 220 5000 BRL t t 20 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +7589b9fc-2ccf-43f0-aea4-abc3edf05473 sms_profissional SMS Profissional 350 créditos SMS. Para quem envia lembretes, confirmações e avisos de cobrança. sms pi pi-send 350 7500 BRL t t 30 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +2dff4229-32a8-4090-8f19-6c3ab836b9c1 sms_premium SMS Premium 500 créditos SMS. Melhor custo-benefício. Clínicas e agenda cheia. sms pi pi-star 500 10000 BRL t t 40 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +\. + + +-- +-- Data for Name: addon_transactions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.addon_transactions (id, tenant_id, owner_id, addon_type, type, amount, balance_before, balance_after, product_id, queue_id, description, admin_user_id, payment_method, payment_reference, price_cents, currency, created_at, metadata) FROM stdin; +46181c84-9f01-4fec-baaf-c10aa935f240 bbbbbbbb-0002-0002-0002-000000000002 \N sms purchase 100 0 100 a6cfa47f-e26d-4835-9c14-4629e8afa331 \N SMS Básico aaaaaaaa-0006-0006-0006-000000000006 manual \N 2500 BRL 2026-03-25 00:15:28.741153+00 {} +a07a56b5-1b6d-46a1-88fd-15623c77f07f bbbbbbbb-0002-0002-0002-000000000002 \N sms purchase 220 100 320 56d95e40-9e32-491d-969d-8970f51c05ed \N SMS Essencial aaaaaaaa-0006-0006-0006-000000000006 manual \N 5000 BRL 2026-03-25 00:15:37.596479+00 {} +\. + + +-- +-- Data for Name: agenda_bloqueios; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_bloqueios (id, owner_id, tenant_id, tipo, titulo, data_inicio, data_fim, hora_inicio, hora_fim, recorrente, dia_semana, observacao, origem, created_at) FROM stdin; +f809fcb6-0369-42fd-985a-5d9976798e88 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 bloqueio Feriado: Sexta-feira Santa 2026-04-03 2026-04-03 \N \N f \N \N agenda_feriado 2026-03-23 11:32:18.225845+00 +\. + + +-- +-- Data for Name: agenda_configuracoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_configuracoes (owner_id, duracao_padrao_minutos, intervalo_padrao_minutos, timezone, usar_horario_admin_custom, admin_inicio_visualizacao, admin_fim_visualizacao, admin_slot_visual_minutos, online_ativo, online_min_antecedencia_horas, online_max_dias_futuro, online_cancelar_ate_horas, online_reagendar_ate_horas, online_limite_agendamentos_futuros, online_modo, online_buffer_antes_min, online_buffer_depois_min, online_modalidade, created_at, updated_at, usar_granularidade_custom, granularidade_min, setup_concluido, setup_concluido_em, agenda_view_mode, agenda_custom_start, agenda_custom_end, session_duration_min, session_break_min, pausas_semanais, setup_clinica_concluido, setup_clinica_concluido_em, tenant_id, jornada_igual_todos, slot_mode, atendimento_mode) FROM stdin; +aaaaaaaa-0005-0005-0005-000000000005 50 0 America/Sao_Paulo f \N \N 30 f 24 60 12 12 1 automatico 0 0 ambos 2026-03-25 18:35:26.138564+00 2026-03-26 17:19:10.818241+00 f \N t 2026-03-26 17:19:10.804+00 full_24h \N \N 40 10 [{"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 0}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 1}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 2}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 3}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 4}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 5}] f \N bbbbbbbb-0005-0005-0005-000000000005 t fixed ambos +aaaaaaaa-0002-0002-0002-000000000002 50 0 America/Sao_Paulo f \N \N 30 f 24 60 12 12 1 automatico 0 0 ambos 2026-03-23 10:58:31.422764+00 2026-03-27 14:18:31.232768+00 f \N t 2026-03-27 14:18:31.221+00 full_24h \N \N 50 10 [{"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 0}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 1}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 2}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 3}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 4}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 5}] f \N bbbbbbbb-0002-0002-0002-000000000002 t fixed ambos +\. + + +-- +-- Data for Name: tenant_members; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_members (id, tenant_id, user_id, role, status, created_at) FROM stdin; +72be63ef-bc2c-4c8e-8522-0c6d64746bbc bbbbbbbb-0002-0002-0002-000000000002 aaaaaaaa-0002-0002-0002-000000000002 tenant_admin active 2026-03-23 10:46:29.876072+00 +07974953-677e-4941-b90f-1ae15ffbbbf6 bbbbbbbb-0003-0003-0003-000000000003 aaaaaaaa-0003-0003-0003-000000000003 tenant_admin active 2026-03-23 10:46:29.876072+00 +11417585-46af-4faa-a34a-dc23a36c5704 bbbbbbbb-0004-0004-0004-000000000004 aaaaaaaa-0004-0004-0004-000000000004 tenant_admin active 2026-03-23 10:46:29.876072+00 +cd22a662-2b16-4cfe-a9e6-15ccb8e7d67e bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0005-0005-0005-000000000005 tenant_admin active 2026-03-23 10:46:29.876072+00 +e40559cd-43af-4547-b447-2cc7bbff5ad7 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0002-0002-0002-000000000002 therapist active 2026-03-23 10:46:29.876072+00 +0ee00481-77f2-4bc0-ab09-3bc441c03b1b bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0007-0007-0007-000000000007 supervisor active 2026-03-23 14:18:05.215881+00 +344a6ae3-379a-4776-9441-8ac9f8733c99 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0008-0008-0008-000000000008 therapist active 2026-03-23 14:18:05.215881+00 +aac8e805-da4f-4089-b2e1-0b32b679d0dd bbbbbbbb-0009-0009-0009-000000000009 aaaaaaaa-0009-0009-0009-000000000009 tenant_admin active 2026-03-23 14:18:06.087973+00 +2633ed47-76e8-4a30-8e3c-07bec4f7c9cd bbbbbbbb-0010-0010-0010-000000000010 aaaaaaaa-0010-0010-0010-000000000010 tenant_admin active 2026-03-23 14:18:06.087973+00 +a72fa56d-04e4-4e23-853c-fd4926b263c3 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0009-0009-0009-000000000009 therapist active 2026-03-23 14:18:06.087973+00 +677f130c-06bd-44d7-b283-844351d65c45 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0010-0010-0010-000000000010 therapist active 2026-03-23 14:18:06.087973+00 +978ce15a-8dc5-4540-8430-f9e1e19d4952 bbbbbbbb-0004-0004-0004-000000000004 aaaaaaaa-0011-0011-0011-000000000011 clinic_admin active 2026-03-23 14:18:06.087973+00 +da8fd413-1710-40f2-85a0-5af9cf2c78d8 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 aaaaaaaa-0008-0008-0008-000000000008 tenant_admin active 2026-03-26 19:46:29.161046+00 +fb7a3517-0479-42ac-8c37-ec6ffc03c5be 1e98ca49-a46c-4701-847b-145a14d53d19 384a69d8-b7cd-40ac-9d3c-764c93532b66 tenant_admin active 2026-03-26 19:47:30.68134+00 +\. + + +-- +-- Data for Name: patients; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patients (id, nome_completo, email_principal, telefone, created_at, owner_id, avatar_url, status, last_attended_at, is_native, naturalidade, data_nascimento, rg, cpf, identification_color, genero, estado_civil, email_alternativo, pais, cep, cidade, estado, endereco, numero, bairro, complemento, escolaridade, profissao, nome_parente, grau_parentesco, telefone_alternativo, onde_nos_conheceu, encaminhado_por, nome_responsavel, telefone_responsavel, cpf_responsavel, observacao_responsavel, cobranca_no_responsavel, observacoes, notas_internas, updated_at, telefone_parente, tenant_id, responsible_member_id, user_id, patient_scope, therapist_member_id) FROM stdin; +6449e64b-050b-419f-8845-029b6f10a17d Otto Rank otto.rank.437@example.com 86363331874 2026-03-23 11:33:02.010795+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-23 11:33:02.010795+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +76cec0bb-1e63-45bf-9b03-feaaa2a5d18d Peter Fonagy peter.fonagy.466@example.com 88283303064 2026-03-24 10:20:35.019712+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-24 10:20:35.019712+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 Carla Lima Souza carla.lima.souza.882@email.com 55327597657 2026-03-25 20:47:18.217202+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Bauru 1980-10-09 498989206 41727305582 \N Feminino Casado(a) alt.709@email.com Brasil 73591-841 Santos SP Av. Brasil 5688 Jardim Paulista Apto 637 Superior completo Estudante Henrique Costa Irmão 55216555751 Site Ana Ribeiro Yasmin Araújo Lima 55209246525 85070073257 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:47:18.217202+00 55979769772 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +d8a170f0-c67e-4e6d-8dc1-b51423d20889 Anna Freud anna.freud.323@example.com 96307749614 2026-03-25 20:47:35.342234+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-25 20:47:35.342234+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +4fdd639d-5f32-453f-9092-74b183c8bbfe Daniel Oliveira Silva daniel.oliveira.silva.779@email.com 55629968595 2026-03-25 20:47:43.705237+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Ribeirão Preto 2018-06-14 644776179 07710834329 \N Feminino Solteiro(a) alt.72@email.com Brasil 43519-831 Ribeirão Preto RS Rua XV de Novembro 5937 Vila Prado Apto 78 Superior incompleto Autônomo Felipe Lima Irmã 55619689156 Outro Vanessa Martins Vanessa Carvalho Barbosa 55209874519 24378696207 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:47:43.705237+00 55738955502 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +8e33a4dc-4bfb-4cce-aec0-fe5a5c91300f John Bowlby john.bowlby.398@example.com 84123228043 2026-03-25 20:48:39.752714+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-25 20:48:39.752714+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +2aaa34f7-c770-4d78-a2a2-c77bd4771c6a Yasmin Martins Lima yasmin.martins.lima.810@email.com 55979361027 2026-03-25 20:58:11.897509+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Santos 1950-10-11 25599572 65091040676 \N Prefere não informar Divorciado(a) alt.364@email.com Brasil 55632-609 Santos RJ Av. Brasil 1268 Jardim Paulista Apto 117 Superior incompleto Autônomo Bruno Martins Pai 55819227599 Google Marcos Martins Ana Oliveira Martins 55129232266 29325344165 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:58:11.897509+00 55529945515 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +c193905f-e70c-4935-aec3-b9c161c6044c Otávio Souza Ferreira888 otavio.souza.ferreira.212@email.com 55519100542 2026-03-25 20:58:37.007581+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Campinas 2018-03-10 249907937 85207459899 \N Feminino Solteiro(a) alt.523@email.com Brasil 80355-723 Araraquara RS Rua XV de Novembro 2199 Vila Prado Apto 249 Ensino Médio Professora Felipe Carvalho Pai 55759833223 Threads Yasmin Carvalho Sabrina Martins Santos 55749688637 89130651778 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:58:37.007581+00 55225172394 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +\. + + +-- +-- Data for Name: billing_contracts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.billing_contracts (id, owner_id, tenant_id, patient_id, type, total_sessions, sessions_used, package_price, amount, billing_interval, active_from, active_to, status, created_at) FROM stdin; +\. + + +-- +-- Data for Name: determined_commitments; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.determined_commitments (id, tenant_id, created_by, is_native, native_key, is_locked, active, name, description, created_at, updated_at, bg_color, text_color) FROM stdin; +5944e5d9-622d-4db1-b73d-0083a6a4a9a1 bbbbbbbb-0002-0002-0002-000000000002 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +cd1076b1-0f3d-460e-842f-1585c0ae2a64 bbbbbbbb-0003-0003-0003-000000000003 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +b82819d4-02a1-40c5-bf57-6e2e0425e62d bbbbbbbb-0003-0003-0003-000000000003 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +2e5f4151-e259-4261-8954-e3d196a28383 bbbbbbbb-0004-0004-0004-000000000004 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +bec193c2-172b-40ef-a5fe-91b7c44c841e bbbbbbbb-0004-0004-0004-000000000004 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +b3bf589d-4a72-429f-b9bd-ba426c3f42f1 bbbbbbbb-0002-0002-0002-000000000002 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 16:44:21.537071+00 \N \N +793f5b1d-b218-40a3-aa73-3e1440d9ea66 bbbbbbbb-0005-0005-0005-000000000005 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +717e552c-99cc-4375-b7f2-a1b29f3581e3 bbbbbbbb-0002-0002-0002-000000000002 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 16:46:11.099434+00 \N \N +8c1a77a1-b66f-462e-940d-2f60d080149c bbbbbbbb-0005-0005-0005-000000000005 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +fc6da673-fd7c-4101-b980-54eb3842804c bbbbbbbb-0009-0009-0009-000000000009 \N t reading f t Leitura Praticar leitura 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +4c4a8ced-d109-443b-bc83-817c858f9bb6 bbbbbbbb-0009-0009-0009-000000000009 \N t class f f Aula Dar aula 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +fe52a6b2-e86e-47e0-9dc7-9b7e63599cff bbbbbbbb-0010-0010-0010-000000000010 \N t reading f t Leitura Praticar leitura 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +53193b8a-8af1-4824-a7c6-22cbb7459c45 bbbbbbbb-0010-0010-0010-000000000010 \N t class f f Aula Dar aula 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 bbbbbbbb-0002-0002-0002-000000000002 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 20:57:27.372517+00 \N \N +b567bfb6-7a58-4893-b072-eb23c72948a2 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t reading f t Leitura Praticar leitura 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +19072432-4eaf-411e-940f-c505639ec78a bbbbbbbb-0002-0002-0002-000000000002 \N f \N f t test teste 2026-03-24 19:42:19.588131+00 2026-03-24 19:42:19.588131+00 f97316 #ffffff +a4dcb5bf-86ab-4daa-80ca-357b88d7a6ba bbbbbbbb-0002-0002-0002-000000000002 \N f \N f t teste teste 2026-03-24 22:56:02.422706+00 2026-03-24 22:56:02.422706+00 6366f1 #ffffff +be219d8b-b2fb-4cfc-8910-64171cd7692f a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t session t t Sess??o Sess??o com paciente 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t supervision f t Supervis??o Supervis??o 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +6a3194d1-475a-4131-9d5f-d8a3bb4ad84e a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t class f f Aula Dar aula 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +fcd58365-0042-4df3-b93d-26cbae67d14a a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t analysis f t An??lise Pessoal Minha an??lise pessoal 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +65f1a136-ce11-46a0-b452-80f00132319d 1e98ca49-a46c-4701-847b-145a14d53d19 \N t session t t Sess??o Sess??o com paciente 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +b5bcbc9e-abf0-4ff3-8960-133c3592de88 1e98ca49-a46c-4701-847b-145a14d53d19 \N t reading f t Leitura Praticar leitura 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +8b0f4d71-f1d2-4c97-bd75-241aae919cfe 1e98ca49-a46c-4701-847b-145a14d53d19 \N t supervision f t Supervis??o Supervis??o 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 1e98ca49-a46c-4701-847b-145a14d53d19 \N t class f f Aula Dar aula 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +871f7d97-2d2c-485b-a631-6a4884e1c1e9 1e98ca49-a46c-4701-847b-145a14d53d19 \N t analysis f t An??lise Pessoal Minha an??lise pessoal 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +d2ea6a5e-e563-434b-950e-2d27bbf445ec bbbbbbbb-0003-0003-0003-000000000003 \N t session t t Sessão Sess??o com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +21f39315-5171-42ce-848c-129c1955206b bbbbbbbb-0004-0004-0004-000000000004 \N t session t t Sessão Sess??o com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +3276b032-6990-4bfc-942d-9155943c241e bbbbbbbb-0005-0005-0005-000000000005 \N t session t t Sessão Sess??o com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +fb194a3e-06ee-4704-a8cb-fe302dd2528e bbbbbbbb-0009-0009-0009-000000000009 \N t session t t Sessão Sess??o com paciente 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +1936c4af-5757-4c10-ad8f-8a5988287acc bbbbbbbb-0010-0010-0010-000000000010 \N t session t t Sessão Sess??o com paciente 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +42a8c681-8b32-4608-870f-b617acbe249e bbbbbbbb-0002-0002-0002-000000000002 \N t session t t Sessão Sessão com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 6366f1 #ffffff +9ded91f1-1974-4e56-afc6-12b2e8f9456c bbbbbbbb-0003-0003-0003-000000000003 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +0d6cb161-8d89-4b18-9323-5631e8cddd8f bbbbbbbb-0004-0004-0004-000000000004 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +83666a58-2e53-49a6-8b13-73eeb203e5ba bbbbbbbb-0005-0005-0005-000000000005 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +3dbb8c2f-6fab-478f-b094-5bc13606a504 bbbbbbbb-0009-0009-0009-000000000009 \N t supervision f t Supervisão Supervis??o 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +fb2724d6-3938-4b3f-b8ec-aeb4417a4057 bbbbbbbb-0010-0010-0010-000000000010 \N t supervision f t Supervisão Supervis??o 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +b5c43dc2-a3a1-40ca-8b75-02ec649fd914 bbbbbbbb-0003-0003-0003-000000000003 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 bbbbbbbb-0004-0004-0004-000000000004 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad bbbbbbbb-0005-0005-0005-000000000005 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +b180f6e4-39ad-464c-842e-d42dc60cdf13 bbbbbbbb-0009-0009-0009-000000000009 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +29b5f51c-0833-4902-8c03-ee8bb7836a33 bbbbbbbb-0010-0010-0010-000000000010 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +\. + + +-- +-- Data for Name: insurance_plans; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.insurance_plans (id, owner_id, tenant_id, name, notes, default_value, active, created_at, updated_at) FROM stdin; +ec7aa65f-cbd2-48a5-9ec0-368a788438a7 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste asd \N t 2026-03-24 12:21:20.501533+00 2026-03-24 12:21:20.501533+00 +\. + + +-- +-- Data for Name: insurance_plan_services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.insurance_plan_services (id, insurance_plan_id, name, value, active, created_at, updated_at) FROM stdin; +b24c1a29-a5b3-4676-94b0-8effa89c672a ec7aa65f-cbd2-48a5-9ec0-368a788438a7 procedimento 1111.00 t 2026-03-24 12:21:29.140157+00 2026-03-24 12:21:29.140157+00 +\. + + +-- +-- Data for Name: recurrence_rules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.recurrence_rules (id, tenant_id, owner_id, therapist_id, patient_id, determined_commitment_id, type, "interval", weekdays, start_time, end_time, timezone, duration_min, start_date, end_date, max_occurrences, open_ended, modalidade, titulo_custom, observacoes, extra_fields, status, created_at, updated_at, price, insurance_plan_id, insurance_guide_number, insurance_value, insurance_plan_service_id) FROM stdin; +\. + + +-- +-- Data for Name: agenda_eventos; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_eventos (id, owner_id, tipo, status, titulo, observacoes, inicio_em, fim_em, created_at, updated_at, terapeuta_id, tenant_id, visibility_scope, mirror_of_event_id, mirror_source, patient_id, determined_commitment_id, link_online, titulo_custom, extra_fields, recurrence_id, recurrence_date, modalidade, price, billing_contract_id, billed, services_customized, insurance_plan_id, insurance_guide_number, insurance_value, insurance_plan_service_id) FROM stdin; +dbf6b44e-24f1-4cd2-86f5-a594fa39f9d3 aaaaaaaa-0002-0002-0002-000000000002 sessao agendado Otto Rank [Sess??o] \N 2026-03-23 13:00:00+00 2026-03-23 13:50:00+00 2026-03-23 11:33:08.0471+00 2026-03-23 11:33:08.0471+00 \N bbbbbbbb-0002-0002-0002-000000000002 public \N \N 6449e64b-050b-419f-8845-029b6f10a17d 42a8c681-8b32-4608-870f-b617acbe249e \N \N \N \N \N presencial 0.00 \N f f \N \N \N \N +\. + + +-- +-- Data for Name: agenda_excecoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_excecoes (id, owner_id, data, hora_inicio, hora_fim, tipo, motivo, created_at, updated_at, status, fonte, aplicavel_online, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agenda_online_slots; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_online_slots (id, owner_id, weekday, "time", enabled, created_at, updated_at, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agenda_regras_semanais; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_regras_semanais (id, owner_id, dia_semana, hora_inicio, hora_fim, modalidade, ativo, created_at, updated_at, tenant_id) FROM stdin; +a3212bcd-fc80-40c3-b3ea-92d31d1d051a aaaaaaaa-0002-0002-0002-000000000002 0 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +cbeae6bd-1ebd-494d-b5c0-2f3a8bf09e14 aaaaaaaa-0002-0002-0002-000000000002 1 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +dd9ecded-d82c-4d87-a2df-85b89c331001 aaaaaaaa-0002-0002-0002-000000000002 2 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +ce5054c2-f382-4135-8365-06b3dab7ea1c aaaaaaaa-0002-0002-0002-000000000002 3 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +e8e22a8d-4deb-4eeb-a9e8-e1f906030499 aaaaaaaa-0002-0002-0002-000000000002 4 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +265ff3ab-645f-4dd6-8a91-4a3a4d085ff0 aaaaaaaa-0002-0002-0002-000000000002 5 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +cf21b2ed-fbf6-4899-95b6-bee502e7b139 aaaaaaaa-0005-0005-0005-000000000005 0 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +a6fc9552-59dd-4717-9282-129d3297b5e0 aaaaaaaa-0005-0005-0005-000000000005 1 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +c028ea38-75a0-4e5f-a6c5-705264f43826 aaaaaaaa-0005-0005-0005-000000000005 2 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +c674e740-90b9-4ae1-8b9b-56ee6342235e aaaaaaaa-0005-0005-0005-000000000005 3 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +a272ac19-32d2-4c3c-b1a1-fbdedfffc6d3 aaaaaaaa-0005-0005-0005-000000000005 4 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +1a98514b-8508-4539-9e96-6af89b91417d aaaaaaaa-0005-0005-0005-000000000005 5 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +\. + + +-- +-- Data for Name: agenda_slots_bloqueados_semanais; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_slots_bloqueados_semanais (id, owner_id, dia_semana, hora_inicio, motivo, ativo, created_at, updated_at, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agenda_slots_regras; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_slots_regras (id, owner_id, dia_semana, passo_minutos, offset_minutos, buffer_antes_min, buffer_depois_min, min_antecedencia_horas, ativo, created_at, updated_at, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agendador_configuracoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agendador_configuracoes (owner_id, tenant_id, ativo, link_slug, imagem_fundo_url, imagem_header_url, logomarca_url, cor_primaria, nome_exibicao, endereco, botao_como_chegar_ativo, maps_url, modo_aprovacao, modalidade, tipos_habilitados, duracao_sessao_min, antecedencia_minima_horas, prazo_resposta_horas, reserva_horas, pagamento_obrigatorio, pix_chave, pix_countdown_minutos, triagem_motivo, triagem_como_conheceu, verificacao_email, exigir_aceite_lgpd, mensagem_boas_vindas, texto_como_se_preparar, texto_termos_lgpd, created_at, updated_at, pagamento_modo, pagamento_metodos_visiveis) FROM stdin; +\. + + +-- +-- Data for Name: agendador_solicitacoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agendador_solicitacoes (id, owner_id, tenant_id, paciente_nome, paciente_sobrenome, paciente_email, paciente_celular, paciente_cpf, tipo, modalidade, data_solicitada, hora_solicitada, reservado_ate, motivo, como_conheceu, pix_status, pix_pago_em, status, recusado_motivo, autorizado_em, autorizado_por, user_id, patient_id, evento_id, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.services (id, owner_id, tenant_id, name, description, price, duration_min, active, created_at, updated_at) FROM stdin; +2f2d31a7-abef-4efc-89fc-794a458682cb aaaaaaaa-0005-0005-0005-000000000005 bbbbbbbb-0005-0005-0005-000000000005 Atendimento padrão \N 0.00 50 t 2026-03-26 16:50:09.010706+00 2026-03-26 16:50:09.010706+00 +0166ac88-669d-4f0c-8769-17eb0029c8a9 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 Atendimento padrão \N 40.00 50 t 2026-03-26 21:48:12.84259+00 2026-03-26 21:50:44.411125+00 +045843e1-815a-437b-b11a-ea9136e10cdf aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste \N 1.00 50 t 2026-03-27 11:40:12.054774+00 2026-03-27 11:40:12.054774+00 +36cc58d9-7beb-451b-b630-e81271d45de4 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste \N 2.00 50 t 2026-03-27 13:03:35.570064+00 2026-03-27 13:03:35.570064+00 +\. + + +-- +-- Data for Name: commitment_services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.commitment_services (id, commitment_id, service_id, quantity, unit_price, discount_pct, discount_flat, final_price, created_at) FROM stdin; +\. + + +-- +-- Data for Name: commitment_time_logs; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.commitment_time_logs (id, tenant_id, commitment_id, calendar_event_id, source, started_at, ended_at, minutes, created_by, created_at) FROM stdin; +\. + + +-- +-- Data for Name: company_profiles; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.company_profiles (id, tenant_id, nome_fantasia, razao_social, tipo_empresa, cnpj, ie, im, cep, logradouro, numero, complemento, bairro, cidade, estado, email, telefone, site, logo_url, redes_sociais, created_at, updated_at) FROM stdin; +9c2039d4-0058-46d0-b60f-29e16459bb85 bbbbbbbb-0002-0002-0002-000000000002 teste \N consultorio \N \N \N 13561-260 Avenida Tancredo de Almeida Neves 457 complemento Parque Santa Mônica São Carlos SP comercial@gmail.com (11) 11111-1111 site http://127.0.0.1:54321/storage/v1/object/public/logos/bbbbbbbb-0002-0002-0002-000000000002/logo.png [{"url": "@perfil", "name": "Instagram"}] 2026-03-27 12:35:48.759848+00 2026-03-27 14:18:20.352667+00 +\. + + +-- +-- Data for Name: determined_commitment_fields; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.determined_commitment_fields (id, tenant_id, commitment_id, key, label, field_type, required, sort_order, created_at, updated_at) FROM stdin; +3f45731e-f025-4a95-b37d-35becf557474 bbbbbbbb-0002-0002-0002-000000000002 5944e5d9-622d-4db1-b73d-0083a6a4a9a1 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +7cb99c51-3de2-4216-be29-1067d1d2a6e8 bbbbbbbb-0002-0002-0002-000000000002 5944e5d9-622d-4db1-b73d-0083a6a4a9a1 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +29a48e4d-b4b9-41e6-8a3f-23e862b11f71 bbbbbbbb-0002-0002-0002-000000000002 5944e5d9-622d-4db1-b73d-0083a6a4a9a1 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +de1bf25c-2c6e-4de1-9be2-9976eb26e9e6 bbbbbbbb-0002-0002-0002-000000000002 717e552c-99cc-4375-b7f2-a1b29f3581e3 supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +0a2f4c96-be82-49a1-b0bc-65d08bf29078 bbbbbbbb-0002-0002-0002-000000000002 717e552c-99cc-4375-b7f2-a1b29f3581e3 topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +ea018b37-ac9a-49a5-93dd-ebc4f2bcc069 bbbbbbbb-0002-0002-0002-000000000002 717e552c-99cc-4375-b7f2-a1b29f3581e3 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4f4fff30-8b01-46cc-a6c9-25e0c623b03c bbbbbbbb-0002-0002-0002-000000000002 b3bf589d-4a72-429f-b9bd-ba426c3f42f1 theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +3ddb7185-eea6-43e4-bab7-686aa996f4e6 bbbbbbbb-0002-0002-0002-000000000002 b3bf589d-4a72-429f-b9bd-ba426c3f42f1 group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +20c208a1-eae7-4021-916f-a3a45f7d9e84 bbbbbbbb-0002-0002-0002-000000000002 b3bf589d-4a72-429f-b9bd-ba426c3f42f1 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +82c2b726-8de7-4bdd-a7ab-0fe519200f2c bbbbbbbb-0002-0002-0002-000000000002 e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +88bb598e-44f1-4090-a09a-21a11e628ab0 bbbbbbbb-0002-0002-0002-000000000002 e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +f0aa7c4c-e26d-42c2-8f90-5e4a87308fe9 bbbbbbbb-0002-0002-0002-000000000002 e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +fa8083ef-7e1f-4e00-84b6-6dd197a31e11 bbbbbbbb-0003-0003-0003-000000000003 cd1076b1-0f3d-460e-842f-1585c0ae2a64 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +2fea846d-3d7f-4946-8b8a-921756d1375d bbbbbbbb-0003-0003-0003-000000000003 cd1076b1-0f3d-460e-842f-1585c0ae2a64 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +7f71d073-7337-40c3-9f2b-80d359ee6825 bbbbbbbb-0003-0003-0003-000000000003 cd1076b1-0f3d-460e-842f-1585c0ae2a64 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +9ea25942-9529-4ffb-99b1-17c8f48fa1b3 bbbbbbbb-0003-0003-0003-000000000003 9ded91f1-1974-4e56-afc6-12b2e8f9456c supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +642ee453-276b-4636-808b-0deed8827416 bbbbbbbb-0003-0003-0003-000000000003 9ded91f1-1974-4e56-afc6-12b2e8f9456c topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +1107588b-dafc-49f0-adf1-1d478410fbab bbbbbbbb-0003-0003-0003-000000000003 9ded91f1-1974-4e56-afc6-12b2e8f9456c notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +2d00519b-2d66-4b66-9471-4edfd7f03152 bbbbbbbb-0003-0003-0003-000000000003 b82819d4-02a1-40c5-bf57-6e2e0425e62d theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +6fb9b8f8-0a50-4267-93ff-a154487d0ee2 bbbbbbbb-0003-0003-0003-000000000003 b82819d4-02a1-40c5-bf57-6e2e0425e62d group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +99d0df7d-20f7-49df-acd8-c359b195348d bbbbbbbb-0003-0003-0003-000000000003 b82819d4-02a1-40c5-bf57-6e2e0425e62d notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +241c3b0f-78c1-4485-9f11-c8fc5fdf6650 bbbbbbbb-0003-0003-0003-000000000003 b5c43dc2-a3a1-40ca-8b75-02ec649fd914 analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +08440a39-027f-4879-8091-7a9b2cf522c1 bbbbbbbb-0003-0003-0003-000000000003 b5c43dc2-a3a1-40ca-8b75-02ec649fd914 focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +d11f4029-c46e-42bf-b7aa-bc3b1cc9874a bbbbbbbb-0003-0003-0003-000000000003 b5c43dc2-a3a1-40ca-8b75-02ec649fd914 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +d2d11b9f-5dcf-4355-bc33-c977ac7591a9 bbbbbbbb-0004-0004-0004-000000000004 2e5f4151-e259-4261-8954-e3d196a28383 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +2d3e3c6e-6ca0-4170-b9ed-e45e470a8738 bbbbbbbb-0004-0004-0004-000000000004 2e5f4151-e259-4261-8954-e3d196a28383 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +a7003c31-46b1-44a2-876e-034f3c61ce8c bbbbbbbb-0004-0004-0004-000000000004 2e5f4151-e259-4261-8954-e3d196a28383 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +29f2cb01-22c2-45c3-ba02-55b908b5ceac bbbbbbbb-0004-0004-0004-000000000004 0d6cb161-8d89-4b18-9323-5631e8cddd8f supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +e0b8f4dd-740c-4162-b988-62fc8216de8c bbbbbbbb-0004-0004-0004-000000000004 0d6cb161-8d89-4b18-9323-5631e8cddd8f topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +187902e7-4497-4ab6-81f7-7dbe177189e9 bbbbbbbb-0004-0004-0004-000000000004 0d6cb161-8d89-4b18-9323-5631e8cddd8f notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4ef4ac8e-adf4-4034-851f-3767ef37a8e4 bbbbbbbb-0004-0004-0004-000000000004 bec193c2-172b-40ef-a5fe-91b7c44c841e theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4972b76d-c080-4989-a4f8-3d3c57a9b96e bbbbbbbb-0004-0004-0004-000000000004 bec193c2-172b-40ef-a5fe-91b7c44c841e group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +fc589056-f8b8-4d96-94f0-7ac49ab53059 bbbbbbbb-0004-0004-0004-000000000004 bec193c2-172b-40ef-a5fe-91b7c44c841e notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4300058b-c0b4-4b86-8f6e-eda6d99553fc bbbbbbbb-0004-0004-0004-000000000004 d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +20add80c-2807-42c9-8c42-40291a49e5df bbbbbbbb-0004-0004-0004-000000000004 d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4d01680a-733e-4c0c-a26a-e19b962fe9fa bbbbbbbb-0004-0004-0004-000000000004 d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +e56ad576-c831-444a-bb21-0bcd74630d48 bbbbbbbb-0005-0005-0005-000000000005 793f5b1d-b218-40a3-aa73-3e1440d9ea66 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +cc36d270-7961-481f-8a8b-99e0b9ef2e64 bbbbbbbb-0005-0005-0005-000000000005 793f5b1d-b218-40a3-aa73-3e1440d9ea66 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +8eb622a4-8ee9-4c34-af09-9dd5c74923fa bbbbbbbb-0005-0005-0005-000000000005 793f5b1d-b218-40a3-aa73-3e1440d9ea66 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +24d0c58f-3382-4ea8-81e7-3dc32f122b90 bbbbbbbb-0005-0005-0005-000000000005 83666a58-2e53-49a6-8b13-73eeb203e5ba supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4993e870-d101-4754-b2bc-18205255d643 bbbbbbbb-0005-0005-0005-000000000005 83666a58-2e53-49a6-8b13-73eeb203e5ba topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +b6e403de-e93a-42e0-832a-e6103a935421 bbbbbbbb-0005-0005-0005-000000000005 83666a58-2e53-49a6-8b13-73eeb203e5ba notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +a552127c-1b16-464c-a8f3-d7bdf054b0a9 bbbbbbbb-0005-0005-0005-000000000005 8c1a77a1-b66f-462e-940d-2f60d080149c theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +be9ecf92-2dc8-4f62-94f3-223b0165c0af bbbbbbbb-0005-0005-0005-000000000005 8c1a77a1-b66f-462e-940d-2f60d080149c group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +7f7a9b35-60f1-4192-bf6c-d91ca42813a1 bbbbbbbb-0005-0005-0005-000000000005 8c1a77a1-b66f-462e-940d-2f60d080149c notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +528d426f-23d4-422f-8624-5b4178ce3a8b bbbbbbbb-0005-0005-0005-000000000005 a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +11a02a30-7a0a-4baf-acdd-2525c2f2cf84 bbbbbbbb-0005-0005-0005-000000000005 a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +f456679b-05d7-4ccc-ae3b-c5f673f8f6f8 bbbbbbbb-0005-0005-0005-000000000005 a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +07ba5c18-9ebb-4b38-bd2f-591b8e4f7f87 bbbbbbbb-0009-0009-0009-000000000009 fc6da673-fd7c-4101-b980-54eb3842804c book Livro text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +92eec7d8-20f3-46e4-8700-c80e9bd90bcb bbbbbbbb-0009-0009-0009-000000000009 fc6da673-fd7c-4101-b980-54eb3842804c author Autor text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +fdf6b850-b627-494e-9cb0-d1a044409b3c bbbbbbbb-0009-0009-0009-000000000009 fc6da673-fd7c-4101-b980-54eb3842804c notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +ef9a9605-9b0f-4671-b056-835c56844cf9 bbbbbbbb-0009-0009-0009-000000000009 3dbb8c2f-6fab-478f-b094-5bc13606a504 supervisor Supervisor text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e19f0ad4-fe52-4595-84aa-cf11828a7ecd bbbbbbbb-0009-0009-0009-000000000009 3dbb8c2f-6fab-478f-b094-5bc13606a504 topic Assunto text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +41a085ac-6849-4e29-8728-79e1a47c1ac5 bbbbbbbb-0009-0009-0009-000000000009 3dbb8c2f-6fab-478f-b094-5bc13606a504 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +8fdaad8b-f31c-4510-9b72-7346026103b9 bbbbbbbb-0009-0009-0009-000000000009 4c4a8ced-d109-443b-bc83-817c858f9bb6 theme Tema text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +9e53f6f5-6770-4f3e-8b0e-cb18c9df54b7 bbbbbbbb-0009-0009-0009-000000000009 4c4a8ced-d109-443b-bc83-817c858f9bb6 group Turma text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +222021b7-6917-4977-a08f-e19532aa2a0e bbbbbbbb-0009-0009-0009-000000000009 4c4a8ced-d109-443b-bc83-817c858f9bb6 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +329eebff-55c4-49e2-87ee-b3d0f418234b bbbbbbbb-0009-0009-0009-000000000009 b180f6e4-39ad-464c-842e-d42dc60cdf13 analyst Analista text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e87c45d8-8f5f-476d-811d-5b5b4a764e07 bbbbbbbb-0009-0009-0009-000000000009 b180f6e4-39ad-464c-842e-d42dc60cdf13 focus Foco text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +2a8c3eff-4eed-4b2d-98e7-50267ebb910e bbbbbbbb-0009-0009-0009-000000000009 b180f6e4-39ad-464c-842e-d42dc60cdf13 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e6109ef3-7409-4268-8ecf-eaff64fa9498 bbbbbbbb-0010-0010-0010-000000000010 fe52a6b2-e86e-47e0-9dc7-9b7e63599cff book Livro text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +2069cb4c-3c49-4a82-ada1-63082d14bba3 bbbbbbbb-0010-0010-0010-000000000010 fe52a6b2-e86e-47e0-9dc7-9b7e63599cff author Autor text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +d8428a90-f534-4794-99a7-1c752d42e0d2 bbbbbbbb-0010-0010-0010-000000000010 fe52a6b2-e86e-47e0-9dc7-9b7e63599cff notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +69435311-e703-4fbb-84b8-d634fd0148fe bbbbbbbb-0010-0010-0010-000000000010 fb2724d6-3938-4b3f-b8ec-aeb4417a4057 supervisor Supervisor text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +2c71883c-dee5-420e-9c7a-d1b1ade908f5 bbbbbbbb-0010-0010-0010-000000000010 fb2724d6-3938-4b3f-b8ec-aeb4417a4057 topic Assunto text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +1cf750d0-449d-4780-8828-bffce53cc651 bbbbbbbb-0010-0010-0010-000000000010 fb2724d6-3938-4b3f-b8ec-aeb4417a4057 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e38226ca-3af4-41c3-a901-9edfc0814e46 bbbbbbbb-0010-0010-0010-000000000010 53193b8a-8af1-4824-a7c6-22cbb7459c45 theme Tema text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +6100b0b2-db5c-4aeb-98e3-3c6f6a20f058 bbbbbbbb-0010-0010-0010-000000000010 53193b8a-8af1-4824-a7c6-22cbb7459c45 group Turma text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +ef948cc1-4e2b-4c7a-9043-956a6341085a bbbbbbbb-0010-0010-0010-000000000010 53193b8a-8af1-4824-a7c6-22cbb7459c45 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +9405625b-f781-4b5d-836c-19371fccefab bbbbbbbb-0010-0010-0010-000000000010 29b5f51c-0833-4902-8c03-ee8bb7836a33 analyst Analista text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +cc414a63-c433-4269-be6c-caad9f83408d bbbbbbbb-0010-0010-0010-000000000010 29b5f51c-0833-4902-8c03-ee8bb7836a33 focus Foco text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +c5c4f4c9-2e74-4f65-b5d9-ee1121005cbc bbbbbbbb-0010-0010-0010-000000000010 29b5f51c-0833-4902-8c03-ee8bb7836a33 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +155f3805-15e9-48fe-9f36-454e589ca2b2 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b567bfb6-7a58-4893-b072-eb23c72948a2 book Livro text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +95283550-683a-4c3d-a7d7-ac66f3b1f3e5 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b567bfb6-7a58-4893-b072-eb23c72948a2 author Autor text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +f293f578-4b69-46e9-b652-9eb535ae904b a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b567bfb6-7a58-4893-b072-eb23c72948a2 notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +9fa8bb78-eed2-4d10-9cab-e527c19137d8 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 supervisor Supervisor text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +972ac699-e2aa-4312-a0c9-1faa8853e38d a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 topic Assunto text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +f70a4cbb-b55a-4661-94de-d322f80f78a7 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +bc4036a8-32c2-4e13-bfde-0a85e21016f8 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 6a3194d1-475a-4131-9d5f-d8a3bb4ad84e theme Tema text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +4a5acde2-4d27-4377-8cf7-f94c25b2b13a a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 6a3194d1-475a-4131-9d5f-d8a3bb4ad84e group Turma text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +fe89c629-de7b-4dfe-b5b4-00954cf5ef4e a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 6a3194d1-475a-4131-9d5f-d8a3bb4ad84e notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +60192c88-c9ef-4650-8591-104e02f22460 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 fcd58365-0042-4df3-b93d-26cbae67d14a analyst Analista text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +dd49a6ca-755e-4eb2-b4e8-9c17393c2e77 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 fcd58365-0042-4df3-b93d-26cbae67d14a focus Foco text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +d2e9d95a-be44-4bde-a853-0e516345421b a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 fcd58365-0042-4df3-b93d-26cbae67d14a notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +c0206c42-b9c0-4fc4-afc1-22a6178750e2 1e98ca49-a46c-4701-847b-145a14d53d19 b5bcbc9e-abf0-4ff3-8960-133c3592de88 book Livro text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +23f30e08-728e-460d-9410-cb1bb24548ce 1e98ca49-a46c-4701-847b-145a14d53d19 b5bcbc9e-abf0-4ff3-8960-133c3592de88 author Autor text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +5406a62c-430a-4959-b0c5-cca804b03f53 1e98ca49-a46c-4701-847b-145a14d53d19 b5bcbc9e-abf0-4ff3-8960-133c3592de88 notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +1248cce1-2f5e-4e16-a193-503d374408f0 1e98ca49-a46c-4701-847b-145a14d53d19 8b0f4d71-f1d2-4c97-bd75-241aae919cfe supervisor Supervisor text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +cb9c6678-be1b-474a-b5a8-f4aaaff51213 1e98ca49-a46c-4701-847b-145a14d53d19 8b0f4d71-f1d2-4c97-bd75-241aae919cfe topic Assunto text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +b1ade16e-28f0-4b3e-b25e-7dcc6c338f7f 1e98ca49-a46c-4701-847b-145a14d53d19 8b0f4d71-f1d2-4c97-bd75-241aae919cfe notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +fd21821a-58db-40a6-8d2c-54e8bf8dfe45 1e98ca49-a46c-4701-847b-145a14d53d19 e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 theme Tema text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +757a5af1-e35e-4f70-a81c-89295d85979e 1e98ca49-a46c-4701-847b-145a14d53d19 e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 group Turma text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +198896b3-8181-428b-9022-938587ac9db7 1e98ca49-a46c-4701-847b-145a14d53d19 e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +ca55ceb1-a1d4-48ee-b82d-d767dac745c6 1e98ca49-a46c-4701-847b-145a14d53d19 871f7d97-2d2c-485b-a631-6a4884e1c1e9 analyst Analista text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +21832e62-718c-405a-b602-1e725330ccb3 1e98ca49-a46c-4701-847b-145a14d53d19 871f7d97-2d2c-485b-a631-6a4884e1c1e9 focus Foco text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +cad4c61b-482c-4d8c-adf1-9bf08d04f90a 1e98ca49-a46c-4701-847b-145a14d53d19 871f7d97-2d2c-485b-a631-6a4884e1c1e9 notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +\. + + +-- +-- Data for Name: dev_user_credentials; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.dev_user_credentials (id, user_id, email, password_dev, kind, note, created_at) FROM stdin; +\. + + +-- +-- Data for Name: email_layout_config; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.email_layout_config (id, tenant_id, header_config, footer_config, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: email_templates_global; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.email_templates_global (id, key, domain, channel, subject, body_html, body_text, version, is_active, variables, created_at, updated_at) FROM stdin; +be1e52ec-0c1d-4cbe-97da-9c47ce052631 session.reminder.email session email Lembrete: sua sessão amanhã às {{session_time}} \n

Olá, {{patient_name}}!

\n

Este é um lembrete da sua sessão agendada para {{session_date}} às {{session_time}}.

\n

Modalidade: {{session_modality}}

\n {{#if session_link}}\n

Clique aqui para entrar na sessão online

\n {{/if}}\n

Em caso de necessidade de cancelamento, entre em contato com antecedência.

\n

Até logo,
{{therapist_name}}

\n Olá, {{patient_name}}! Lembrete da sua sessão: {{session_date}} às {{session_time}} ({{session_modality}}). 2 t {"patient_name": "Nome completo do paciente", "session_date": "Data da sessão (ex: 20/03/2026)", "session_link": "Link da videochamada (apenas online)", "session_time": "Horário da sessão (ex: 14:00)", "therapist_name": "Nome do terapeuta", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +bf956245-c79a-4ed0-86f1-e0be57072cc8 session.confirmation.email session email Sessão confirmada — {{session_date}} às {{session_time}} \n

Olá, {{patient_name}}!

\n

Sua sessão foi confirmada com sucesso.

\n
    \n
  • Data: {{session_date}}
  • \n
  • Horário: {{session_time}}
  • \n
  • Modalidade: {{session_modality}}
  • \n {{#if session_address}}
  • Local: {{session_address}}
  • {{/if}}\n
\n

Até lá,
{{therapist_name}}

\n Sessão confirmada: {{session_date}} às {{session_time}} ({{session_modality}}). 1 t {"patient_name": "Nome do paciente", "session_date": "Data da sessão", "session_time": "Horário da sessão", "therapist_name": "Nome do terapeuta", "session_address": "Endereço (apenas presencial)", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +9039f35e-e31f-4bad-a6a5-2b865f1152b3 session.cancellation.email session email Sessão cancelada — {{session_date}} \n

Olá, {{patient_name}}!

\n

Informamos que sua sessão do dia {{session_date}} às {{session_time}} foi cancelada.

\n {{#if cancellation_reason}}

Motivo: {{cancellation_reason}}

{{/if}}\n

Entre em contato para reagendar.

\n

{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Data cancelada", "session_time": "Horário cancelado", "therapist_name": "Nome do terapeuta", "cancellation_reason": "Motivo do cancelamento (opcional)"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +ff36dccc-6fe1-4391-9eac-bd03927b38f5 session.rescheduled.email session email Sessão reagendada — {{session_date}} às {{session_time}} \n

Olá, {{patient_name}}!

\n

Sua sessão foi reagendada para {{session_date}} às {{session_time}}.

\n

Modalidade: {{session_modality}}

\n

Até lá,
{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Nova data", "session_time": "Novo horário", "therapist_name": "Nome do terapeuta", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +f17b10f8-d811-4f83-bf5e-3ed636262f28 intake.received.email intake email Recebemos seu cadastro — {{clinic_name}} \n

Olá, {{patient_name}}!

\n

Recebemos seu cadastro com sucesso. Nossa equipe entrará em contato em breve para dar continuidade ao processo.

\n

Obrigado pela confiança,
{{clinic_name}}

\n \N 1 t {"clinic_name": "Nome da clínica ou terapeuta", "patient_name": "Nome do solicitante"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +5b4a9a8b-bc92-4030-92ec-acbb6f9e7ee0 intake.approved.email intake email Cadastro aprovado — bem-vindo(a)! \n

Olá, {{patient_name}}!

\n

Seu cadastro foi aprovado. Você já pode acessar o portal e agendar sua primeira sessão.

\n

Acessar portal →

\n

Qualquer dúvida, estamos à disposição.
{{therapist_name}}

\n \N 1 t {"portal_link": "Link do portal do paciente", "patient_name": "Nome do paciente", "therapist_name": "Nome do terapeuta"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +04da6880-708a-4018-9b1f-0f398d152e0c intake.rejected.email intake email Atualização sobre seu cadastro — {{clinic_name}} \n

Olá, {{patient_name}}!

\n

Agradecemos seu interesse. Infelizmente não será possível dar continuidade ao seu cadastro no momento.

\n {{#if rejection_reason}}

{{rejection_reason}}

{{/if}}\n

{{clinic_name}}

\n \N 1 t {"clinic_name": "Nome da clínica", "patient_name": "Nome do paciente", "rejection_reason": "Motivo (opcional)"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +3e9ae66a-598c-4763-ab61-f02bb71323a4 scheduler.request_accepted.email session email Sua solicitação foi aceita — {{session_date}} às {{session_time}} \n

Olá, {{patient_name}}!

\n

Sua solicitação de agendamento foi aceita.

\n
    \n
  • Data: {{session_date}}
  • \n
  • Horário: {{session_time}}
  • \n
  • Tipo: {{session_type}}
  • \n
  • Modalidade: {{session_modality}}
  • \n
\n

Até logo,
{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Data confirmada", "session_time": "Horário confirmado", "session_type": "Primeira consulta / Retorno", "therapist_name": "Nome do terapeuta", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +7c7f736d-1ddb-46ba-ab41-f66d2267b730 scheduler.request_rejected.email session email Atualização sobre sua solicitação de agendamento \n

Olá, {{patient_name}}!

\n

Infelizmente não foi possível confirmar sua solicitação de agendamento para {{session_date}}.

\n {{#if rejection_reason}}

Motivo: {{rejection_reason}}

{{/if}}\n

Entre em contato para verificar outros horários disponíveis.

\n

{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Data solicitada", "therapist_name": "Nome do terapeuta", "rejection_reason": "Motivo (opcional)"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +78a33f29-3428-482f-9b0e-362c69ff20a8 system.welcome.email system email Bem-vindo(a) ao {{clinic_name}}! \n

Olá, {{patient_name}}!

\n

Seja bem-vindo(a)! Sua conta foi criada com sucesso.

\n

Acessar minha área →

\n \N 1 t {"clinic_name": "Nome da clínica", "portal_link": "Link do portal", "patient_name": "Nome do paciente"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +21a3fab3-01af-4b16-bd63-50929be5001d system.password_reset.email system email Redefinição de senha \n

Olá, {{patient_name}}!

\n

Recebemos uma solicitação para redefinir sua senha.

\n

Clique aqui para redefinir sua senha →

\n

Se você não solicitou a redefinição, ignore este e-mail.

\n \N 1 t {"reset_link": "Link de redefinição de senha", "patient_name": "Nome do usuário"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +\. + + +-- +-- Data for Name: email_templates_tenant; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.email_templates_tenant (id, tenant_id, owner_id, template_key, subject, body_html, body_text, enabled, synced_version, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: entitlements_invalidation; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.entitlements_invalidation (owner_id, changed_at) FROM stdin; +aaaaaaaa-0002-0002-0002-000000000002 2026-03-27 09:41:23.50964+00 +\. + + +-- +-- Data for Name: features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.features (id, key, description, created_at, descricao, name) FROM stdin; +5e539124-630f-4c2a-a9de-7999317085e6 agenda.view Visualização da agenda 2026-02-21 02:36:01.562728+00 Visualizar agenda Agenda - Visualizar +a74fef14-c9d9-4884-ba45-f81c60e0783a agenda.manage Gerenciamento completo da agenda 2026-02-21 02:35:50.629667+00 Adicionar Compromissos na agenda Agenda - Gerenciar +a56482a1-0787-49da-90a7-e1857488734a patients Módulo de pacientes 2026-03-02 12:35:19.955748+00 Pacientes Pacientes +57f631a1-9ebe-480b-a2cb-144ad32ff5f0 patients.view Visualização de pacientes 2026-02-28 11:15:14.275572+00 Visualizar pacientes Pacientes - Visualizar +4e5bc50b-e339-42fe-9d91-61e8555f83e7 patients.manage Gerenciamento completo de pacientes 2026-02-28 11:15:14.275572+00 Gerenciar pacientes Pacientes - Gerenciar +53a48c3b-0617-4618-adf8-f3a255c51ee4 online_scheduling Sistema de agendamento online 2026-03-01 09:59:15.432733+00 Agendamento online Agendamento Online +5739aa27-b089-4b15-b149-31b13d768825 online_scheduling.manage Gerenciamento do agendamento online 2026-02-15 21:50:02.056357+00 Gerenciar agendamento online (admin) Agendamento Online - Gerenciar +0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 online_scheduling.public Página pública do agendador 2026-02-15 21:50:02.056357+00 Página pública de agendamento Agendamento Online - Público +f5d66212-fd73-4472-a306-07928e5deaec reminders Sistema de lembretes automáticos 2026-03-01 09:59:15.432733+00 Lembretes Lembretes +b3efa25d-60a4-4974-8153-6ec098b3d477 reports_basic Relatórios básicos 2026-03-01 09:59:15.432733+00 Relatórios básicos Relatórios Básicos +bf133ad1-da8e-4ea9-bd66-21901cb50075 reports_advanced Relatórios avançados com exportação 2026-03-01 09:59:15.432733+00 Relatórios avançados Relatórios Avançados +336aeeba-b18e-4e68-8303-d42ba09f4b20 secretary Funcionalidade de secretária 2026-03-01 09:59:15.432733+00 Secretaria Secretária +30c9cdd5-7c8c-44d9-8c0b-614165bb9496 shared_reception Recepção compartilhada entre terapeutas 2026-03-02 12:35:19.955748+00 Recepção / Secretária Recepção Compartilhada +74fc1321-4d17-49c3-b72e-db3a7f4be451 rooms Gerenciamento de salas 2026-03-02 12:35:19.955748+00 Salas / Coworking Salas +c109ad27-0edf-4774-91a7-94dac4faab49 intake_public Formulário de intake público 2026-03-02 12:35:19.955748+00 Link externo de cadastro Intake Público +90e92108-8124-40ee-88a0-f0ecafb57d76 intakes_pro Funcionalidades avançadas de intake 2026-02-15 23:29:55.845638+00 Formulários PRO Intakes PRO +f393178c-284d-422f-b096-8793f85428d5 custom_branding Personalização de marca 2026-03-01 09:59:15.432733+00 Personalização de marca Branding Personalizado +d6f54674-ea8b-484b-af0e-99127a510da2 api_access Acesso via API 2026-03-01 09:59:15.432733+00 Integrações/API Acesso API +a5593d96-dd95-46bb-bef0-bd379b56ad50 audit_log Log de auditoria completo 2026-03-01 09:59:15.432733+00 Auditoria Log de Auditoria +8cc81988-d02a-4542-9cb2-ce2ed7c18d60 sms_reminder Lembretes via SMS 2026-02-15 23:29:55.845638+00 Lembretes por SMS Lembrete SMS +9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 clinic_calendar Visão consolidada do calendário 2026-03-01 09:59:15.432733+00 Agenda da clínica Calendário da Clínica +a830e45b-3bb4-4b17-812d-fe83777a2377 advanced_reports Relatórios avançados da clínica 2026-02-15 23:29:55.845638+00 Relatórios avançados Relatórios Avançados (Clínica) +9ab8bdbb-838b-4946-aa5d-fd9cfdd257b3 supervisor.access Acesso ao módulo de supervisão 2026-03-05 00:58:17.218326+00 Acesso básico ao espaço de supervisão (sala, lista de supervisionados). Supervisor - Acesso +1167b54a-0e93-43a2-94d7-c12e64eb56de supervisor.invite Convidar supervisionados 2026-03-05 00:58:17.218326+00 Permite convidar terapeutas para participar da sala de supervisão. Supervisor - Convidar +761e4495-b46a-4791-9519-86ffe48dc47f supervisor.sessions Gerenciar sessões de supervisão 2026-03-05 00:58:17.218326+00 Agendamento e registro de sessões de supervisão. Supervisor - Sessões +7e82ee01-44f6-4b3f-9861-840c58e13f58 supervisor.reports Relatórios de supervisão 2026-03-05 00:58:17.218326+00 Relatórios avançados de progresso e evolução dos supervisionados. Supervisor - Relatórios +\. + + +-- +-- Data for Name: feriados; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.feriados (id, tenant_id, owner_id, tipo, nome, data, cidade, estado, observacao, bloqueia_sessoes, criado_em) FROM stdin; +\. + + +-- +-- Data for Name: financial_categories; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.financial_categories (id, user_id, name, type, color, icon, sort_order, created_at) FROM stdin; +\. + + +-- +-- Data for Name: financial_exceptions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.financial_exceptions (id, owner_id, tenant_id, exception_type, charge_mode, charge_value, charge_pct, min_hours_notice, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: financial_records; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.financial_records (id, owner_id, tenant_id, type, amount, description, category, payment_method, paid_at, due_date, installments, installment_number, installment_group, agenda_evento_id, patient_id, clinic_fee_pct, clinic_fee_amount, insurance_plan_id, notes, tags, created_at, updated_at, deleted_at, discount_amount, final_amount, status, category_id) FROM stdin; +d51c8380-812a-45a7-8154-c799f4f95723 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 receita 1.00 \N \N \N \N 2026-03-25 1 1 \N \N 76cec0bb-1e63-45bf-9b03-feaaa2a5d18d 0.00 0.00 \N \N \N 2026-03-24 22:57:09.509577+00 2026-03-24 22:57:09.509577+00 \N 0.00 1.00 pending \N +\. + + +-- +-- Data for Name: global_notices; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.global_notices (id, title, message, variant, roles, contexts, starts_at, ends_at, is_active, priority, dismissible, persist_dismiss, dismiss_scope, show_once, max_views, cooldown_minutes, version, action_type, action_label, action_url, action_route, views_count, clicks_count, created_at, updated_at, created_by, content_align, link_target) FROM stdin; +\. + + +-- +-- Data for Name: login_carousel_slides; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.login_carousel_slides (id, title, body, icon, ordem, ativo, created_at, updated_at) FROM stdin; +0af63b3a-1819-4384-bf94-b29cbe84aca3 Gestão clínica simplificada Agendamentos, prontuários e sessões em um único painel. Foco no que importa: seus pacientes. pi-calendar-clock 0 t 2026-03-23 14:18:08.414378+00 2026-03-23 14:18:08.414378+00 +c02309d3-85cf-452f-bb85-363889aea9f3 Gestão clínica simplificada Gerencie agenda, pacientes e financeiro em um só lugar. Simples, rápido e seguro. pi-users 1 t 2026-03-23 14:18:08.414378+00 2026-03-23 14:18:08.414378+00 +763a600e-987c-4e42-8f62-29b5dea59c39 Múltiplos profissionais, uma só plataforma Ideal para clínicas com vários terapeutas. Cada profissional com sua agenda e seus pacientes. pi-shield 2 t 2026-03-23 14:18:08.414378+00 2026-03-23 14:18:08.414378+00 +\. + + +-- +-- Data for Name: modules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.modules (id, key, name, description, is_active, created_at) FROM stdin; +\. + + +-- +-- Data for Name: module_features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.module_features (module_id, feature_id, enabled, limits, created_at) FROM stdin; +\. + + +-- +-- Data for Name: notice_dismissals; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notice_dismissals (id, notice_id, user_id, version, dismissed_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_channels; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_channels (id, tenant_id, owner_id, channel, provider, is_active, display_name, sender_address, credentials, connection_status, last_health_check, metadata, created_at, updated_at, deleted_at, twilio_subaccount_sid, twilio_phone_number, twilio_phone_sid, webhook_url, cost_per_message_usd, price_per_message_brl, provisioned_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_queue; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_queue (id, tenant_id, owner_id, agenda_evento_id, patient_id, channel, template_key, schedule_key, resolved_vars, recipient_address, status, scheduled_at, sent_at, next_retry_at, attempts, max_attempts, last_error, idempotency_key, provider_message_id, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_logs; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_logs (id, tenant_id, owner_id, queue_id, agenda_evento_id, patient_id, channel, template_key, schedule_key, recipient_address, resolved_message, resolved_vars, status, provider, provider_message_id, provider_status, provider_response, sent_at, delivered_at, read_at, failed_at, failure_reason, estimated_cost_brl, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_preferences; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_preferences (id, tenant_id, owner_id, patient_id, whatsapp_opt_in, email_opt_in, sms_opt_in, preferred_time_start, preferred_time_end, lgpd_consent_given, lgpd_consent_date, lgpd_consent_version, lgpd_consent_ip, lgpd_opt_out_date, lgpd_opt_out_reason, created_at, updated_at, deleted_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_schedules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_schedules (id, tenant_id, owner_id, schedule_key, event_type, trigger_type, offset_minutes, whatsapp_enabled, email_enabled, sms_enabled, allowed_time_start, allowed_time_end, skip_weekends, skip_holidays, is_active, sort_order, created_at, updated_at, deleted_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_templates; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_templates (id, tenant_id, owner_id, key, domain, channel, event_type, body_text, meta_template_name, meta_template_namespace, meta_components, meta_status, variables, version, is_active, is_default, created_at, updated_at, deleted_at) FROM stdin; +37311b1a-2919-4f38-8dbc-7ccf81942062 \N \N session.reminder.whatsapp session whatsapp lembrete_sessao Olá {{nome_paciente}}! 👋\n\nLembrete: você tem sessão agendada para *{{data_sessao}}* às *{{hora_sessao}}* com {{nome_terapeuta}}.\n\n📋 {{modalidade}}\n\nPara confirmar, responda ✅\nPara cancelar, responda ❌\n\nSe precisar remarcar, entre em contato.\n\n_Responda SAIR para não receber mais lembretes._ \N \N \N draft ["nome_paciente", "data_sessao", "hora_sessao", "nome_terapeuta", "modalidade"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +4e61a64d-b2f9-49de-9d27-6496ddba8aef \N \N session.confirmation.whatsapp session whatsapp confirmacao_sessao ✅ Sessão confirmada!\n\nOlá {{nome_paciente}}, sua sessão com {{nome_terapeuta}} foi confirmada:\n\n📅 {{data_sessao}} às {{hora_sessao}}\n📋 {{modalidade}}\n\nTe esperamos! \N \N \N draft ["nome_paciente", "data_sessao", "hora_sessao", "nome_terapeuta", "modalidade"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +262e9dba-0324-478f-9d76-3faba555ec3c \N \N session.cancellation.whatsapp session whatsapp cancelamento_sessao Olá {{nome_paciente}},\n\nSua sessão de {{data_sessao}} às {{hora_sessao}} com {{nome_terapeuta}} foi *cancelada*.\n\nSe desejar reagendar, entre em contato.\n\nAtenciosamente,\n{{nome_terapeuta}} \N \N \N draft ["nome_paciente", "data_sessao", "hora_sessao", "nome_terapeuta"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +2c5331f9-6728-4cea-b4cc-e9b2659f5362 \N \N session.reminder.sms session sms lembrete_sessao Lembrete: sessao em {{data_sessao}} as {{hora_sessao}} com {{nome_terapeuta}}. Confirme respondendo OK. \N \N \N draft ["data_sessao", "hora_sessao", "nome_terapeuta"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +3e4386ba-2790-41e8-9b92-3b6beab549d8 \N \N session.lembrete.whatsapp session whatsapp lembrete_sessao Olá, {{nome_paciente}}! 👋\n\nLembrete da sua sessão com {{nome_terapeuta}}.\n\n📅 Data: {{data_sessao}}\n🕐 Hora: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nQualquer dúvida, entre em contato. Até lá! 💚 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +eb218e05-4ea0-4884-acd2-a766d3986d04 \N \N session.lembrete_2h.whatsapp session whatsapp lembrete_sessao Olá, {{nome_paciente}}! Sua sessão com {{nome_terapeuta}} começa em 2 horas.\n\n🕐 Hora: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nAté já! 😊 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +4574eb97-aeda-4cbb-a7e9-243c8f8c0b17 \N \N session.confirmacao.whatsapp session whatsapp confirmacao_sessao Olá, {{nome_paciente}}! ✅\n\nSua sessão foi confirmada!\n\n📅 Data: {{data_sessao}}\n🕐 Hora: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nAté lá! 💚 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade", "link_confirmacao"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +b321696c-96e2-4d16-8f8d-aa46d98945b8 \N \N session.cancelamento.whatsapp session whatsapp cancelamento_sessao Olá, {{nome_paciente}}. Infelizmente a sessão do dia {{data_sessao}} às {{hora_sessao}} foi cancelada.\n\nEntre em contato para reagendar. 🙏 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +216c6404-6e6c-4cb6-b5bd-486c8abc3c48 \N \N session.reagendamento.whatsapp session whatsapp reagendamento Olá, {{nome_paciente}}! Sua sessão foi reagendada.\n\n📅 Nova data: {{data_sessao}}\n🕐 Novo horário: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nAté lá! 😊 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +48a93a15-9675-49a7-a2d7-3b9b589e14f5 \N \N cobranca.pendente.whatsapp billing whatsapp cobranca_pendente Olá, {{nome_paciente}}. Identificamos uma cobrança pendente no valor de {{valor_sessao}} referente à sua sessão do dia {{data_sessao}}.\n\nPor favor, entre em contato para regularizar. 🙏 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade", "valor_sessao"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +26edef88-2d45-4ee2-8496-30f76f35cb77 \N \N sistema.boas_vindas.whatsapp system whatsapp boas_vindas_paciente Olá, {{nome_paciente}}! 🎉\n\nSeja muito bem-vindo(a)! Estamos felizes em ter você aqui.\n\nEm caso de dúvidas, estamos à disposição. Até a nossa primeira sessão! 💚 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +e66ef5f5-ab3a-46c1-8446-25b55487a017 \N \N session.confirmation.sms session sms confirmacao_sessao Sessão confirmada! {{session_date}} às {{session_time}} ({{session_modality}}) com {{therapist_name}}. \N \N \N draft ["patient_name", "session_date", "session_time", "session_modality", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +aae09094-82cb-406f-8015-c42cd24afded \N \N session.cancellation.sms session sms cancelamento_sessao Sua sessão de {{session_date}} às {{session_time}} foi cancelada. Entre em contato para reagendar. — {{therapist_name}} \N \N \N draft ["patient_name", "session_date", "session_time", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +c8521ea4-31c0-4015-8923-7ef57b3f8b47 \N \N session.rescheduled.sms session sms reagendamento Sua sessão foi reagendada para {{session_date}} às {{session_time}} ({{session_modality}}). — {{therapist_name}} \N \N \N draft ["patient_name", "session_date", "session_time", "session_modality", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +ba32ee4e-6548-48de-bf83-b0cb11a00761 \N \N intake.received.sms intake sms intake_recebido Olá {{patient_name}}, recebemos seu cadastro. Em breve entraremos em contato. — {{clinic_name}} \N \N \N draft ["patient_name", "clinic_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +7ccd6a04-8b89-4238-bb97-d78b0d56312f \N \N intake.approved.sms intake sms intake_aprovado Olá {{patient_name}}, seu cadastro foi aprovado! Acesse o portal para agendar sua sessão. — {{therapist_name}} \N \N \N draft ["patient_name", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +\. + + +-- +-- Data for Name: notifications; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notifications (id, owner_id, tenant_id, type, ref_id, ref_table, payload, read_at, archived, created_at) FROM stdin; +de9453b5-688c-411a-9ea1-c03ec3afea56 aaaaaaaa-0002-0002-0002-000000000002 \N new_patient f94e4a39-c92b-4f9b-82b4-1a2956f2140d patient_intake_requests {"title": "Novo cadastro externo", "detail": "Yasmin Gomes Ferreira", "deeplink": "/therapist/patients/cadastro/recebidos", "avatar_initials": "YA"} \N f 2026-03-23 23:25:39.670692+00 +efb5e6fd-ab89-46d9-b9d5-3534199b10e1 aaaaaaaa-0002-0002-0002-000000000002 \N new_patient 208f1883-a9d8-4056-831d-a22aec679cfb patient_intake_requests {"title": "Novo cadastro externo", "detail": "Carla Lima Almeida", "deeplink": "/therapist/patients/cadastro/recebidos", "avatar_initials": "CA"} 2026-03-23 23:34:19.18+00 f 2026-03-23 23:25:46.914188+00 +\. + + +-- +-- Data for Name: owner_users; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.owner_users (owner_id, user_id, role, created_at) FROM stdin; +\. + + +-- +-- Data for Name: patient_discounts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_discounts (id, owner_id, tenant_id, patient_id, discount_pct, discount_flat, reason, active, active_from, active_to, created_at) FROM stdin; +\. + + +-- +-- Data for Name: patient_group_patient; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_group_patient (patient_group_id, patient_id, created_at, tenant_id) FROM stdin; +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 2026-03-25 20:47:18.396593+00 bbbbbbbb-0002-0002-0002-000000000002 +a5b1e98b-e951-4f8a-b8ab-8221bf74342f 4fdd639d-5f32-453f-9092-74b183c8bbfe 2026-03-25 20:47:43.917435+00 bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 2aaa34f7-c770-4d78-a2a2-c77bd4771c6a 2026-03-25 20:58:12.198254+00 bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 c193905f-e70c-4935-aec3-b9c161c6044c 2026-03-25 20:58:37.212766+00 bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: patient_groups; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_groups (id, nome, descricao, cor, is_active, is_system, owner_id, created_at, updated_at, therapist_id, tenant_id) FROM stdin; +a5b1e98b-e951-4f8a-b8ab-8221bf74342f teste \N #22c55e t f aaaaaaaa-0002-0002-0002-000000000002 2026-03-23 15:35:37.961391+00 2026-03-23 17:13:24.546376+00 \N bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 teste 2 \N \N t f aaaaaaaa-0002-0002-0002-000000000002 2026-03-24 22:43:26.378387+00 2026-03-24 22:43:26.378387+00 \N bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: patient_intake_requests; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_intake_requests (id, owner_id, token, consent, status, created_at, converted_patient_id, rejected_reason, updated_at, cpf, rg, cep, nome_completo, email_principal, telefone, pais, cidade, estado, endereco, numero, bairro, complemento, data_nascimento, naturalidade, genero, estado_civil, onde_nos_conheceu, encaminhado_por, observacoes, notas_internas, email_alternativo, telefone_alternativo, profissao, escolaridade, nacionalidade, avatar_url, tenant_id) FROM stdin; +208f1883-a9d8-4056-831d-a22aec679cfb aaaaaaaa-0002-0002-0002-000000000002 9c0780f2-bf0a-442e-9aa5-787c2f585f5c t converted 2026-03-23 23:25:46.914188+00 f816b137-fb45-471c-81d2-bda8504b4f00 \N 2026-03-23 23:29:46.973175+00 68948962086 98.292.802-5 13561260 Carla Lima Almeida carla.lima.almeida.336@email.com 57973277841 Brasil São Carlos SP Avenida Tancredo de Almeida Neves 7363 Parque Santa Mônica Apto 122 1966-10-10 Campinas other single Outro \N Tenho disponibilidade no período da noite. \N \N \N Enfermeira Superior completo \N \N \N +f94e4a39-c92b-4f9b-82b4-1a2956f2140d aaaaaaaa-0002-0002-0002-000000000002 9c0780f2-bf0a-442e-9aa5-787c2f585f5c t converted 2026-03-23 23:25:39.670692+00 4214ea1c-c387-47ba-922d-5a492aca2ee7 \N 2026-03-23 23:30:08.162018+00 16487081612 43.076.886-9 13561260 Yasmin Gomes Ferreira yasmin.gomes.ferreira.69@email.com 12947152592 Brasil São Carlos SP Avenida Tancredo de Almeida Neves 8346 Parque Santa Mônica \N 1992-03-02 Bauru na single Google \N Cadastro realizado via link externo. \N \N \N Professora Pós-graduação \N \N \N +\. + + +-- +-- Data for Name: patient_invites; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_invites (id, owner_id, token, active, expires_at, max_uses, uses, created_at, tenant_id) FROM stdin; +d04d07d7-736c-4a28-8102-347cd2987bdd aaaaaaaa-0002-0002-0002-000000000002 9c0780f2-bf0a-442e-9aa5-787c2f585f5c t \N \N 0 2026-03-23 16:52:59.16431+00 \N +\. + + +-- +-- Data for Name: patient_tags; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_tags (id, owner_id, nome, cor, is_padrao, created_at, updated_at, tenant_id) FROM stdin; +69826bd2-005f-414f-a7fe-74b873e88012 aaaaaaaa-0002-0002-0002-000000000002 teste #b53e3e f 2026-03-23 16:56:08.12277+00 2026-03-23 22:54:26.043013+00 bbbbbbbb-0002-0002-0002-000000000002 +d39ec5c3-ae75-411c-9f2c-2d19f017b53b aaaaaaaa-0002-0002-0002-000000000002 teste2 #ef4444 f 2026-03-23 23:34:11.517479+00 \N bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: patient_patient_tag; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_patient_tag (owner_id, patient_id, tag_id, created_at, tenant_id) FROM stdin; +aaaaaaaa-0002-0002-0002-000000000002 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-25 20:47:18.644356+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-25 20:47:18.644356+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 4fdd639d-5f32-453f-9092-74b183c8bbfe 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-25 20:47:44.202242+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 2aaa34f7-c770-4d78-a2a2-c77bd4771c6a d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-25 20:58:12.518944+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 c193905f-e70c-4935-aec3-b9c161c6044c 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-25 20:58:37.551427+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 c193905f-e70c-4935-aec3-b9c161c6044c d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-25 20:58:37.551427+00 bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: payment_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.payment_settings (id, owner_id, tenant_id, pix_ativo, pix_tipo, pix_chave, pix_nome_titular, deposito_ativo, deposito_banco, deposito_agencia, deposito_conta, deposito_tipo_conta, deposito_titular, deposito_cpf_cnpj, dinheiro_ativo, cartao_ativo, cartao_instrucao, convenio_ativo, convenio_lista, observacoes_pagamento, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: plans; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plans (id, key, name, description, is_active, created_at, price_cents, currency, billing_interval, target, max_supervisees) FROM stdin; +984c1f29-a975-4208-93ac-2118ed1039b7 patient_free Paciente Free Plano gratuito para pacientes t 2026-03-03 22:40:11.413107+00 0 BRL month patient \N +c56fe2a8-2c17-4048-adc7-ff7fbd89461a therapist_free THERAPIST FREE Plano gratuito para terapeutas. t 2026-03-01 09:40:48.439668+00 0 BRL month therapist \N +82067ba7-16f0-4803-b36f-4c4e8919d4b4 therapist_pro THERAPIST PRO Plano completo para terapeutas. t 2026-03-01 09:25:03.878498+00 4900 BRL month therapist \N +01a5867f-0705-4714-ac97-a23470949157 clinic_free CLINIC FREE Plano gratuito para clínicas iniciarem. t 2026-03-01 09:25:03.878498+00 0 BRL month clinic \N +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 clinic_pro CLINIC PRO Plano completo para clínicas. t 2026-03-01 09:30:06.50975+00 14900 BRL month clinic \N +8c4895a3-e12d-48de-a078-efb8a4ea2eb2 supervisor_free Supervisor Free Plano gratuito de supervisão. Até 3 terapeutas supervisionados. t 2026-03-05 00:58:17.218326+00 0 BRL month supervisor 3 +ca28e46c-0687-45d5-9406-0a0f56a5b625 supervisor_pro Supervisor PRO Plano profissional de supervisão. Até 20 terapeutas supervisionados. t 2026-03-05 00:58:17.218326+00 0 BRL month supervisor 20 +\. + + +-- +-- Data for Name: plan_features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_features (plan_id, feature_id, enabled, limits, created_at) FROM stdin; +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 t {"max_patients": 9999, "max_therapists": 999} 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a56482a1-0787-49da-90a7-e1857488734a t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 9999} 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 bf133ad1-da8e-4ea9-bd66-21901cb50075 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a830e45b-3bb4-4b17-812d-fe83777a2377 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 9999} 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 336aeeba-b18e-4e68-8303-d42ba09f4b20 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 30c9cdd5-7c8c-44d9-8c0b-614165bb9496 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 74fc1321-4d17-49c3-b72e-db3a7f4be451 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 90e92108-8124-40ee-88a0-f0ecafb57d76 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 f393178c-284d-422f-b096-8793f85428d5 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 d6f54674-ea8b-484b-af0e-99127a510da2 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a5593d96-dd95-46bb-bef0-bd379b56ad50 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 t {"max_patients": 30, "max_therapists": 5} 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a56482a1-0787-49da-90a7-e1857488734a t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 40} 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 bf133ad1-da8e-4ea9-bd66-21901cb50075 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a830e45b-3bb4-4b17-812d-fe83777a2377 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 50} 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 90e92108-8124-40ee-88a0-f0ecafb57d76 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 336aeeba-b18e-4e68-8303-d42ba09f4b20 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 30c9cdd5-7c8c-44d9-8c0b-614165bb9496 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 74fc1321-4d17-49c3-b72e-db3a7f4be451 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 f393178c-284d-422f-b096-8793f85428d5 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 d6f54674-ea8b-484b-af0e-99127a510da2 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a5593d96-dd95-46bb-bef0-bd379b56ad50 f \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 9999} 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 bf133ad1-da8e-4ea9-bd66-21901cb50075 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 a830e45b-3bb4-4b17-812d-fe83777a2377 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 9999} 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 336aeeba-b18e-4e68-8303-d42ba09f4b20 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 90e92108-8124-40ee-88a0-f0ecafb57d76 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 f393178c-284d-422f-b096-8793f85428d5 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 d6f54674-ea8b-484b-af0e-99127a510da2 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 a5593d96-dd95-46bb-bef0-bd379b56ad50 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 40} 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a bf133ad1-da8e-4ea9-bd66-21901cb50075 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 50} 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 90e92108-8124-40ee-88a0-f0ecafb57d76 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a f393178c-284d-422f-b096-8793f85428d5 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a d6f54674-ea8b-484b-af0e-99127a510da2 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a a5593d96-dd95-46bb-bef0-bd379b56ad50 f \N 2026-03-23 14:18:07.731209+00 +8c4895a3-e12d-48de-a078-efb8a4ea2eb2 9ab8bdbb-838b-4946-aa5d-fd9cfdd257b3 t \N 2026-03-23 14:18:07.731209+00 +8c4895a3-e12d-48de-a078-efb8a4ea2eb2 1167b54a-0e93-43a2-94d7-c12e64eb56de t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 9ab8bdbb-838b-4946-aa5d-fd9cfdd257b3 t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 1167b54a-0e93-43a2-94d7-c12e64eb56de t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 761e4495-b46a-4791-9519-86ffe48dc47f t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 7e82ee01-44f6-4b3f-9861-840c58e13f58 t \N 2026-03-23 14:18:07.731209+00 +\. + + +-- +-- Data for Name: plan_prices; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_prices (id, plan_id, currency, "interval", amount_cents, is_active, active_from, active_to, source, provider, provider_price_id, created_at) FROM stdin; +37510504-4617-4421-9979-4249778bd5ae 82067ba7-16f0-4803-b36f-4c4e8919d4b4 BRL month 4900 t 2026-03-01 09:25:03.878498+00 \N manual \N \N 2026-03-01 09:25:03.878498+00 +225afd5a-9f30-46bc-a0df-5eb8f91660cb 82067ba7-16f0-4803-b36f-4c4e8919d4b4 BRL year 49000 t 2026-03-01 09:25:03.878498+00 \N manual \N \N 2026-03-01 09:25:03.878498+00 +124779b4-362d-4890-9631-747021ecc1c0 a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 BRL month 14900 t 2026-03-01 09:30:06.50975+00 \N manual \N \N 2026-03-01 09:30:06.50975+00 +73908784-6299-45c8-b547-e1556b45c292 a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 BRL year 149000 t 2026-03-01 09:30:06.50975+00 \N manual \N \N 2026-03-01 09:30:06.50975+00 +\. + + +-- +-- Data for Name: plan_public; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_public (plan_id, public_name, public_description, badge, is_featured, is_visible, sort_order, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: plan_public_bullets; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_public_bullets (id, plan_id, text, sort_order, highlight, created_at) FROM stdin; +\. + + +-- +-- Data for Name: professional_pricing; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.professional_pricing (id, owner_id, tenant_id, determined_commitment_id, price, notes, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: profiles; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.profiles (id, role, full_name, created_at, updated_at, avatar_url, phone, bio, language, timezone, notify_system_email, notify_reminders, notify_news, account_type, platform_roles, nickname, work_description, work_description_other, site_url, social_instagram, social_youtube, social_facebook, social_x, social_custom) FROM stdin; +aaaaaaaa-0001-0001-0001-000000000001 portal_user Ana Paciente 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N pt-BR America/Sao_Paulo t t f patient {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0002-0002-0002-000000000002 tenant_member Leonardo Nohama223 2026-03-23 10:46:29.876072+00 2026-03-27 14:18:26.567019+00 http://127.0.0.1:54321/storage/v1/object/public/avatars/aaaaaaaa-0002-0002-0002-000000000002/avatar.webp (11) 11111-1111 Bio 2asd pt-BR America/Sao_Paulo t t t therapist {} Léo psicologo_clinico \N \N \N \N \N \N [] +aaaaaaaa-0006-0006-0006-000000000006 saas_admin Admin Plataforma 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N pt-BR America/Sao_Paulo t t f free {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0007-0007-0007-000000000007 tenant_member Carlos Supervisor 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0009-0009-0009-000000000009 tenant_member Eva Terapeuta 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0010-0010-0010-000000000010 tenant_member Felipe Terapeuta 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0011-0011-0011-000000000011 tenant_member Gabriela Secretária 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0003-0003-0003-000000000003 tenant_member Clínica Espaço Psi 2026-03-23 10:46:29.876072+00 2026-03-23 14:18:43.066684+00 \N \N \N pt-BR America/Sao_Paulo t t f clinic {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0004-0004-0004-000000000004 tenant_member Clínica Mente Sã 2026-03-23 10:46:29.876072+00 2026-03-23 14:18:59.312611+00 \N \N \N pt-BR America/Sao_Paulo t t f clinic {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0005-0005-0005-000000000005 tenant_member Leonardo Nohama 2026-03-23 10:46:29.876072+00 2026-03-26 17:19:07.27483+00 \N (16) 98828-0038 bio curta pt-BR America/Sao_Paulo t t f clinic {} Léo psicanalista \N \N \N \N \N \N [] +aaaaaaaa-0008-0008-0008-000000000008 tenant_member Diana Editora 2026-03-23 14:18:05.215881+00 2026-03-26 19:46:29.161046+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {editor} \N \N \N \N \N \N \N \N [] +384a69d8-b7cd-40ac-9d3c-764c93532b66 portal_user \N 2026-03-26 19:47:30.477551+00 2026-03-26 19:47:30.68134+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +\. + + +-- +-- Data for Name: recurrence_exceptions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.recurrence_exceptions (id, recurrence_id, tenant_id, original_date, type, new_date, new_start_time, new_end_time, modalidade, observacoes, titulo_custom, extra_fields, reason, agenda_evento_id, created_at) FROM stdin; +\. + + +-- +-- Data for Name: recurrence_rule_services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.recurrence_rule_services (id, rule_id, service_id, quantity, unit_price, discount_pct, discount_flat, final_price, created_at) FROM stdin; +\. + + +-- +-- Data for Name: saas_admins; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_admins (user_id, created_at) FROM stdin; +aaaaaaaa-0006-0006-0006-000000000006 2026-03-23 10:46:29.876072+00 +\. + + +-- +-- Data for Name: saas_docs; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_docs (id, titulo, conteudo, medias, tipo_acesso, pagina_path, docs_relacionados, ativo, ordem, created_at, updated_at, categoria, exibir_no_faq, votos_util, votos_nao_util) FROM stdin; +\. + + +-- +-- Data for Name: saas_doc_votos; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_doc_votos (id, doc_id, user_id, util, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: saas_faq; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_faq (id, pergunta, categoria, publico, votos, titulo, conteudo, tipo_acesso, pagina_path, pagina_label, medias, faqs_relacionados, ativo, ordem, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: saas_faq_itens; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_faq_itens (id, doc_id, pergunta, resposta, ordem, ativo, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: subscriptions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscriptions (id, user_id, plan_id, status, current_period_start, current_period_end, cancel_at_period_end, provider, provider_customer_id, provider_subscription_id, created_at, updated_at, tenant_id, plan_key, "interval", source, started_at, canceled_at, activated_at, past_due_since, suspended_at, suspended_reason, cancelled_at, cancel_reason, expired_at) FROM stdin; +61f81f06-c718-4d52-8063-67c38c1c1df9 aaaaaaaa-0001-0001-0001-000000000001 984c1f29-a975-4208-93ac-2118ed1039b7 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N patient_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +8716fb6d-372f-4560-98a5-68c40aec96dc aaaaaaaa-0007-0007-0007-000000000007 8c4895a3-e12d-48de-a078-efb8a4ea2eb2 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N supervisor_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +3fc090ba-3e9a-47ef-9711-edf9a3fd1795 aaaaaaaa-0008-0008-0008-000000000008 c56fe2a8-2c17-4048-adc7-ff7fbd89461a active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N therapist_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +cc4c4eb4-c9e5-4ce7-a705-9f5dc415fcb4 aaaaaaaa-0009-0009-0009-000000000009 c56fe2a8-2c17-4048-adc7-ff7fbd89461a active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N therapist_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +d2516bea-712d-453a-a5ed-9e40456f9aca aaaaaaaa-0010-0010-0010-000000000010 c56fe2a8-2c17-4048-adc7-ff7fbd89461a active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N therapist_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +bd6dd2c0-9c50-4939-8fc9-41fd67423a3e \N 01a5867f-0705-4714-ac97-a23470949157 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 bbbbbbbb-0003-0003-0003-000000000003 clinic_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +505d05fa-a6db-46b7-8cbf-bb6b12fb8e2b \N 01a5867f-0705-4714-ac97-a23470949157 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 bbbbbbbb-0004-0004-0004-000000000004 clinic_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +27eef221-7db2-442a-99f3-989f910cdcb4 \N 01a5867f-0705-4714-ac97-a23470949157 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 bbbbbbbb-0005-0005-0005-000000000005 clinic_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +6864f71c-c4d4-4d7e-9897-89a644002b6d aaaaaaaa-0002-0002-0002-000000000002 82067ba7-16f0-4803-b36f-4c4e8919d4b4 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-27 09:41:23.50964+00 \N therapist_pro month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +\. + + +-- +-- Data for Name: subscription_events; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_events (id, subscription_id, owner_id, event_type, old_plan_id, new_plan_id, created_at, created_by, source, reason, metadata, owner_type, owner_ref) FROM stdin; +b9180edb-76a7-4f0b-be24-92f056091f14 6864f71c-c4d4-4d7e-9897-89a644002b6d aaaaaaaa-0002-0002-0002-000000000002 plan_changed c56fe2a8-2c17-4048-adc7-ff7fbd89461a 82067ba7-16f0-4803-b36f-4c4e8919d4b4 2026-03-27 09:41:23.50964+00 aaaaaaaa-0002-0002-0002-000000000002 dev_menu Plan change via DEV menu {"new_plan": "82067ba7-16f0-4803-b36f-4c4e8919d4b4", "new_plan_key": "therapist_pro", "previous_plan": "c56fe2a8-2c17-4048-adc7-ff7fbd89461a", "new_plan_target": "therapist"} therapist aaaaaaaa-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: subscription_intents_legacy; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_intents_legacy (id, user_id, email, plan_key, "interval", amount_cents, currency, status, source, notes, created_at, paid_at, tenant_id, created_by_user_id) FROM stdin; +\. + + +-- +-- Data for Name: subscription_intents_personal; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_intents_personal (id, user_id, created_by_user_id, email, plan_id, plan_key, "interval", amount_cents, currency, status, source, notes, created_at, paid_at, subscription_id) FROM stdin; +\. + + +-- +-- Data for Name: subscription_intents_tenant; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_intents_tenant (id, user_id, created_by_user_id, email, plan_id, plan_key, "interval", amount_cents, currency, status, source, notes, created_at, paid_at, tenant_id, subscription_id) FROM stdin; +\. + + +-- +-- Data for Name: support_sessions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.support_sessions (id, tenant_id, admin_id, token, expires_at, created_at) FROM stdin; +\. + + +-- +-- Data for Name: tenant_feature_exceptions_log; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_feature_exceptions_log (id, tenant_id, feature_key, enabled, reason, created_by, created_at) FROM stdin; +\. + + +-- +-- Data for Name: tenant_features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_features (tenant_id, feature_key, enabled, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: tenant_invites; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_invites (id, tenant_id, email, role, token, invited_by, created_at, expires_at, accepted_at, accepted_by, revoked_at, revoked_by) FROM stdin; +\. + + +-- +-- Data for Name: tenant_modules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_modules (id, owner_id, module_id, status, settings, provider, provider_item_id, installed_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: therapist_payouts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.therapist_payouts (id, owner_id, tenant_id, period_start, period_end, total_sessions, gross_amount, clinic_fee_total, net_amount, status, paid_at, notes, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: therapist_payout_records; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.therapist_payout_records (payout_id, financial_record_id) FROM stdin; +\. + + +-- +-- Data for Name: twilio_subaccount_usage; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.twilio_subaccount_usage (id, tenant_id, channel_id, twilio_subaccount_sid, period_start, period_end, messages_sent, messages_delivered, messages_failed, cost_usd, cost_brl, revenue_brl, usd_brl_rate, synced_at, created_at) FROM stdin; +\. + + +-- +-- Data for Name: user_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.user_settings (user_id, theme_mode, preset, primary_color, surface_color, menu_mode, created_at, updated_at, layout_variant) FROM stdin; +aaaaaaaa-0002-0002-0002-000000000002 dark Lara rose soho static 2026-03-25 18:47:05.458455+00 2026-03-27 14:18:26.578988+00 classic +aaaaaaaa-0005-0005-0005-000000000005 dark Aura orange slate static 2026-03-26 10:14:04.329765+00 2026-03-26 17:19:07.587294+00 classic +\. + + +-- +-- Name: refresh_tokens_id_seq; Type: SEQUENCE SET; Schema: auth; Owner: - +-- + +SELECT pg_catalog.setval('auth.refresh_tokens_id_seq', 137, true); + + +-- +-- Name: jobid_seq; Type: SEQUENCE SET; Schema: cron; Owner: - +-- + +SELECT pg_catalog.setval('cron.jobid_seq', 1, false); + + +-- +-- Name: runid_seq; Type: SEQUENCE SET; Schema: cron; Owner: - +-- + +SELECT pg_catalog.setval('cron.runid_seq', 1, false); + + +-- +-- Name: _db_migrations_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public._db_migrations_id_seq', 15, true); + + +-- +-- Name: agenda_online_slots_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.agenda_online_slots_id_seq', 1, false); + + +-- +-- PostgreSQL database dump complete +-- + +\unrestrict rLNwKN3lgxiGbLOVrmaPCsUPxow34x8FOOeMLqy7UR7fEtGHvkAU0wfwrrWEI90 + diff --git a/database-novo/backups/2026-03-27/full_dump.sql b/database-novo/backups/2026-03-27/full_dump.sql new file mode 100644 index 0000000..7832c62 --- /dev/null +++ b/database-novo/backups/2026-03-27/full_dump.sql @@ -0,0 +1,21202 @@ +-- +-- PostgreSQL database dump +-- + +\restrict oIIu7S4MFsQnGbpSaoEafTAdeIOsvmomx1CO8wf3ssLisMzLEg3OFPh8agVudRl + +-- Dumped from database version 17.6 +-- Dumped by pg_dump version 17.6 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET transaction_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Name: _realtime; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA _realtime; + + +-- +-- Name: auth; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA auth; + + +-- +-- Name: pg_cron; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_cron WITH SCHEMA pg_catalog; + + +-- +-- Name: EXTENSION pg_cron; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_cron IS 'Job scheduler for PostgreSQL'; + + +-- +-- Name: extensions; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA extensions; + + +-- +-- Name: graphql; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA graphql; + + +-- +-- Name: graphql_public; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA graphql_public; + + +-- +-- Name: pg_net; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_net WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION pg_net; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_net IS 'Async HTTP'; + + +-- +-- Name: pgbouncer; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA pgbouncer; + + +-- +-- Name: realtime; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA realtime; + + +-- +-- Name: storage; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA storage; + + +-- +-- Name: supabase_functions; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA supabase_functions; + + +-- +-- Name: vault; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA vault; + + +-- +-- Name: btree_gist; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS btree_gist WITH SCHEMA public; + + +-- +-- Name: EXTENSION btree_gist; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION btree_gist IS 'support for indexing common datatypes in GiST'; + + +-- +-- Name: citext; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public; + + +-- +-- Name: EXTENSION citext; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION citext IS 'data type for case-insensitive character strings'; + + +-- +-- Name: pg_graphql; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_graphql WITH SCHEMA graphql; + + +-- +-- Name: EXTENSION pg_graphql; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_graphql IS 'pg_graphql: GraphQL support'; + + +-- +-- Name: pg_stat_statements; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION pg_stat_statements; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_stat_statements IS 'track planning and execution statistics of all SQL statements executed'; + + +-- +-- Name: pg_trgm; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA public; + + +-- +-- Name: EXTENSION pg_trgm; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_trgm IS 'text similarity measurement and index searching based on trigrams'; + + +-- +-- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pgcrypto IS 'cryptographic functions'; + + +-- +-- Name: supabase_vault; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS supabase_vault WITH SCHEMA vault; + + +-- +-- Name: EXTENSION supabase_vault; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION supabase_vault IS 'Supabase Vault Extension'; + + +-- +-- Name: uuid-ossp; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION "uuid-ossp"; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)'; + + +-- +-- Name: aal_level; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.aal_level AS ENUM ( + 'aal1', + 'aal2', + 'aal3' +); + + +-- +-- Name: code_challenge_method; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.code_challenge_method AS ENUM ( + 's256', + 'plain' +); + + +-- +-- Name: factor_status; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.factor_status AS ENUM ( + 'unverified', + 'verified' +); + + +-- +-- Name: factor_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.factor_type AS ENUM ( + 'totp', + 'webauthn', + 'phone' +); + + +-- +-- Name: oauth_authorization_status; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_authorization_status AS ENUM ( + 'pending', + 'approved', + 'denied', + 'expired' +); + + +-- +-- Name: oauth_client_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_client_type AS ENUM ( + 'public', + 'confidential' +); + + +-- +-- Name: oauth_registration_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_registration_type AS ENUM ( + 'dynamic', + 'manual' +); + + +-- +-- Name: oauth_response_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_response_type AS ENUM ( + 'code' +); + + +-- +-- Name: one_time_token_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.one_time_token_type AS ENUM ( + 'confirmation_token', + 'reauthentication_token', + 'recovery_token', + 'email_change_token_new', + 'email_change_token_current', + 'phone_change_token' +); + + +-- +-- Name: commitment_log_source; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.commitment_log_source AS ENUM ( + 'manual', + 'auto' +); + + +-- +-- Name: determined_field_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.determined_field_type AS ENUM ( + 'text', + 'textarea', + 'number', + 'date', + 'select', + 'boolean' +); + + +-- +-- Name: financial_record_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.financial_record_type AS ENUM ( + 'receita', + 'despesa' +); + + +-- +-- Name: recurrence_exception_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.recurrence_exception_type AS ENUM ( + 'cancel_session', + 'reschedule_session', + 'patient_missed', + 'therapist_canceled', + 'holiday_block' +); + + +-- +-- Name: recurrence_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.recurrence_type AS ENUM ( + 'weekly', + 'biweekly', + 'monthly', + 'yearly', + 'custom_weekdays' +); + + +-- +-- Name: status_agenda_serie; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.status_agenda_serie AS ENUM ( + 'ativo', + 'pausado', + 'cancelado' +); + + +-- +-- Name: status_evento_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.status_evento_agenda AS ENUM ( + 'agendado', + 'realizado', + 'faltou', + 'cancelado', + 'remarcar' +); + + +-- +-- Name: status_excecao_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.status_excecao_agenda AS ENUM ( + 'pendente', + 'ativo', + 'arquivado' +); + + +-- +-- Name: tipo_evento_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.tipo_evento_agenda AS ENUM ( + 'sessao', + 'bloqueio' +); + + +-- +-- Name: tipo_excecao_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.tipo_excecao_agenda AS ENUM ( + 'bloqueio', + 'horario_extra' +); + + +-- +-- Name: action; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.action AS ENUM ( + 'INSERT', + 'UPDATE', + 'DELETE', + 'TRUNCATE', + 'ERROR' +); + + +-- +-- Name: equality_op; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.equality_op AS ENUM ( + 'eq', + 'neq', + 'lt', + 'lte', + 'gt', + 'gte', + 'in' +); + + +-- +-- Name: user_defined_filter; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.user_defined_filter AS ( + column_name text, + op realtime.equality_op, + value text +); + + +-- +-- Name: wal_column; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.wal_column AS ( + name text, + type_name text, + type_oid oid, + value jsonb, + is_pkey boolean, + is_selectable boolean +); + + +-- +-- Name: wal_rls; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.wal_rls AS ( + wal jsonb, + is_rls_enabled boolean, + subscription_ids uuid[], + errors text[] +); + + +-- +-- Name: buckettype; Type: TYPE; Schema: storage; Owner: - +-- + +CREATE TYPE storage.buckettype AS ENUM ( + 'STANDARD', + 'ANALYTICS', + 'VECTOR' +); + + +-- +-- Name: email(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.email() RETURNS text + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim.email', true), ''), + (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'email') + )::text +$$; + + +-- +-- Name: FUNCTION email(); Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON FUNCTION auth.email() IS 'Deprecated. Use auth.jwt() -> ''email'' instead.'; + + +-- +-- Name: jwt(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.jwt() RETURNS jsonb + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim', true), ''), + nullif(current_setting('request.jwt.claims', true), '') + )::jsonb +$$; + + +-- +-- Name: role(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.role() RETURNS text + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim.role', true), ''), + (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'role') + )::text +$$; + + +-- +-- Name: FUNCTION role(); Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON FUNCTION auth.role() IS 'Deprecated. Use auth.jwt() -> ''role'' instead.'; + + +-- +-- Name: uid(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.uid() RETURNS uuid + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim.sub', true), ''), + (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'sub') + )::uuid +$$; + + +-- +-- Name: FUNCTION uid(); Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON FUNCTION auth.uid() IS 'Deprecated. Use auth.jwt() -> ''sub'' instead.'; + + +-- +-- Name: grant_pg_cron_access(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.grant_pg_cron_access() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT + FROM pg_event_trigger_ddl_commands() AS ev + JOIN pg_extension AS ext + ON ev.objid = ext.oid + WHERE ext.extname = 'pg_cron' + ) + THEN + grant usage on schema cron to postgres with grant option; + + alter default privileges in schema cron grant all on tables to postgres with grant option; + alter default privileges in schema cron grant all on functions to postgres with grant option; + alter default privileges in schema cron grant all on sequences to postgres with grant option; + + alter default privileges for user supabase_admin in schema cron grant all + on sequences to postgres with grant option; + alter default privileges for user supabase_admin in schema cron grant all + on tables to postgres with grant option; + alter default privileges for user supabase_admin in schema cron grant all + on functions to postgres with grant option; + + grant all privileges on all tables in schema cron to postgres with grant option; + revoke all on table cron.job from postgres; + grant select on table cron.job to postgres with grant option; + END IF; +END; +$$; + + +-- +-- Name: FUNCTION grant_pg_cron_access(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.grant_pg_cron_access() IS 'Grants access to pg_cron'; + + +-- +-- Name: grant_pg_graphql_access(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.grant_pg_graphql_access() RETURNS event_trigger + LANGUAGE plpgsql + AS $_$ +DECLARE + func_is_graphql_resolve bool; +BEGIN + func_is_graphql_resolve = ( + SELECT n.proname = 'resolve' + FROM pg_event_trigger_ddl_commands() AS ev + LEFT JOIN pg_catalog.pg_proc AS n + ON ev.objid = n.oid + ); + + IF func_is_graphql_resolve + THEN + -- Update public wrapper to pass all arguments through to the pg_graphql resolve func + DROP FUNCTION IF EXISTS graphql_public.graphql; + create or replace function graphql_public.graphql( + "operationName" text default null, + query text default null, + variables jsonb default null, + extensions jsonb default null + ) + returns jsonb + language sql + as $$ + select graphql.resolve( + query := query, + variables := coalesce(variables, '{}'), + "operationName" := "operationName", + extensions := extensions + ); + $$; + + -- This hook executes when `graphql.resolve` is created. That is not necessarily the last + -- function in the extension so we need to grant permissions on existing entities AND + -- update default permissions to any others that are created after `graphql.resolve` + grant usage on schema graphql to postgres, anon, authenticated, service_role; + grant select on all tables in schema graphql to postgres, anon, authenticated, service_role; + grant execute on all functions in schema graphql to postgres, anon, authenticated, service_role; + grant all on all sequences in schema graphql to postgres, anon, authenticated, service_role; + alter default privileges in schema graphql grant all on tables to postgres, anon, authenticated, service_role; + alter default privileges in schema graphql grant all on functions to postgres, anon, authenticated, service_role; + alter default privileges in schema graphql grant all on sequences to postgres, anon, authenticated, service_role; + + -- Allow postgres role to allow granting usage on graphql and graphql_public schemas to custom roles + grant usage on schema graphql_public to postgres with grant option; + grant usage on schema graphql to postgres with grant option; + END IF; + +END; +$_$; + + +-- +-- Name: FUNCTION grant_pg_graphql_access(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.grant_pg_graphql_access() IS 'Grants access to pg_graphql'; + + +-- +-- Name: grant_pg_net_access(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.grant_pg_net_access() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_event_trigger_ddl_commands() AS ev + JOIN pg_extension AS ext + ON ev.objid = ext.oid + WHERE ext.extname = 'pg_net' + ) + THEN + GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role; + + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + + REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + + GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + END IF; +END; +$$; + + +-- +-- Name: FUNCTION grant_pg_net_access(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.grant_pg_net_access() IS 'Grants access to pg_net'; + + +-- +-- Name: pgrst_ddl_watch(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.pgrst_ddl_watch() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +DECLARE + cmd record; +BEGIN + FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands() + LOOP + IF cmd.command_tag IN ( + 'CREATE SCHEMA', 'ALTER SCHEMA' + , 'CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO', 'ALTER TABLE' + , 'CREATE FOREIGN TABLE', 'ALTER FOREIGN TABLE' + , 'CREATE VIEW', 'ALTER VIEW' + , 'CREATE MATERIALIZED VIEW', 'ALTER MATERIALIZED VIEW' + , 'CREATE FUNCTION', 'ALTER FUNCTION' + , 'CREATE TRIGGER' + , 'CREATE TYPE', 'ALTER TYPE' + , 'CREATE RULE' + , 'COMMENT' + ) + -- don't notify in case of CREATE TEMP table or other objects created on pg_temp + AND cmd.schema_name is distinct from 'pg_temp' + THEN + NOTIFY pgrst, 'reload schema'; + END IF; + END LOOP; +END; $$; + + +-- +-- Name: pgrst_drop_watch(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.pgrst_drop_watch() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +DECLARE + obj record; +BEGIN + FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() + LOOP + IF obj.object_type IN ( + 'schema' + , 'table' + , 'foreign table' + , 'view' + , 'materialized view' + , 'function' + , 'trigger' + , 'type' + , 'rule' + ) + AND obj.is_temporary IS false -- no pg_temp objects + THEN + NOTIFY pgrst, 'reload schema'; + END IF; + END LOOP; +END; $$; + + +-- +-- Name: set_graphql_placeholder(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.set_graphql_placeholder() RETURNS event_trigger + LANGUAGE plpgsql + AS $_$ + DECLARE + graphql_is_dropped bool; + BEGIN + graphql_is_dropped = ( + SELECT ev.schema_name = 'graphql_public' + FROM pg_event_trigger_dropped_objects() AS ev + WHERE ev.schema_name = 'graphql_public' + ); + + IF graphql_is_dropped + THEN + create or replace function graphql_public.graphql( + "operationName" text default null, + query text default null, + variables jsonb default null, + extensions jsonb default null + ) + returns jsonb + language plpgsql + as $$ + DECLARE + server_version float; + BEGIN + server_version = (SELECT (SPLIT_PART((select version()), ' ', 2))::float); + + IF server_version >= 14 THEN + RETURN jsonb_build_object( + 'errors', jsonb_build_array( + jsonb_build_object( + 'message', 'pg_graphql extension is not enabled.' + ) + ) + ); + ELSE + RETURN jsonb_build_object( + 'errors', jsonb_build_array( + jsonb_build_object( + 'message', 'pg_graphql is only available on projects running Postgres 14 onwards.' + ) + ) + ); + END IF; + END; + $$; + END IF; + + END; +$_$; + + +-- +-- Name: FUNCTION set_graphql_placeholder(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.set_graphql_placeholder() IS 'Reintroduces placeholder function for graphql_public.graphql'; + + +-- +-- Name: get_auth(text); Type: FUNCTION; Schema: pgbouncer; Owner: - +-- + +CREATE FUNCTION pgbouncer.get_auth(p_usename text) RETURNS TABLE(username text, password text) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $_$ +begin + raise debug 'PgBouncer auth request: %', p_usename; + + return query + select + rolname::text, + case when rolvaliduntil < now() + then null + else rolpassword::text + end + from pg_authid + where rolname=$1 and rolcanlogin; +end; +$_$; + + +-- +-- Name: __rls_ping(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.__rls_ping() RETURNS text + LANGUAGE sql STABLE + AS $$ + select 'ok'::text; +$$; + + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: subscriptions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscriptions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid, + plan_id uuid NOT NULL, + status text DEFAULT 'active'::text NOT NULL, + current_period_start timestamp with time zone, + current_period_end timestamp with time zone, + cancel_at_period_end boolean DEFAULT false NOT NULL, + provider text DEFAULT 'manual'::text NOT NULL, + provider_customer_id text, + provider_subscription_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid, + plan_key text, + "interval" text, + source text DEFAULT 'manual'::text NOT NULL, + started_at timestamp with time zone DEFAULT now() NOT NULL, + canceled_at timestamp with time zone, + activated_at timestamp with time zone, + past_due_since timestamp with time zone, + suspended_at timestamp with time zone, + suspended_reason text, + cancelled_at timestamp with time zone, + cancel_reason text, + expired_at timestamp with time zone, + CONSTRAINT subscriptions_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text]))), + CONSTRAINT subscriptions_owner_xor CHECK ((((tenant_id IS NOT NULL) AND (user_id IS NULL)) OR ((tenant_id IS NULL) AND (user_id IS NOT NULL)))), + CONSTRAINT subscriptions_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'active'::text, 'past_due'::text, 'suspended'::text, 'cancelled'::text, 'expired'::text]))) +); + + +-- +-- Name: activate_subscription_from_intent(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_intent record; + v_sub public.subscriptions; + v_days int; + v_user_id uuid; + v_plan_id uuid; + v_target text; +begin + -- l?? pela VIEW unificada + select * into v_intent + from public.subscription_intents + where id = p_intent_id; + + if not found then + raise exception 'Intent n??o encontrado: %', p_intent_id; + end if; + + if v_intent.status <> 'paid' then + raise exception 'Intent precisa estar paid para ativar assinatura'; + end if; + + -- resolve target e plan_id via plans.key + select p.id, p.target + into v_plan_id, v_target + from public.plans p + where p.key = v_intent.plan_key + limit 1; + + if v_plan_id is null then + raise exception 'Plano n??o encontrado em plans.key = %', v_intent.plan_key; + end if; + + v_target := lower(coalesce(v_target, '')); + + -- ??? supervisor adicionado + if v_target not in ('clinic', 'therapist', 'supervisor') then + raise exception 'Target inv??lido em plans.target: %', v_target; + end if; + + -- regra por target + if v_target = 'clinic' then + if v_intent.tenant_id is null then + raise exception 'Intent sem tenant_id'; + end if; + else + -- therapist ou supervisor: vinculado ao user + v_user_id := v_intent.user_id; + if v_user_id is null then + v_user_id := v_intent.created_by_user_id; + end if; + end if; + + if v_target in ('therapist', 'supervisor') and v_user_id is null then + raise exception 'N??o foi poss??vel determinar user_id para assinatura %.', v_target; + end if; + + -- cancela assinatura ativa anterior + if v_target = 'clinic' then + update public.subscriptions + set status = 'cancelled', + cancelled_at = now() + where tenant_id = v_intent.tenant_id + and plan_id = v_plan_id + and status = 'active'; + else + -- therapist ou supervisor + update public.subscriptions + set status = 'cancelled', + cancelled_at = now() + where user_id = v_user_id + and plan_id = v_plan_id + and status = 'active' + and tenant_id is null; + end if; + + -- dura????o do plano (30 dias para mensal) + v_days := case + when lower(coalesce(v_intent.interval, 'month')) = 'year' then 365 + else 30 + end; + + -- cria nova assinatura + insert into public.subscriptions ( + user_id, + plan_id, + status, + started_at, + expires_at, + cancelled_at, + activated_at, + tenant_id, + plan_key, + interval, + source, + created_at, + updated_at + ) + values ( + case when v_target = 'clinic' then null else v_user_id end, + v_plan_id, + 'active', + now(), + now() + make_interval(days => v_days), + null, + now(), + case when v_target = 'clinic' then v_intent.tenant_id else null end, + v_intent.plan_key, + v_intent.interval, + 'manual', + now(), + now() + ) + returning * into v_sub; + + -- grava v??nculo intent ??? subscription + if v_target = 'clinic' then + update public.subscription_intents_tenant + set subscription_id = v_sub.id + where id = p_intent_id; + else + update public.subscription_intents_personal + set subscription_id = v_sub.id + where id = p_intent_id; + end if; + + return v_sub; +end; +$$; + + +-- +-- Name: admin_credit_addon(uuid, text, integer, uuid, text, text, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Cr??dito manual'::text, p_payment_method text DEFAULT 'manual'::text, p_price_cents integer DEFAULT 0) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_credit addon_credits%ROWTYPE; + v_balance_before INTEGER; + v_balance_after INTEGER; + v_tx_id UUID; +BEGIN + -- Upsert addon_credits + INSERT INTO addon_credits (tenant_id, addon_type, balance, total_purchased) + VALUES (p_tenant_id, p_addon_type, 0, 0) + ON CONFLICT (tenant_id, addon_type) DO NOTHING; + + -- Lock e leitura + SELECT * INTO v_credit + FROM addon_credits + WHERE tenant_id = p_tenant_id AND addon_type = p_addon_type + FOR UPDATE; + + v_balance_before := v_credit.balance; + v_balance_after := v_credit.balance + p_amount; + + -- Atualiza saldo + UPDATE addon_credits + SET balance = v_balance_after, + total_purchased = total_purchased + p_amount, + low_balance_notified = CASE WHEN v_balance_after > COALESCE(low_balance_threshold, 10) THEN false ELSE low_balance_notified END, + updated_at = now() + WHERE id = v_credit.id; + + -- Registra transa????o + INSERT INTO addon_transactions ( + tenant_id, addon_type, type, amount, + balance_before, balance_after, + product_id, description, + admin_user_id, payment_method, price_cents + ) VALUES ( + p_tenant_id, p_addon_type, 'purchase', p_amount, + v_balance_before, v_balance_after, + p_product_id, p_description, + auth.uid(), p_payment_method, p_price_cents + ) + RETURNING id INTO v_tx_id; + + RETURN jsonb_build_object( + 'success', true, + 'transaction_id', v_tx_id, + 'balance_before', v_balance_before, + 'balance_after', v_balance_after + ); +END; +$$; + + +-- +-- Name: FUNCTION admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) IS 'Admin adiciona cr??ditos de add-on a um tenant. Cria registro se n??o existir.'; + + +-- +-- Name: admin_delete_email_template_global(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_delete_email_template_global(p_id uuid) RETURNS boolean + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + DELETE FROM public.email_templates_global WHERE id = p_id; + IF NOT FOUND THEN + RAISE EXCEPTION 'Template com id % n??o encontrado', p_id; + END IF; + RETURN true; +END; +$$; + + +-- +-- Name: admin_fix_plan_target(text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_plan_id uuid; +begin + -- (opcional) restringe targets v??lidos + if p_new_target not in ('clinic','therapist') then + raise exception 'Target inv??lido: %', p_new_target using errcode='P0001'; + end if; + + -- trava o plano + select id into v_plan_id + from public.plans + where key = p_plan_key + for update; + + if v_plan_id is null then + raise exception 'Plano n??o encontrado: %', p_plan_key using errcode='P0001'; + end if; + + -- seguran??a: n??o mexer se existe subscription + if exists (select 1 from public.subscriptions s where s.plan_id = v_plan_id) then + raise exception 'Plano % possui subscriptions. Migra????o bloqueada.', p_plan_key using errcode='P0001'; + end if; + + -- liga bypass SOMENTE nesta transa????o + perform set_config('app.plan_migration_bypass', '1', true); + + update public.plans + set target = p_new_target + where id = v_plan_id; + +end +$$; + + +-- +-- Name: admin_upsert_email_template_global(uuid, text, text, text, text, text, text, boolean, jsonb); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_upsert_email_template_global(p_id uuid DEFAULT NULL::uuid, p_key text DEFAULT NULL::text, p_domain text DEFAULT NULL::text, p_channel text DEFAULT 'email'::text, p_subject text DEFAULT NULL::text, p_body_html text DEFAULT NULL::text, p_body_text text DEFAULT NULL::text, p_is_active boolean DEFAULT true, p_variables jsonb DEFAULT '{}'::jsonb) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_result jsonb; + v_id uuid; +BEGIN + -- UPDATE existente + IF p_id IS NOT NULL THEN + UPDATE public.email_templates_global + SET + subject = COALESCE(p_subject, subject), + body_html = COALESCE(p_body_html, body_html), + body_text = p_body_text, + is_active = p_is_active, + variables = COALESCE(p_variables, variables), + version = version + 1 + WHERE id = p_id + RETURNING to_jsonb(email_templates_global.*) INTO v_result; + + IF v_result IS NULL THEN + RAISE EXCEPTION 'Template com id % n??o encontrado', p_id; + END IF; + + RETURN v_result; + END IF; + + -- INSERT novo + IF p_key IS NULL OR p_domain IS NULL OR p_subject IS NULL OR p_body_html IS NULL THEN + RAISE EXCEPTION 'key, domain, subject e body_html s??o obrigat??rios para novo template'; + END IF; + + INSERT INTO public.email_templates_global (key, domain, channel, subject, body_html, body_text, is_active, variables) + VALUES (p_key, p_domain, p_channel, p_subject, p_body_html, p_body_text, p_is_active, p_variables) + RETURNING to_jsonb(email_templates_global.*) INTO v_result; + + RETURN v_result; +END; +$$; + + +-- +-- Name: agenda_cfg_sync(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agenda_cfg_sync() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if new.agenda_view_mode = 'custom' then + new.usar_horario_admin_custom := true; + new.admin_inicio_visualizacao := new.agenda_custom_start; + new.admin_fim_visualizacao := new.agenda_custom_end; + else + new.usar_horario_admin_custom := false; + end if; + + return new; +end; +$$; + + +-- +-- Name: agendador_dias_disponiveis(text, integer, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer) RETURNS TABLE(data date, tem_slots boolean) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_owner_id uuid; + v_antecedencia int; + v_agora timestamptz; + v_data date; + v_data_inicio date; + v_data_fim date; + v_db_dow int; + v_tem_slot boolean; + v_bloqueado boolean; +BEGIN + SELECT c.owner_id, c.antecedencia_minima_horas + INTO v_owner_id, v_antecedencia + FROM public.agendador_configuracoes c + WHERE c.link_slug = p_slug AND c.ativo = true + LIMIT 1; + + IF v_owner_id IS NULL THEN RETURN; END IF; + + v_agora := now(); + v_data_inicio := make_date(p_ano, p_mes, 1); + v_data_fim := (v_data_inicio + interval '1 month' - interval '1 day')::date; + + v_data := v_data_inicio; + WHILE v_data <= v_data_fim LOOP + v_db_dow := extract(dow from v_data::timestamp)::int; + + -- ?????? Dia inteiro bloqueado? (agenda_bloqueios) ??????????????????????????????????????????????????????????????????????????? + SELECT EXISTS ( + SELECT 1 FROM public.agenda_bloqueios b + WHERE b.owner_id = v_owner_id + AND b.data_inicio <= v_data + AND COALESCE(b.data_fim, v_data) >= v_data + AND b.hora_inicio IS NULL -- bloqueio de dia inteiro + AND ( + (NOT b.recorrente) + OR (b.recorrente AND b.dia_semana = v_db_dow) + ) + ) INTO v_bloqueado; + + IF v_bloqueado THEN + v_data := v_data + 1; + CONTINUE; + END IF; + + -- ?????? Tem slots dispon??veis no dia? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????? + SELECT EXISTS ( + SELECT 1 FROM public.agenda_online_slots s + WHERE s.owner_id = v_owner_id + AND s.weekday = v_db_dow + AND s.enabled = true + AND (v_data::text || ' ' || s.time::text)::timestamp + AT TIME ZONE 'America/Sao_Paulo' + >= v_agora + (v_antecedencia || ' hours')::interval + ) INTO v_tem_slot; + + IF v_tem_slot THEN + data := v_data; + tem_slots := true; + RETURN NEXT; + END IF; + + v_data := v_data + 1; + END LOOP; +END; +$$; + + +-- +-- Name: agendador_gerar_slug(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agendador_gerar_slug() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_slug text; + v_exists boolean; +BEGIN + -- s?? gera se ativou e n??o tem slug ainda + IF NEW.ativo = true AND (NEW.link_slug IS NULL OR NEW.link_slug = '') THEN + LOOP + v_slug := lower(substring(replace(gen_random_uuid()::text, '-', ''), 1, 8)); + SELECT EXISTS ( + SELECT 1 FROM public.agendador_configuracoes + WHERE link_slug = v_slug AND owner_id <> NEW.owner_id + ) INTO v_exists; + EXIT WHEN NOT v_exists; + END LOOP; + NEW.link_slug := v_slug; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: agendador_slots_disponiveis(text, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) RETURNS TABLE(hora time without time zone, disponivel boolean) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_owner_id uuid; + v_duracao int; + v_antecedencia int; + v_agora timestamptz; + v_db_dow int; + v_slot time; + v_slot_fim time; + v_slot_ts timestamptz; + v_ocupado boolean; + -- loop de recorr??ncias + v_rule RECORD; + v_rule_start_dow int; + v_first_occ date; + v_day_diff int; + v_ex_type text; +BEGIN + SELECT c.owner_id, c.duracao_sessao_min, c.antecedencia_minima_horas + INTO v_owner_id, v_duracao, v_antecedencia + FROM public.agendador_configuracoes c + WHERE c.link_slug = p_slug AND c.ativo = true + LIMIT 1; + + IF v_owner_id IS NULL THEN RETURN; END IF; + + v_agora := now(); + v_db_dow := extract(dow from p_data::timestamp)::int; + + -- ?????? Dia inteiro bloqueado? (agenda_bloqueios sem hora) ????????????????????????????????????????????????????????? + -- Se sim, n??o h?? nenhum slot dispon??vel ??? retorna vazio. + IF EXISTS ( + SELECT 1 FROM public.agenda_bloqueios b + WHERE b.owner_id = v_owner_id + AND b.data_inicio <= p_data + AND COALESCE(b.data_fim, p_data) >= p_data + AND b.hora_inicio IS NULL -- bloqueio de dia inteiro + AND ( + (NOT b.recorrente) + OR (b.recorrente AND b.dia_semana = v_db_dow) + ) + ) THEN + RETURN; + END IF; + + FOR v_slot IN + SELECT s.time + FROM public.agenda_online_slots s + WHERE s.owner_id = v_owner_id + AND s.weekday = v_db_dow + AND s.enabled = true + ORDER BY s.time + LOOP + v_slot_fim := v_slot + (v_duracao || ' minutes')::interval; + v_ocupado := false; + + -- ?????? Anteced??ncia m??nima ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + v_slot_ts := (p_data::text || ' ' || v_slot::text)::timestamp + AT TIME ZONE 'America/Sao_Paulo'; + IF v_slot_ts < v_agora + (v_antecedencia || ' hours')::interval THEN + v_ocupado := true; + END IF; + + -- ?????? Bloqueio de hor??rio espec??fico (agenda_bloqueios com hora) ????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 FROM public.agenda_bloqueios b + WHERE b.owner_id = v_owner_id + AND b.data_inicio <= p_data + AND COALESCE(b.data_fim, p_data) >= p_data + AND b.hora_inicio IS NOT NULL + AND b.hora_inicio < v_slot_fim + AND b.hora_fim > v_slot + AND ( + (NOT b.recorrente) + OR (b.recorrente AND b.dia_semana = v_db_dow) + ) + ) INTO v_ocupado; + END IF; + + -- ?????? Eventos avulsos internos (agenda_eventos) ???????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 FROM public.agenda_eventos e + WHERE e.owner_id = v_owner_id + AND e.status::text NOT IN ('cancelado', 'faltou') + AND (e.inicio_em AT TIME ZONE 'America/Sao_Paulo')::date = p_data + AND (e.inicio_em AT TIME ZONE 'America/Sao_Paulo')::time < v_slot_fim + AND (e.fim_em AT TIME ZONE 'America/Sao_Paulo')::time > v_slot + ) INTO v_ocupado; + END IF; + + -- ?????? Recorr??ncias ativas (recurrence_rules) ????????????????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + FOR v_rule IN + SELECT + r.id, + r.start_date::date AS start_date, + r.end_date::date AS end_date, + r.start_time::time AS start_time, + r.end_time::time AS end_time, + COALESCE(r.interval, 1)::int AS interval + FROM public.recurrence_rules r + WHERE r.owner_id = v_owner_id + AND r.status = 'ativo' + AND p_data >= r.start_date::date + AND (r.end_date IS NULL OR p_data <= r.end_date::date) + AND v_db_dow = ANY(r.weekdays) + AND r.start_time::time < v_slot_fim + AND r.end_time::time > v_slot + LOOP + v_rule_start_dow := extract(dow from v_rule.start_date)::int; + v_first_occ := v_rule.start_date + + (((v_db_dow - v_rule_start_dow + 7) % 7))::int; + v_day_diff := (p_data - v_first_occ)::int; + + IF v_day_diff >= 0 AND v_day_diff % (7 * v_rule.interval) = 0 THEN + v_ex_type := NULL; + SELECT ex.type INTO v_ex_type + FROM public.recurrence_exceptions ex + WHERE ex.recurrence_id = v_rule.id + AND ex.original_date = p_data + LIMIT 1; + + IF v_ex_type IS NULL OR v_ex_type NOT IN ( + 'cancel_session', 'patient_missed', + 'therapist_canceled', 'holiday_block', + 'reschedule_session' + ) THEN + v_ocupado := true; + EXIT; + END IF; + END IF; + END LOOP; + END IF; + + -- ?????? Recorr??ncias remarcadas para este dia ???????????????????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 + FROM public.recurrence_exceptions ex + JOIN public.recurrence_rules r ON r.id = ex.recurrence_id + WHERE r.owner_id = v_owner_id + AND r.status = 'ativo' + AND ex.type = 'reschedule_session' + AND ex.new_date = p_data + AND COALESCE(ex.new_start_time, r.start_time)::time < v_slot_fim + AND COALESCE(ex.new_end_time, r.end_time)::time > v_slot + ) INTO v_ocupado; + END IF; + + -- ?????? Solicita????es p??blicas pendentes ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 FROM public.agendador_solicitacoes sol + WHERE sol.owner_id = v_owner_id + AND sol.status = 'pendente' + AND sol.data_solicitada = p_data + AND sol.hora_solicitada = v_slot + AND (sol.reservado_ate IS NULL OR sol.reservado_ate > v_agora) + ) INTO v_ocupado; + END IF; + + hora := v_slot; + disponivel := NOT v_ocupado; + RETURN NEXT; + END LOOP; +END; +$$; + + +-- +-- Name: auto_create_financial_record_from_session(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.auto_create_financial_record_from_session() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_price NUMERIC(10,2); + v_services_total NUMERIC(10,2); + v_already_billed BOOLEAN; +BEGIN + -- ?????? Guards de sa??da r??pida ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + + -- S?? processa quando o status muda PARA 'realizado' + IF NEW.status::TEXT <> 'realizado' THEN + RETURN NEW; + END IF; + + -- S?? processa quando houve mudan??a real de status + IF OLD.status IS NOT DISTINCT FROM NEW.status THEN + RETURN NEW; + END IF; + + -- S?? sess??es (n??o bloqueios, feriados, etc.) + IF NEW.tipo::TEXT <> 'sessao' THEN + RETURN NEW; + END IF; + + -- Paciente obrigat??rio para vincular a cobran??a + IF NEW.patient_id IS NULL THEN + RETURN NEW; + END IF; + + -- Sess??es de pacote t??m cobran??a gerenciada por billing_contract + IF NEW.billing_contract_id IS NOT NULL THEN + RETURN NEW; + END IF; + + -- Idempot??ncia: j?? existe financial_record para este evento? + SELECT billed INTO v_already_billed + FROM public.agenda_eventos + WHERE id = NEW.id; + + IF v_already_billed = TRUE THEN + -- Confirma no financial_records tamb??m (dupla verifica????o) + IF EXISTS ( + SELECT 1 FROM public.financial_records + WHERE agenda_evento_id = NEW.id AND deleted_at IS NULL + ) THEN + RETURN NEW; + END IF; + END IF; + + -- ?????? Busca do pre??o ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + + v_price := NULL; + + -- Prioridade 1: soma dos servi??os da regra de recorr??ncia + IF NEW.recurrence_id IS NOT NULL THEN + SELECT COALESCE(SUM(rrs.final_price), 0) + INTO v_services_total + FROM public.recurrence_rule_services rrs + WHERE rrs.rule_id = NEW.recurrence_id; + + IF v_services_total > 0 THEN + v_price := v_services_total; + END IF; + + -- Prioridade 2: price direto da regra (fallback se sem servi??os) + IF v_price IS NULL OR v_price = 0 THEN + SELECT price INTO v_price + FROM public.recurrence_rules + WHERE id = NEW.recurrence_id; + END IF; + END IF; + + -- Prioridade 3: price do pr??prio evento de agenda + IF v_price IS NULL OR v_price = 0 THEN + v_price := NEW.price; + END IF; + + -- Sem pre??o ??? n??o criar registro (n??o ?? erro, apenas skip silencioso) + IF v_price IS NULL OR v_price <= 0 THEN + RETURN NEW; + END IF; + + -- ?????? Cria????o do financial_record ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + + INSERT INTO public.financial_records ( + owner_id, + tenant_id, + patient_id, + agenda_evento_id, + type, + amount, + discount_amount, + final_amount, + clinic_fee_pct, + clinic_fee_amount, + status, + due_date + -- payment_method: NULL at?? o momento do pagamento (mark_as_paid preenche) + ) VALUES ( + NEW.owner_id, + NEW.tenant_id, + NEW.patient_id, + NEW.id, + 'receita', + v_price, + 0, + v_price, + 0, -- clinic_fee_pct: sem campo de configura????o global no schema atual. + 0, -- clinic_fee_amount: calculado manualmente ou via update posterior. + 'pending', + (NEW.inicio_em::DATE + 7) -- vencimento padr??o: 7 dias ap??s a sess??o + ); + + -- ?????? Marca sess??o como billed ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + -- UPDATE em billed (n??o em status) ??? n??o re-dispara este trigger + UPDATE public.agenda_eventos + SET billed = TRUE + WHERE id = NEW.id; + + RETURN NEW; + +EXCEPTION + WHEN OTHERS THEN + -- Log silencioso: nunca bloquear a agenda por falha financeira + RAISE WARNING '[auto_create_financial_record_from_session] evento=% erro=%', + NEW.id, SQLERRM; + RETURN NEW; +END; +$$; + + +-- +-- Name: FUNCTION auto_create_financial_record_from_session(); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.auto_create_financial_record_from_session() IS 'Trigger que cria automaticamente um financial_record (receita, pending) quando uma sess??o de agenda ?? marcada como realizada. Prioridade de pre??o: recurrence_rule_services > recurrence_rules.price > agenda_eventos.price. Skip silencioso se sem pre??o, pacote ou registro j?? existente.'; + + +-- +-- Name: can_delete_patient(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.can_delete_patient(p_patient_id uuid) RETURNS boolean + LANGUAGE sql STABLE SECURITY DEFINER + AS $$ + SELECT NOT EXISTS ( + SELECT 1 FROM public.agenda_eventos WHERE patient_id = p_patient_id + UNION ALL + SELECT 1 FROM public.recurrence_rules WHERE patient_id = p_patient_id + UNION ALL + SELECT 1 FROM public.billing_contracts WHERE patient_id = p_patient_id + ); +$$; + + +-- +-- Name: cancel_notifications_on_opt_out(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_notifications_on_opt_out() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + -- WhatsApp opt-out + IF OLD.whatsapp_opt_in = true AND NEW.whatsapp_opt_in = false THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, 'whatsapp' + ); + END IF; + -- Email opt-out + IF OLD.email_opt_in = true AND NEW.email_opt_in = false THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, 'email' + ); + END IF; + -- SMS opt-out + IF OLD.sms_opt_in = true AND NEW.sms_opt_in = false THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, 'sms' + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: cancel_notifications_on_session_cancel(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_notifications_on_session_cancel() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF NEW.status IN ('cancelado', 'excluido') + AND OLD.status NOT IN ('cancelado', 'excluido') + THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, NULL, NEW.id + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: cancel_patient_pending_notifications(uuid, text, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text DEFAULT NULL::text, p_evento_id uuid DEFAULT NULL::uuid) RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_canceled integer; +BEGIN + UPDATE public.notification_queue + SET status = 'cancelado', + updated_at = now() + WHERE patient_id = p_patient_id + AND status IN ('pendente', 'processando') + AND (p_channel IS NULL OR channel = p_channel) + AND (p_evento_id IS NULL OR agenda_evento_id = p_evento_id); + + GET DIAGNOSTICS v_canceled = ROW_COUNT; + RETURN v_canceled; +END; +$$; + + +-- +-- Name: cancel_recurrence_from(uuid, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + UPDATE public.recurrence_rules + SET + end_date = p_from_date - INTERVAL '1 day', + open_ended = false, + status = CASE + WHEN p_from_date <= start_date THEN 'cancelado' + ELSE status + END, + updated_at = now() + WHERE id = p_recurrence_id; +END; +$$; + + +-- +-- Name: cancel_subscription(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_subscription(p_subscription_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_owner_type text; + v_owner_ref uuid; +begin + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + if v_sub.status = 'canceled' then + return v_sub; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set status = 'canceled', + cancel_at_period_end = false, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'canceled', + v_sub.plan_id, + v_sub.plan_id, + auth.uid(), + 'Cancelamento manual via admin', + 'admin_panel', + jsonb_build_object('previous_status', 'active') + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation(owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; + +end; +$$; + + +-- +-- Name: cancelar_eventos_serie(uuid, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone DEFAULT now()) RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_count integer; +BEGIN + UPDATE public.agenda_eventos + SET status = 'cancelado', + updated_at = now() + WHERE serie_id = p_serie_id + AND inicio_em >= p_a_partir_de + AND status NOT IN ('realizado', 'cancelado'); + + GET DIAGNOSTICS v_count = ROW_COUNT; + RETURN v_count; +END; +$$; + + +-- +-- Name: FUNCTION cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone) IS 'Cancela todos os eventos futuros de uma s??rie a partir de p_a_partir_de (inclusive). + N??o cancela eventos j?? realizados.'; + + +-- +-- Name: change_subscription_plan(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_old_plan uuid; + v_new_key text; + + v_owner_type text; + v_owner_ref uuid; + + v_new_target text; + v_sub_target text; +begin + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + v_old_plan := v_sub.plan_id; + + if v_old_plan = p_new_plan_id then + return v_sub; + end if; + + select key, target + into v_new_key, v_new_target + from public.plans + where id = p_new_plan_id; + + if v_new_key is null then + raise exception 'Plano n??o encontrado'; + end if; + + v_new_target := lower(coalesce(v_new_target, '')); + + v_sub_target := case + when v_sub.tenant_id is not null then 'clinic' + else 'therapist' + end; + + if v_new_target <> v_sub_target then + raise exception 'Plano inv??lido para este tipo de assinatura. Assinatura ?? % e o plano ?? %.', + v_sub_target, v_new_target + using errcode = 'P0001'; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set plan_id = p_new_plan_id, + plan_key = v_new_key, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'plan_changed', + v_old_plan, + p_new_plan_id, + auth.uid(), + 'Plan change via DEV menu', + 'dev_menu', + jsonb_build_object( + 'previous_plan', v_old_plan, + 'new_plan', p_new_plan_id, + 'new_plan_key', v_new_key, + 'new_plan_target', v_new_target + ) + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation (owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; +end; +$$; + + +-- +-- Name: cleanup_notification_queue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cleanup_notification_queue() RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_deleted integer; +BEGIN + DELETE FROM public.notification_queue + WHERE status IN ('enviado', 'cancelado', 'ignorado') + AND created_at < now() - interval '90 days'; + + GET DIAGNOSTICS v_deleted = ROW_COUNT; + RETURN v_deleted; +END; +$$; + + +-- +-- Name: create_clinic_tenant(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_clinic_tenant(p_name text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_uid uuid; + v_tenant uuid; + v_name text; +begin + v_uid := auth.uid(); + if v_uid is null then + raise exception 'Not authenticated'; + end if; + + v_name := nullif(trim(coalesce(p_name, '')), ''); + if v_name is null then + v_name := 'Cl??nica'; + end if; + + insert into public.tenants (name, kind, created_at) + values (v_name, 'clinic', now()) + returning id into v_tenant; + + insert into public.tenant_members (tenant_id, user_id, role, status, created_at) + values (v_tenant, v_uid, 'tenant_admin', 'active', now()); + + return v_tenant; +end; +$$; + + +-- +-- Name: financial_records; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.financial_records ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + type public.financial_record_type DEFAULT 'receita'::public.financial_record_type NOT NULL, + amount numeric(10,2) NOT NULL, + description text, + category text, + payment_method text, + paid_at timestamp with time zone, + due_date date, + installments smallint DEFAULT 1, + installment_number smallint DEFAULT 1, + installment_group uuid, + agenda_evento_id uuid, + patient_id uuid, + clinic_fee_pct numeric(5,2) DEFAULT 0, + clinic_fee_amount numeric(10,2) DEFAULT 0, + net_amount numeric(10,2) GENERATED ALWAYS AS ((amount - clinic_fee_amount)) STORED, + insurance_plan_id uuid, + notes text, + tags text[], + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + discount_amount numeric(10,2) DEFAULT 0 NOT NULL, + final_amount numeric(10,2) DEFAULT 0 NOT NULL, + status text DEFAULT 'pending'::text NOT NULL, + category_id uuid, + CONSTRAINT financial_records_amount_check CHECK ((amount >= (0)::numeric)), + CONSTRAINT financial_records_clinic_fee_amount_check CHECK ((clinic_fee_amount >= (0)::numeric)), + CONSTRAINT financial_records_clinic_fee_pct_check CHECK (((clinic_fee_pct >= (0)::numeric) AND (clinic_fee_pct <= (100)::numeric))), + CONSTRAINT financial_records_discount_amount_check CHECK ((discount_amount >= (0)::numeric)), + CONSTRAINT financial_records_final_amount_check CHECK ((final_amount >= (0)::numeric)), + CONSTRAINT financial_records_installments_check CHECK ((installments >= 1)), + CONSTRAINT financial_records_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'paid'::text, 'partial'::text, 'overdue'::text, 'cancelled'::text, 'refunded'::text]))) +); + + +-- +-- Name: create_financial_record_for_session(uuid, uuid, uuid, uuid, numeric, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_financial_record_for_session(p_tenant_id uuid, p_owner_id uuid, p_patient_id uuid, p_agenda_evento_id uuid, p_amount numeric, p_due_date date) RETURNS SETOF public.financial_records + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_existing public.financial_records%ROWTYPE; + v_new public.financial_records%ROWTYPE; +BEGIN + -- Idempot??ncia: retorna o registro existente se j?? foi criado + SELECT * INTO v_existing + FROM public.financial_records + WHERE agenda_evento_id = p_agenda_evento_id + AND deleted_at IS NULL + LIMIT 1; + + IF FOUND THEN + RETURN NEXT v_existing; + RETURN; + END IF; + + -- Cria o novo registro + INSERT INTO public.financial_records ( + tenant_id, + owner_id, + patient_id, + agenda_evento_id, + amount, + discount_amount, + final_amount, + status, + due_date + ) VALUES ( + p_tenant_id, + p_owner_id, + p_patient_id, + p_agenda_evento_id, + p_amount, + 0, + p_amount, + 'pending', + p_due_date + ) + RETURNING * INTO v_new; + + -- Marca o evento da agenda como billed = true + UPDATE public.agenda_eventos + SET billed = TRUE + WHERE id = p_agenda_evento_id; + + RETURN NEXT v_new; +END; +$$; + + +-- +-- Name: create_patient_intake_request(text, text, text, text, text, boolean); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text DEFAULT NULL::text, p_phone text DEFAULT NULL::text, p_notes text DEFAULT NULL::text, p_consent boolean DEFAULT false) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_owner uuid; + v_active boolean; + v_expires timestamptz; + v_max_uses int; + v_uses int; + v_id uuid; +begin + select owner_id, active, expires_at, max_uses, uses + into v_owner, v_active, v_expires, v_max_uses, v_uses + from public.patient_invites + where token = p_token + limit 1; + + if v_owner is null then + raise exception 'Token inv??lido'; + end if; + + if v_active is not true then + raise exception 'Link desativado'; + end if; + + if v_expires is not null and now() > v_expires then + raise exception 'Link expirado'; + end if; + + if v_max_uses is not null and v_uses >= v_max_uses then + raise exception 'Limite de uso atingido'; + end if; + + if p_name is null or length(trim(p_name)) = 0 then + raise exception 'Nome ?? obrigat??rio'; + end if; + + insert into public.patient_intake_requests + (owner_id, token, name, email, phone, notes, consent, status) + values + (v_owner, p_token, trim(p_name), + nullif(lower(trim(p_email)), ''), + nullif(trim(p_phone), ''), + nullif(trim(p_notes), ''), + coalesce(p_consent, false), + 'new') + returning id into v_id; + + update public.patient_invites + set uses = uses + 1 + where token = p_token; + + return v_id; +end; +$$; + + +-- +-- Name: create_patient_intake_request_v2(text, jsonb); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $_$ +declare + v_owner_id uuid; + v_intake_id uuid; + v_birth_raw text; + v_birth date; +begin + select owner_id + into v_owner_id + from public.patient_invites + where token = p_token; + + if v_owner_id is null then + raise exception 'Token inv??lido ou expirado'; + end if; + + v_birth_raw := nullif(trim(coalesce( + p_payload->>'data_nascimento', + '' + )), ''); + + v_birth := case + when v_birth_raw is null then null + when v_birth_raw ~ '^\d{4}-\d{2}-\d{2}$' then v_birth_raw::date + when v_birth_raw ~ '^\d{2}-\d{2}-\d{4}$' then to_date(v_birth_raw, 'DD-MM-YYYY') + else null + end; + + insert into public.patient_intake_requests ( + owner_id, + token, + status, + consent, + + nome_completo, + email_principal, + telefone, + + avatar_url, -- ???? AQUI + + data_nascimento, + cpf, + rg, + genero, + estado_civil, + profissao, + escolaridade, + nacionalidade, + naturalidade, + + cep, + pais, + cidade, + estado, + endereco, + numero, + complemento, + bairro, + + observacoes, + notas_internas, + + encaminhado_por, + onde_nos_conheceu + ) + values ( + v_owner_id, + p_token, + 'new', + coalesce((p_payload->>'consent')::boolean, false), + + nullif(trim(p_payload->>'nome_completo'), ''), + nullif(trim(p_payload->>'email_principal'), ''), + nullif(regexp_replace(coalesce(p_payload->>'telefone',''), '\D', '', 'g'), ''), + + nullif(trim(p_payload->>'avatar_url'), ''), -- ???? AQUI + + v_birth, + nullif(regexp_replace(coalesce(p_payload->>'cpf',''), '\D', '', 'g'), ''), + nullif(trim(p_payload->>'rg'), ''), + nullif(trim(p_payload->>'genero'), ''), + nullif(trim(p_payload->>'estado_civil'), ''), + nullif(trim(p_payload->>'profissao'), ''), + nullif(trim(p_payload->>'escolaridade'), ''), + nullif(trim(p_payload->>'nacionalidade'), ''), + nullif(trim(p_payload->>'naturalidade'), ''), + + nullif(regexp_replace(coalesce(p_payload->>'cep',''), '\D', '', 'g'), ''), + nullif(trim(p_payload->>'pais'), ''), + nullif(trim(p_payload->>'cidade'), ''), + nullif(trim(p_payload->>'estado'), ''), + nullif(trim(p_payload->>'endereco'), ''), + nullif(trim(p_payload->>'numero'), ''), + nullif(trim(p_payload->>'complemento'), ''), + nullif(trim(p_payload->>'bairro'), ''), + + nullif(trim(p_payload->>'observacoes'), ''), + nullif(trim(p_payload->>'notas_internas'), ''), + + nullif(trim(p_payload->>'encaminhado_por'), ''), + nullif(trim(p_payload->>'onde_nos_conheceu'), '') + ) + returning id into v_intake_id; + + return v_intake_id; +end; +$_$; + + +-- +-- Name: create_support_session(uuid, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer DEFAULT 60) RETURNS json + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_admin_id uuid; + v_role text; + v_token text; + v_expires timestamp with time zone; + v_session support_sessions; +BEGIN + -- Verifica autentica????o + v_admin_id := auth.uid(); + IF v_admin_id IS NULL THEN + RAISE EXCEPTION 'N??o autenticado.' USING ERRCODE = 'P0001'; + END IF; + + -- Verifica role saas_admin + SELECT role INTO v_role + FROM public.profiles + WHERE id = v_admin_id; + + IF v_role <> 'saas_admin' THEN + RAISE EXCEPTION 'Acesso negado. Somente saas_admin pode criar sess??es de suporte.' + USING ERRCODE = 'P0002'; + END IF; + + -- Valida TTL (1 a 120 minutos) + IF p_ttl_minutes < 1 OR p_ttl_minutes > 120 THEN + RAISE EXCEPTION 'TTL inv??lido. Use entre 1 e 120 minutos.' + USING ERRCODE = 'P0003'; + END IF; + + -- Valida tenant + IF NOT EXISTS (SELECT 1 FROM public.tenants WHERE id = p_tenant_id) THEN + RAISE EXCEPTION 'Tenant n??o encontrado.' + USING ERRCODE = 'P0004'; + END IF; + + -- Gera token ??nico (64 chars hex, sem pgcrypto) + v_token := replace(gen_random_uuid()::text, '-', '') || replace(gen_random_uuid()::text, '-', ''); + v_expires := now() + (p_ttl_minutes || ' minutes')::interval; + + -- Insere sess??o + INSERT INTO public.support_sessions (tenant_id, admin_id, token, expires_at) + VALUES (p_tenant_id, v_admin_id, v_token, v_expires) + RETURNING * INTO v_session; + + RETURN json_build_object( + 'token', v_session.token, + 'expires_at', v_session.expires_at, + 'session_id', v_session.id + ); +END; +$$; + + +-- +-- Name: therapist_payouts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.therapist_payouts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + period_start date NOT NULL, + period_end date NOT NULL, + total_sessions integer DEFAULT 0 NOT NULL, + gross_amount numeric(10,2) DEFAULT 0 NOT NULL, + clinic_fee_total numeric(10,2) DEFAULT 0 NOT NULL, + net_amount numeric(10,2) DEFAULT 0 NOT NULL, + status text DEFAULT 'pending'::text NOT NULL, + paid_at timestamp with time zone, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT therapist_payouts_clinic_fee_total_check CHECK ((clinic_fee_total >= (0)::numeric)), + CONSTRAINT therapist_payouts_gross_amount_check CHECK ((gross_amount >= (0)::numeric)), + CONSTRAINT therapist_payouts_net_amount_check CHECK ((net_amount >= (0)::numeric)), + CONSTRAINT therapist_payouts_period_chk CHECK ((period_end >= period_start)), + CONSTRAINT therapist_payouts_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'paid'::text, 'cancelled'::text]))) +); + + +-- +-- Name: create_therapist_payout(uuid, uuid, date, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) RETURNS public.therapist_payouts + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_payout public.therapist_payouts%ROWTYPE; + v_total_sessions INTEGER; + v_gross NUMERIC(10,2); + v_clinic_fee NUMERIC(10,2); + v_net NUMERIC(10,2); +BEGIN + -- ?????? Verifica????o de permiss??o ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + -- Apenas o pr??prio terapeuta ou o tenant_admin pode criar o repasse + IF auth.uid() <> p_therapist_id AND NOT public.is_tenant_admin(p_tenant_id) THEN + RAISE EXCEPTION 'Sem permiss??o para criar repasse para este terapeuta.'; + END IF; + + -- ?????? Verifica se j?? existe repasse para o mesmo per??odo ??????????????????????????????????????????????????? + IF EXISTS ( + SELECT 1 FROM public.therapist_payouts + WHERE owner_id = p_therapist_id + AND tenant_id = p_tenant_id + AND period_start = p_period_start + AND period_end = p_period_end + AND status <> 'cancelled' + ) THEN + RAISE EXCEPTION + 'J?? existe um repasse ativo para o per??odo % a % deste terapeuta.', + p_period_start, p_period_end; + END IF; + + -- ?????? Agrega os financial_records eleg??veis ?????????????????????????????????????????????????????????????????????????????????????????? + -- Eleg??veis: paid, receita, owner=terapeuta, tenant correto, paid_at no per??odo, + -- n??o soft-deleted, ainda n??o vinculados a nenhum payout. + SELECT + COUNT(*) AS total_sessions, + COALESCE(SUM(amount), 0) AS gross_amount, + COALESCE(SUM(clinic_fee_amount), 0) AS clinic_fee_total, + COALESCE(SUM(net_amount), 0) AS net_amount + INTO + v_total_sessions, v_gross, v_clinic_fee, v_net + FROM public.financial_records fr + WHERE fr.owner_id = p_therapist_id + AND fr.tenant_id = p_tenant_id + AND fr.type = 'receita' + AND fr.status = 'paid' + AND fr.deleted_at IS NULL + AND fr.paid_at::DATE BETWEEN p_period_start AND p_period_end + AND NOT EXISTS ( + SELECT 1 FROM public.therapist_payout_records tpr + WHERE tpr.financial_record_id = fr.id + ); + + -- Sem registros eleg??veis ??? n??o criar payout vazio + IF v_total_sessions = 0 THEN + RAISE EXCEPTION + 'Nenhum registro financeiro eleg??vel encontrado para o per??odo % a %.', + p_period_start, p_period_end; + END IF; + + -- ?????? Cria o repasse ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + INSERT INTO public.therapist_payouts ( + owner_id, + tenant_id, + period_start, + period_end, + total_sessions, + gross_amount, + clinic_fee_total, + net_amount, + status + ) VALUES ( + p_therapist_id, + p_tenant_id, + p_period_start, + p_period_end, + v_total_sessions, + v_gross, + v_clinic_fee, + v_net, + 'pending' + ) + RETURNING * INTO v_payout; + + -- ?????? Vincula os financial_records ao repasse ???????????????????????????????????????????????????????????????????????????????????? + INSERT INTO public.therapist_payout_records (payout_id, financial_record_id) + SELECT v_payout.id, fr.id + FROM public.financial_records fr + WHERE fr.owner_id = p_therapist_id + AND fr.tenant_id = p_tenant_id + AND fr.type = 'receita' + AND fr.status = 'paid' + AND fr.deleted_at IS NULL + AND fr.paid_at::DATE BETWEEN p_period_start AND p_period_end + AND NOT EXISTS ( + SELECT 1 FROM public.therapist_payout_records tpr + WHERE tpr.financial_record_id = fr.id + ); + + RETURN v_payout; +END; +$$; + + +-- +-- Name: FUNCTION create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) IS 'Cria um repasse para o terapeuta com todos os financial_records paid+receita do per??odo que ainda n??o estejam vinculados a outro repasse. Lan??a exce????o se n??o houver registros eleg??veis ou se j?? houver repasse ativo no per??odo.'; + + +-- +-- Name: current_member_id(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.current_member_id(p_tenant_id uuid) RETURNS uuid + LANGUAGE sql STABLE + AS $$ + select tm.id + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + limit 1 +$$; + + +-- +-- Name: current_member_role(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.current_member_role(p_tenant_id uuid) RETURNS text + LANGUAGE sql STABLE + AS $$ + select tm.role + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + limit 1 +$$; + + +-- +-- Name: debit_addon_credit(uuid, text, uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Consumo'::text) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_credit addon_credits%ROWTYPE; + v_balance_before INTEGER; + v_balance_after INTEGER; +BEGIN + -- Lock e leitura + SELECT * INTO v_credit + FROM addon_credits + WHERE tenant_id = p_tenant_id AND addon_type = p_addon_type AND is_active = true + FOR UPDATE; + + IF NOT FOUND THEN + RETURN jsonb_build_object('success', false, 'reason', 'no_credits', 'balance', 0); + END IF; + + -- Verifica saldo + IF v_credit.balance <= 0 THEN + RETURN jsonb_build_object('success', false, 'reason', 'insufficient_balance', 'balance', 0); + END IF; + + -- Verifica rate limit di??rio + IF v_credit.daily_limit IS NOT NULL THEN + -- Reset se passou do dia + IF v_credit.daily_reset_at IS NULL OR v_credit.daily_reset_at < date_trunc('day', now()) THEN + UPDATE addon_credits SET daily_used = 0, daily_reset_at = date_trunc('day', now()) + interval '1 day' WHERE id = v_credit.id; + v_credit.daily_used := 0; + END IF; + + IF v_credit.daily_used >= v_credit.daily_limit THEN + RETURN jsonb_build_object('success', false, 'reason', 'daily_limit_reached', 'balance', v_credit.balance); + END IF; + END IF; + + -- Verifica rate limit hor??rio + IF v_credit.hourly_limit IS NOT NULL THEN + IF v_credit.hourly_reset_at IS NULL OR v_credit.hourly_reset_at < date_trunc('hour', now()) THEN + UPDATE addon_credits SET hourly_used = 0, hourly_reset_at = date_trunc('hour', now()) + interval '1 hour' WHERE id = v_credit.id; + v_credit.hourly_used := 0; + END IF; + + IF v_credit.hourly_used >= v_credit.hourly_limit THEN + RETURN jsonb_build_object('success', false, 'reason', 'hourly_limit_reached', 'balance', v_credit.balance); + END IF; + END IF; + + -- Verifica expira????o + IF v_credit.expires_at IS NOT NULL AND v_credit.expires_at < now() THEN + RETURN jsonb_build_object('success', false, 'reason', 'credits_expired', 'balance', v_credit.balance); + END IF; + + v_balance_before := v_credit.balance; + v_balance_after := v_credit.balance - 1; + + -- Debita + UPDATE addon_credits + SET balance = v_balance_after, + total_consumed = total_consumed + 1, + daily_used = COALESCE(daily_used, 0) + 1, + hourly_used = COALESCE(hourly_used, 0) + 1, + updated_at = now() + WHERE id = v_credit.id; + + -- Registra transa????o + INSERT INTO addon_transactions ( + tenant_id, addon_type, type, amount, + balance_before, balance_after, + queue_id, description + ) VALUES ( + p_tenant_id, p_addon_type, 'consume', -1, + v_balance_before, v_balance_after, + p_queue_id, p_description + ); + + RETURN jsonb_build_object( + 'success', true, + 'balance_before', v_balance_before, + 'balance_after', v_balance_after + ); +END; +$$; + + +-- +-- Name: FUNCTION debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) IS 'Debita 1 cr??dito de add-on. Verifica saldo, rate limits e expira????o.'; + + +-- +-- Name: delete_commitment_full(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_is_native boolean; + v_fields int := 0; + v_logs int := 0; + v_parent int := 0; +begin + if auth.uid() is null then + raise exception 'Not authenticated'; + end if; + + if not exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.status = 'active' + ) then + raise exception 'Not allowed'; + end if; + + select dc.is_native + into v_is_native + from public.determined_commitments dc + where dc.tenant_id = p_tenant_id + and dc.id = p_commitment_id; + + if v_is_native is null then + raise exception 'Commitment not found'; + end if; + + if v_is_native = true then + raise exception 'Cannot delete native commitment'; + end if; + + delete from public.determined_commitment_fields + where tenant_id = p_tenant_id + and commitment_id = p_commitment_id; + get diagnostics v_fields = row_count; + + delete from public.commitment_time_logs + where tenant_id = p_tenant_id + and commitment_id = p_commitment_id; + get diagnostics v_logs = row_count; + + delete from public.determined_commitments + where tenant_id = p_tenant_id + and id = p_commitment_id; + get diagnostics v_parent = row_count; + + if v_parent <> 1 then + raise exception 'Parent not deleted (RLS/owner issue).'; + end if; + + return jsonb_build_object( + 'ok', true, + 'deleted', jsonb_build_object( + 'fields', v_fields, + 'logs', v_logs, + 'commitment', v_parent + ) + ); +end; +$$; + + +-- +-- Name: delete_determined_commitment(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_is_native boolean; + v_fields_deleted int := 0; + v_logs_deleted int := 0; + v_commitment_deleted int := 0; +begin + if auth.uid() is null then + raise exception 'Not authenticated'; + end if; + + if not exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.status = 'active' + ) then + raise exception 'Not allowed'; + end if; + + select dc.is_native + into v_is_native + from public.determined_commitments dc + where dc.tenant_id = p_tenant_id + and dc.id = p_commitment_id; + + if v_is_native is null then + raise exception 'Commitment not found for tenant'; + end if; + + if v_is_native = true then + raise exception 'Cannot delete native commitment'; + end if; + + delete from public.determined_commitment_fields f + where f.tenant_id = p_tenant_id + and f.commitment_id = p_commitment_id; + get diagnostics v_fields_deleted = row_count; + + delete from public.commitment_time_logs l + where l.tenant_id = p_tenant_id + and l.commitment_id = p_commitment_id; + get diagnostics v_logs_deleted = row_count; + + delete from public.determined_commitments dc + where dc.tenant_id = p_tenant_id + and dc.id = p_commitment_id; + get diagnostics v_commitment_deleted = row_count; + + if v_commitment_deleted <> 1 then + raise exception 'Delete did not remove the commitment (tenant mismatch?)'; + end if; + + return jsonb_build_object( + 'ok', true, + 'tenant_id', p_tenant_id, + 'commitment_id', p_commitment_id, + 'deleted', jsonb_build_object( + 'fields', v_fields_deleted, + 'logs', v_logs_deleted, + 'commitment', v_commitment_deleted + ) + ); +end; +$$; + + +-- +-- Name: dev_list_auth_users(integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_list_auth_users(p_limit integer DEFAULT 50) RETURNS TABLE(id uuid, email text, created_at timestamp with time zone) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +begin + -- s?? saas_admin pode ver + if not exists ( + select 1 + from public.profiles p + where p.id = auth.uid() + and p.role = 'saas_admin' + ) then + return; + end if; + + return query + select + u.id, + u.email, + u.created_at + from auth.users u + order by u.created_at desc + limit greatest(1, least(coalesce(p_limit, 50), 500)); +end; +$$; + + +-- +-- Name: dev_list_custom_users(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_list_custom_users() RETURNS TABLE(user_id uuid, email text, created_at timestamp with time zone, global_role text, tenant_role text, tenant_id uuid, password_dev text, kind text) + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + with base as ( + select + u.id as user_id, + lower(u.email) as email, + u.created_at + from auth.users u + where lower(u.email) not in ( + 'clinic@agenciapsi.com.br', + 'therapist@agenciapsi.com.br', + 'patient@agenciapsi.com.br', + 'saas@agenciapsi.com.br' + ) + ), + prof as ( + select p.id, p.role as global_role + from public.profiles p + ), + last_membership as ( + select distinct on (tm.user_id) + tm.user_id, + tm.tenant_id, + tm.role as tenant_role, + tm.created_at + from public.tenant_members tm + where tm.status = 'active' + order by tm.user_id, tm.created_at desc + ) + select + b.user_id, + b.email, + b.created_at, + pr.global_role, + lm.tenant_role, + lm.tenant_id, + dc.password_dev, + dc.kind + from base b + left join prof pr on pr.id = b.user_id + left join last_membership lm on lm.user_id = b.user_id + left join public.dev_user_credentials dc on lower(dc.email) = b.email + order by b.created_at desc; +$$; + + +-- +-- Name: dev_list_intent_leads(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_list_intent_leads() RETURNS TABLE(email text, last_intent_at timestamp with time zone, plan_key text, billing_interval text, status text, tenant_id uuid) + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + select + lower(si.email) as email, + max(si.created_at) as last_intent_at, + (array_agg(si.plan_key order by si.created_at desc))[1] as plan_key, + (array_agg(si.interval order by si.created_at desc))[1] as billing_interval, + (array_agg(si.status order by si.created_at desc))[1] as status, + (array_agg(si.tenant_id order by si.created_at desc))[1] as tenant_id + from public.subscription_intents si + where si.email is not null + and not exists ( + select 1 + from auth.users au + where lower(au.email) = lower(si.email) + ) + group by lower(si.email) + order by max(si.created_at) desc; +$$; + + +-- +-- Name: dev_public_debug_snapshot(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_public_debug_snapshot() RETURNS TABLE(users_total integer, tenants_total integer, intents_new_total integer, latest_intents jsonb) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $_$ +declare + v_latest jsonb; +begin + select jsonb_agg( + jsonb_build_object( + 'created_at', si.created_at, + 'email_masked', + regexp_replace(lower(si.email), '(^.).*(@.*$)', '\1***\2'), + 'plan_key', si.plan_key, + 'status', si.status + ) + order by si.created_at desc + ) + into v_latest + from ( + select si.* + from public.subscription_intents si + where si.email is not null + order by si.created_at desc + limit 5 + ) si; + + return query + select + (select count(*)::int from auth.users) as users_total, + (select count(*)::int from public.tenants) as tenants_total, + (select count(*)::int from public.subscription_intents where status = 'new') as intents_new_total, + coalesce(v_latest, '[]'::jsonb) as latest_intents; +end; +$_$; + + +-- +-- Name: ensure_personal_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.ensure_personal_tenant() RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_uid uuid; + v_existing uuid; +BEGIN + v_uid := auth.uid(); + IF v_uid IS NULL THEN + RAISE EXCEPTION 'Not authenticated'; + END IF; + + SELECT tm.tenant_id INTO v_existing + FROM public.tenant_members tm + JOIN public.tenants t ON t.id = tm.tenant_id + WHERE tm.user_id = v_uid + AND tm.status = 'active' + AND t.kind IN ('therapist', 'saas') + ORDER BY tm.created_at DESC + LIMIT 1; + + IF v_existing IS NOT NULL THEN + RETURN v_existing; + END IF; + + RETURN public.provision_account_tenant(v_uid, 'therapist'); +END; +$$; + + +-- +-- Name: ensure_personal_tenant_for_user(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_uid uuid; + v_existing uuid; + v_tenant uuid; + v_email text; + v_name text; +begin + v_uid := p_user_id; + if v_uid is null then + raise exception 'Missing user id'; + end if; + + -- s?? considera tenant pessoal (kind='saas') + select tm.tenant_id + into v_existing + from public.tenant_members tm + join public.tenants t on t.id = tm.tenant_id + where tm.user_id = v_uid + and tm.status = 'active' + and t.kind = 'saas' + order by tm.created_at desc + limit 1; + + if v_existing is not null then + return v_existing; + end if; + + select email into v_email + from auth.users + where id = v_uid; + + v_name := coalesce(split_part(v_email, '@', 1), 'Conta'); + + insert into public.tenants (name, kind, created_at) + values (v_name || ' (Pessoal)', 'saas', now()) + returning id into v_tenant; + + insert into public.tenant_members (tenant_id, user_id, role, status, created_at) + values (v_tenant, v_uid, 'tenant_admin', 'active', now()); + + return v_tenant; +end; +$$; + + +-- +-- Name: faq_votar(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.faq_votar(faq_id uuid) RETURNS void + LANGUAGE sql SECURITY DEFINER + AS $$ + update public.saas_faq + set votos = votos + 1, + updated_at = now() + where id = faq_id + and ativo = true; +$$; + + +-- +-- Name: fix_all_subscription_mismatches(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.fix_all_subscription_mismatches() RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + r record; +begin + for r in + select distinct s.user_id as owner_id + from public.subscriptions s + where s.status = 'active' + and s.user_id is not null + loop + perform public.rebuild_owner_entitlements(r.owner_id); + end loop; +end; +$$; + + +-- +-- Name: fn_agenda_regras_semanais_no_overlap(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.fn_agenda_regras_semanais_no_overlap() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_count int; +begin + if new.ativo is false then + return new; + end if; + + select count(*) into v_count + from public.agenda_regras_semanais r + where r.owner_id = new.owner_id + and r.dia_semana = new.dia_semana + and r.ativo is true + and (tg_op = 'INSERT' or r.id <> new.id) + and (new.hora_inicio < r.hora_fim and new.hora_fim > r.hora_inicio); + + if v_count > 0 then + raise exception 'Janela sobreposta: j?? existe uma regra ativa nesse intervalo.'; + end if; + + return new; +end; +$$; + + +-- +-- Name: get_financial_report(uuid, date, date, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text DEFAULT 'month'::text) RETURNS TABLE(group_key text, group_label text, total_receitas numeric, total_despesas numeric, saldo numeric, total_pendente numeric, total_overdue numeric, count_records bigint) + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + AS $$ + + -- ?????? Valida p_group_by antes de executar ?????????????????????????????????????????????????????????????????????????????????????????????????????? + -- (lan??a erro se valor inv??lido; plpgsql seria necess??rio para isso em SQL puro, + -- ent??o usamos um CTE de valida????o com CASE WHEN para retornar vazio em vez de erro) + + WITH base AS ( + SELECT + fr.type, + fr.amount, + fr.final_amount, + fr.status, + fr.deleted_at, + -- Chave de agrupamento calculada conforme p_group_by + CASE p_group_by + WHEN 'month' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'YYYY-MM' + ) + WHEN 'week' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'IYYY-"W"IW' + ) + WHEN 'category' THEN COALESCE(fr.category_id::TEXT, fr.category, 'sem_categoria') + WHEN 'patient' THEN COALESCE(fr.patient_id::TEXT, 'sem_paciente') + ELSE NULL -- group_by inv??lido ??? group_key NULL ??? retorno vazio + END AS gkey, + -- Label leg??vel (enriquecido via JOIN abaixo quando poss??vel) + CASE p_group_by + WHEN 'month' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'YYYY-MM' + ) + WHEN 'week' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'IYYY-"W"IW' + ) + WHEN 'category' THEN COALESCE(fc.name, fr.category, 'Sem categoria') + WHEN 'patient' THEN COALESCE(p.nome_completo, fr.patient_id::TEXT, 'Sem paciente') + ELSE NULL + END AS glabel + FROM public.financial_records fr + LEFT JOIN public.financial_categories fc + ON fc.id = fr.category_id + LEFT JOIN public.patients p + ON p.id = fr.patient_id + WHERE fr.owner_id = p_owner_id + AND fr.deleted_at IS NULL + AND COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE) + BETWEEN p_start_date AND p_end_date + ) + + SELECT + gkey AS group_key, + glabel AS group_label, + + COALESCE(SUM(final_amount) FILTER (WHERE type = 'receita' AND status = 'paid'), 0) + AS total_receitas, + + COALESCE(SUM(final_amount) FILTER (WHERE type = 'despesa' AND status = 'paid'), 0) + AS total_despesas, + + COALESCE(SUM(final_amount) FILTER (WHERE type = 'receita' AND status = 'paid'), 0) + - COALESCE(SUM(final_amount) FILTER (WHERE type = 'despesa' AND status = 'paid'), 0) + AS saldo, + + COALESCE(SUM(final_amount) FILTER (WHERE status = 'pending'), 0) AS total_pendente, + + COALESCE(SUM(final_amount) FILTER (WHERE status = 'overdue'), 0) AS total_overdue, + + COUNT(*) AS count_records + + FROM base + WHERE gkey IS NOT NULL -- descarta p_group_by inv??lido + GROUP BY gkey, glabel + ORDER BY gkey ASC; + +$$; + + +-- +-- Name: FUNCTION get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text) IS 'Relat??rio financeiro agrupado por m??s, semana ISO, categoria ou paciente. p_group_by aceita: ''month'' | ''week'' | ''category'' | ''patient''. Totais de receita/despesa consideram apenas registros com status=paid. total_pendente e total_overdue incluem todos os tipos (receita + despesa).'; + + +-- +-- Name: get_financial_summary(uuid, integer, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_month integer) RETURNS TABLE(total_receitas numeric, total_despesas numeric, total_pendente numeric, saldo_liquido numeric, total_repasse numeric, count_receitas bigint, count_despesas bigint) + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + AS $$ + SELECT + -- Receitas pagas no per??odo + COALESCE(SUM(amount) FILTER ( + WHERE type = 'receita' AND status = 'paid' + ), 0) AS total_receitas, + + -- Despesas pagas no per??odo + COALESCE(SUM(amount) FILTER ( + WHERE type = 'despesa' AND status = 'paid' + ), 0) AS total_despesas, + + -- Tudo pendente ou vencido (receitas + despesas) + COALESCE(SUM(amount) FILTER ( + WHERE status IN ('pending', 'overdue') + ), 0) AS total_pendente, + + -- Saldo l??quido (receitas pagas ??? despesas pagas) + COALESCE(SUM(amount) FILTER ( + WHERE type = 'receita' AND status = 'paid' + ), 0) + - COALESCE(SUM(amount) FILTER ( + WHERE type = 'despesa' AND status = 'paid' + ), 0) AS saldo_liquido, + + -- Total repassado ?? cl??nica (apenas receitas pagas) + COALESCE(SUM(clinic_fee_amount) FILTER ( + WHERE type = 'receita' AND status = 'paid' + ), 0) AS total_repasse, + + -- Contadores (excluindo soft-deleted) + COUNT(*) FILTER (WHERE type = 'receita' AND deleted_at IS NULL) AS count_receitas, + COUNT(*) FILTER (WHERE type = 'despesa' AND deleted_at IS NULL) AS count_despesas + + FROM public.financial_records + WHERE owner_id = p_owner_id + AND deleted_at IS NULL + AND EXTRACT(YEAR FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_year + AND EXTRACT(MONTH FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_month; +$$; + + +-- +-- Name: get_my_email(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.get_my_email() RETURNS text + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ + select lower(email) + from auth.users + where id = auth.uid(); +$$; + + +-- +-- Name: guard_account_type_immutable(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_account_type_immutable() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF OLD.account_type <> 'free' AND NEW.account_type IS DISTINCT FROM OLD.account_type THEN + RAISE EXCEPTION 'account_type ?? imut??vel ap??s escolha (atual: "%" para tentativa: "%"). Para mudar de perfil, crie uma nova conta.', OLD.account_type, NEW.account_type + USING ERRCODE = 'P0001'; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: guard_locked_commitment(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_locked_commitment() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if (old.is_locked = true) then + if (tg_op = 'DELETE') then + raise exception 'Compromisso bloqueado n??o pode ser exclu??do.'; + end if; + + if (tg_op = 'UPDATE') then + if (new.active = false) then + raise exception 'Compromisso bloqueado n??o pode ser desativado.'; + end if; + + -- trava renomear (mant??m o "Sess??o" sempre igual) + if (new.name is distinct from old.name) then + raise exception 'Compromisso bloqueado n??o pode ser renomeado.'; + end if; + + -- se quiser travar descri????o tamb??m, descomente: + -- if (new.description is distinct from old.description) then + -- raise exception 'Compromisso bloqueado n??o pode alterar descri????o.'; + -- end if; + end if; + end if; + + return new; +end; +$$; + + +-- +-- Name: guard_no_change_core_plan_key(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_no_change_core_plan_key() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') + and new.key is distinct from old.key then + raise exception 'N??o ?? permitido alterar a key do plano padr??o (%).', old.key + using errcode = 'P0001'; + end if; + + return new; +end $$; + + +-- +-- Name: guard_no_change_plan_target(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_no_change_plan_target() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_bypass text; +begin + -- bypass controlado por sess??o/transa????o: + -- s?? passa se app.plan_migration_bypass = '1' + v_bypass := current_setting('app.plan_migration_bypass', true); + + if v_bypass = '1' then + return new; + end if; + + -- comportamento original (bloqueia qualquer mudan??a) + if new.target is distinct from old.target then + raise exception 'N??o ?? permitido alterar target do plano (%) de % para %.', + old.key, old.target, new.target + using errcode = 'P0001'; + end if; + + return new; +end +$$; + + +-- +-- Name: guard_no_delete_core_plans(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_no_delete_core_plans() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') then + raise exception 'Plano padr??o (%) n??o pode ser removido.', old.key + using errcode = 'P0001'; + end if; + + return old; +end $$; + + +-- +-- Name: guard_patient_cannot_own_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_patient_cannot_own_tenant() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_account_type text; +BEGIN + SELECT account_type INTO v_account_type + FROM public.profiles + WHERE id = NEW.user_id; + + IF v_account_type = 'patient' AND NEW.role IN ('tenant_admin', 'therapist') THEN + RAISE EXCEPTION 'Usu??rio com perfil "patient" n??o pode ser propriet??rio ou terapeuta de um tenant. Se tornou profissional? Crie uma nova conta.' + USING ERRCODE = 'P0001'; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: guard_tenant_kind_immutable(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_tenant_kind_immutable() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF NEW.kind IS DISTINCT FROM OLD.kind THEN + RAISE EXCEPTION 'tenants.kind ?? imut??vel ap??s cria????o. Tentativa de alterar "%" para "%".', OLD.kind, NEW.kind + USING ERRCODE = 'P0001'; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: handle_new_user(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.handle_new_user() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + INSERT INTO public.profiles (id, role, account_type) + VALUES (NEW.id, 'portal_user', 'free') + ON CONFLICT (id) DO NOTHING; + RETURN NEW; +END; +$$; + + +-- +-- Name: handle_new_user_create_personal_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.handle_new_user_create_personal_tenant() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + -- Desabilitado. Tenant criado no onboarding via provision_account_tenant(). + RETURN NEW; +END; +$$; + + +-- +-- Name: has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.owner_feature_entitlements e + where e.owner_id = p_owner_id + and e.feature_key = p_feature_key + ); +$$; + + +-- +-- Name: is_clinic_tenant(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_clinic_tenant(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + SELECT EXISTS ( + SELECT 1 FROM public.tenants t + WHERE t.id = _tenant_id + AND t.kind IN ('clinic', 'clinic_coworking', 'clinic_reception', 'clinic_full') + ); +$$; + + +-- +-- Name: is_saas_admin(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_saas_admin() RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 from public.saas_admins sa + where sa.user_id = auth.uid() + ); +$$; + + +-- +-- Name: is_tenant_admin(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_tenant_admin(p_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ + select exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.role = 'tenant_admin' + and tm.status = 'active' + ); +$$; + + +-- +-- Name: is_tenant_member(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_tenant_member(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.tenant_members m + where m.tenant_id = _tenant_id + and m.user_id = auth.uid() + and m.status = 'active' + ); +$$; + + +-- +-- Name: is_therapist_tenant(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_therapist_tenant(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + SELECT EXISTS ( + SELECT 1 FROM public.tenants t + WHERE t.id = _tenant_id AND t.kind = 'therapist' + ); +$$; + + +-- +-- Name: jwt_email(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.jwt_email() RETURNS text + LANGUAGE sql STABLE + AS $$ + select nullif(lower(current_setting('request.jwt.claim.email', true)), ''); +$$; + + +-- +-- Name: list_financial_records(uuid, integer, integer, text, text, uuid, integer, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.list_financial_records(p_owner_id uuid, p_year integer DEFAULT NULL::integer, p_month integer DEFAULT NULL::integer, p_type text DEFAULT NULL::text, p_status text DEFAULT NULL::text, p_patient_id uuid DEFAULT NULL::uuid, p_limit integer DEFAULT 50, p_offset integer DEFAULT 0) RETURNS SETOF public.financial_records + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + AS $$ + SELECT * + FROM public.financial_records + WHERE owner_id = p_owner_id + AND deleted_at IS NULL + AND (p_type IS NULL OR type::TEXT = p_type) + AND (p_status IS NULL OR status = p_status) + AND (p_patient_id IS NULL OR patient_id = p_patient_id) + AND (p_year IS NULL OR EXTRACT(YEAR FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_year) + AND (p_month IS NULL OR EXTRACT(MONTH FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_month) + ORDER BY COALESCE(paid_at, due_date::TIMESTAMPTZ, created_at) DESC + LIMIT p_limit + OFFSET p_offset; +$$; + + +-- +-- Name: mark_as_paid(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method text) RETURNS SETOF public.financial_records + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_record public.financial_records%ROWTYPE; +BEGIN + -- Garante que o registro pertence ao usu??rio autenticado (RLS n??o aplica em SECURITY DEFINER) + SELECT * INTO v_record + FROM public.financial_records + WHERE id = p_financial_record_id + AND owner_id = auth.uid() + AND deleted_at IS NULL; + + IF NOT FOUND THEN + RAISE EXCEPTION 'Registro financeiro n??o encontrado ou sem permiss??o.'; + END IF; + + IF v_record.status NOT IN ('pending', 'overdue') THEN + RAISE EXCEPTION 'Apenas cobran??as pendentes ou vencidas podem ser marcadas como pagas.'; + END IF; + + UPDATE public.financial_records + SET status = 'paid', + paid_at = NOW(), + payment_method = p_payment_method, + updated_at = NOW() + WHERE id = p_financial_record_id + RETURNING * INTO v_record; + + RETURN NEXT v_record; +END; +$$; + + +-- +-- Name: mark_payout_as_paid(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.mark_payout_as_paid(p_payout_id uuid) RETURNS public.therapist_payouts + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_payout public.therapist_payouts%ROWTYPE; +BEGIN + -- Busca o payout + SELECT * INTO v_payout + FROM public.therapist_payouts + WHERE id = p_payout_id; + + IF NOT FOUND THEN + RAISE EXCEPTION 'Repasse n??o encontrado: %', p_payout_id; + END IF; + + -- Verifica permiss??o: apenas tenant_admin do tenant do repasse + IF NOT public.is_tenant_admin(v_payout.tenant_id) THEN + RAISE EXCEPTION 'Apenas o administrador da cl??nica pode marcar repasses como pagos.'; + END IF; + + -- Verifica status + IF v_payout.status <> 'pending' THEN + RAISE EXCEPTION + 'Repasse j?? est?? com status ''%''. Apenas repasses pendentes podem ser pagos.', + v_payout.status; + END IF; + + -- Atualiza + UPDATE public.therapist_payouts + SET + status = 'paid', + paid_at = NOW(), + updated_at = NOW() + WHERE id = p_payout_id + RETURNING * INTO v_payout; + + RETURN v_payout; +END; +$$; + + +-- +-- Name: FUNCTION mark_payout_as_paid(p_payout_id uuid); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.mark_payout_as_paid(p_payout_id uuid) IS 'Marca um repasse de terapeuta como pago. Apenas o tenant_admin pode chamar. Apenas repasses com status=pending podem ser finalizados.'; + + +-- +-- Name: my_tenants(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.my_tenants() RETURNS TABLE(tenant_id uuid, role text, status text, kind text) + LANGUAGE sql STABLE + AS $$ + select + tm.tenant_id, + tm.role, + tm.status, + t.kind + from public.tenant_members tm + join public.tenants t on t.id = tm.tenant_id + where tm.user_id = auth.uid(); +$$; + + +-- +-- Name: notice_track_click(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notice_track_click(p_notice_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +begin + update public.global_notices + set clicks_count = clicks_count + 1 + where id = p_notice_id; +end; +$$; + + +-- +-- Name: notice_track_view(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notice_track_view(p_notice_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +begin + update public.global_notices + set views_count = views_count + 1 + where id = p_notice_id; +end; +$$; + + +-- +-- Name: notify_on_intake(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notify_on_intake() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF NEW.status = 'new' THEN + INSERT INTO public.notifications ( + owner_id, + tenant_id, + type, + ref_id, + ref_table, + payload + ) + VALUES ( + NEW.owner_id, + NEW.tenant_id, + 'new_patient', + NEW.id, + 'patient_intake_requests', + jsonb_build_object( + 'title', 'Novo cadastro externo', + 'detail', COALESCE(NEW.nome_completo, 'Paciente'), + 'deeplink', '/therapist/patients/cadastro/recebidos', + 'avatar_initials', upper(left(COALESCE(NEW.nome_completo, '?'), 2)) + ) + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: notify_on_scheduling(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notify_on_scheduling() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ BEGIN IF NEW.status = 'pendente' THEN + INSERT INTO public.notifications ( owner_id, tenant_id, type, ref_id, ref_table, payload ) VALUES ( + NEW.owner_id, NEW.tenant_id, + 'new_scheduling', NEW.id, 'agendador_solicitacoes', jsonb_build_object( 'title', 'Nova solicita????o de agendamento', 'detail', COALESCE(NEW.paciente_nome, 'Paciente') || ' ' || COALESCE(NEW.paciente_sobrenome, '') || ' ??? ' || COALESCE(NEW.tipo, ''), 'deeplink', '/therapist/agendamentos-recebidos', 'avatar_initials', upper(left(COALESCE(NEW.paciente_nome, '?'), 1) || left(COALESCE(NEW.paciente_sobrenome, ''), 1)) ) ); END IF; RETURN NEW; END; $$; + + +-- +-- Name: notify_on_session_status(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notify_on_session_status() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_nome text; +BEGIN + IF NEW.status IN ('faltou', 'cancelado') AND OLD.status IS DISTINCT FROM NEW.status THEN + + SELECT nome_completo + INTO v_nome + FROM public.patients + WHERE id = NEW.patient_id + LIMIT 1; + + INSERT INTO public.notifications ( + owner_id, + tenant_id, + type, + ref_id, + ref_table, + payload + ) + VALUES ( + NEW.owner_id, + NEW.tenant_id, + 'session_status', + NEW.id, + 'agenda_eventos', + jsonb_build_object( + 'title', CASE WHEN NEW.status = 'faltou' THEN 'Paciente faltou' ELSE 'Sess??o cancelada' END, + 'detail', COALESCE(v_nome, 'Paciente') || ' ??? ' || to_char(NEW.inicio_em, 'DD/MM HH24:MI'), + 'deeplink', '/therapist/agenda', + 'avatar_initials', upper(left(COALESCE(v_nome, '?'), 2)) + ) + ); + + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: on_new_user_seed_patient_groups(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.on_new_user_seed_patient_groups() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + BEGIN + PERFORM public.seed_default_patient_groups(NEW.id); + RETURN NEW; + END; + $$; + + +-- +-- Name: patients_validate_member_consistency(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.patients_validate_member_consistency() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_tenant_responsible uuid; + v_tenant_therapist uuid; +BEGIN + -- responsible_member sempre deve existir e ser do tenant + SELECT tenant_id INTO v_tenant_responsible + FROM public.tenant_members + WHERE id = NEW.responsible_member_id; + + IF v_tenant_responsible IS NULL THEN + RAISE EXCEPTION 'Responsible member not found'; + END IF; + + IF NEW.tenant_id IS NULL THEN + RAISE EXCEPTION 'tenant_id is required'; + END IF; + + IF v_tenant_responsible <> NEW.tenant_id THEN + RAISE EXCEPTION 'Responsible member must belong to the same tenant'; + END IF; + + -- therapist scope: therapist_member_id deve existir e ser do mesmo tenant + IF NEW.patient_scope = 'therapist' THEN + IF NEW.therapist_member_id IS NULL THEN + RAISE EXCEPTION 'therapist_member_id is required when patient_scope=therapist'; + END IF; + + SELECT tenant_id INTO v_tenant_therapist + FROM public.tenant_members + WHERE id = NEW.therapist_member_id; + + IF v_tenant_therapist IS NULL THEN + RAISE EXCEPTION 'Therapist member not found'; + END IF; + + IF v_tenant_therapist <> NEW.tenant_id THEN + RAISE EXCEPTION 'Therapist member must belong to the same tenant'; + END IF; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: patients_validate_responsible_member_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.patients_validate_responsible_member_tenant() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + m_tenant uuid; +begin + select tenant_id into m_tenant + from public.tenant_members + where id = new.responsible_member_id; + + if m_tenant is null then + raise exception 'Responsible member not found'; + end if; + + if new.tenant_id is null then + raise exception 'tenant_id is required'; + end if; + + if m_tenant <> new.tenant_id then + raise exception 'Responsible member must belong to the same tenant'; + end if; + + return new; +end; +$$; + + +-- +-- Name: populate_notification_queue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.populate_notification_queue() RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + INSERT INTO public.notification_queue ( + tenant_id, owner_id, agenda_evento_id, patient_id, + channel, template_key, schedule_key, + resolved_vars, recipient_address, + scheduled_at, idempotency_key + ) + SELECT + ae.tenant_id, + ae.owner_id, + ae.id AS agenda_evento_id, + ae.patient_id, + ch.channel, + 'session.' || REPLACE(ns.event_type, '_sessao', '') || '.' || ch.channel, + ns.schedule_key, + jsonb_build_object( + 'nome_paciente', COALESCE(p.nome_completo, 'Paciente'), + 'data_sessao', TO_CHAR(ae.inicio_em AT TIME ZONE 'America/Sao_Paulo', 'DD/MM/YYYY'), + 'hora_sessao', TO_CHAR(ae.inicio_em AT TIME ZONE 'America/Sao_Paulo', 'HH24:MI'), + 'nome_terapeuta', COALESCE(prof.full_name, 'Terapeuta'), + 'modalidade', COALESCE(ae.modalidade, 'Presencial'), + 'titulo', COALESCE(ae.titulo, 'Sess??o') + ), + CASE ch.channel + WHEN 'whatsapp' THEN COALESCE(p.telefone, '') + WHEN 'sms' THEN COALESCE(p.telefone, '') + WHEN 'email' THEN COALESCE(p.email_principal, '') + END, + CASE + WHEN (ae.inicio_em - (ns.offset_minutes || ' minutes')::interval)::time + < ns.allowed_time_start + THEN DATE_TRUNC('day', ae.inicio_em - (ns.offset_minutes || ' minutes')::interval) + + ns.allowed_time_start + WHEN (ae.inicio_em - (ns.offset_minutes || ' minutes')::interval)::time + > ns.allowed_time_end + THEN DATE_TRUNC('day', ae.inicio_em - (ns.offset_minutes || ' minutes')::interval) + + ns.allowed_time_start + ELSE ae.inicio_em - (ns.offset_minutes || ' minutes')::interval + END, + ae.id::text || ':' || ns.schedule_key || ':' || ch.channel || ':' + || ae.inicio_em::date::text + FROM public.agenda_eventos ae + JOIN public.patients p ON p.id = ae.patient_id + LEFT JOIN public.profiles prof ON prof.id = ae.owner_id + JOIN public.notification_schedules ns + ON ns.owner_id = ae.owner_id + AND ns.is_active = true + AND ns.deleted_at IS NULL + AND ns.trigger_type = 'before_event' + AND ns.event_type = 'lembrete_sessao' + JOIN public.notification_channels nc + ON nc.owner_id = ae.owner_id + AND nc.is_active = true + AND nc.deleted_at IS NULL + CROSS JOIN LATERAL ( + SELECT 'whatsapp' AS channel WHERE ns.whatsapp_enabled AND nc.channel = 'whatsapp' + UNION ALL + SELECT 'email' AS channel WHERE ns.email_enabled AND nc.channel = 'email' + UNION ALL + SELECT 'sms' AS channel WHERE ns.sms_enabled AND nc.channel = 'sms' + ) ch + LEFT JOIN public.notification_preferences np + ON np.patient_id = ae.patient_id + AND np.owner_id = ae.owner_id + AND np.deleted_at IS NULL + WHERE + ae.inicio_em > now() + AND ae.inicio_em <= now() + interval '48 hours' + AND ae.status NOT IN ('cancelado', 'faltou') + AND CASE ch.channel + WHEN 'whatsapp' THEN COALESCE(p.telefone, '') != '' + WHEN 'sms' THEN COALESCE(p.telefone, '') != '' + WHEN 'email' THEN COALESCE(p.email_principal, '') != '' + END + AND CASE ch.channel + WHEN 'whatsapp' THEN COALESCE(np.whatsapp_opt_in, true) + WHEN 'email' THEN COALESCE(np.email_opt_in, true) + WHEN 'sms' THEN COALESCE(np.sms_opt_in, false) + END + AND EXISTS ( + SELECT 1 FROM public.profiles tp + WHERE tp.id = ae.owner_id + AND COALESCE(tp.notify_reminders, true) = true + ) + ON CONFLICT (idempotency_key) DO NOTHING; +END; +$$; + + +-- +-- Name: prevent_promoting_to_system(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.prevent_promoting_to_system() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if new.is_system = true and old.is_system is distinct from true then + raise exception 'N??o ?? permitido transformar um grupo comum em grupo do sistema.'; + end if; + return new; +end; +$$; + + +-- +-- Name: prevent_saas_membership(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.prevent_saas_membership() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM public.profiles + WHERE id = NEW.user_id + AND role = 'saas_admin' + ) THEN + RAISE EXCEPTION 'SaaS admin cannot belong to tenant'; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: prevent_system_group_changes(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.prevent_system_group_changes() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + -- Se for grupo do sistema, regras r??gidas: + if old.is_system = true then + + -- nunca pode deletar + if tg_op = 'DELETE' then + raise exception 'Grupos padr??o do sistema n??o podem ser alterados ou exclu??dos.'; + end if; + + if tg_op = 'UPDATE' then + -- permite SOMENTE mudar tenant_id e/ou updated_at + -- qualquer mudan??a de conte??do permanece proibida + if + new.nome is distinct from old.nome or + new.descricao is distinct from old.descricao or + new.cor is distinct from old.cor or + new.is_active is distinct from old.is_active or + new.is_system is distinct from old.is_system or + new.owner_id is distinct from old.owner_id or + new.therapist_id is distinct from old.therapist_id or + new.created_at is distinct from old.created_at + then + raise exception 'Grupos padr??o do sistema n??o podem ser alterados ou exclu??dos.'; + end if; + + -- chegou aqui: s?? tenant_id/updated_at mudaram -> ok + return new; + end if; + + end if; + + -- n??o-system: deixa passar + if tg_op = 'DELETE' then + return old; + end if; + + return new; +end; +$$; + + +-- +-- Name: provision_account_tenant(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text DEFAULT NULL::text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_tenant_id uuid; + v_account_type text; + v_name text; +BEGIN + IF p_kind NOT IN ('therapist', 'clinic_coworking', 'clinic_reception', 'clinic_full') THEN + RAISE EXCEPTION 'kind inv??lido: "%". Use: therapist, clinic_coworking, clinic_reception, clinic_full.', p_kind + USING ERRCODE = 'P0001'; + END IF; + + v_account_type := CASE WHEN p_kind = 'therapist' THEN 'therapist' ELSE 'clinic' END; + + IF EXISTS ( + SELECT 1 + FROM public.tenant_members tm + JOIN public.tenants t ON t.id = tm.tenant_id + WHERE tm.user_id = p_user_id + AND tm.role = 'tenant_admin' + AND tm.status = 'active' + AND t.kind = p_kind + ) THEN + RAISE EXCEPTION 'Usu??rio j?? possui um tenant do tipo "%".', p_kind + USING ERRCODE = 'P0001'; + END IF; + + v_name := COALESCE( + NULLIF(TRIM(p_name), ''), + ( + SELECT COALESCE(NULLIF(TRIM(pr.full_name), ''), SPLIT_PART(au.email, '@', 1)) + FROM public.profiles pr + JOIN auth.users au ON au.id = pr.id + WHERE pr.id = p_user_id + ), + 'Conta' + ); + + INSERT INTO public.tenants (name, kind, created_at) + VALUES (v_name, p_kind, now()) + RETURNING id INTO v_tenant_id; + + INSERT INTO public.tenant_members (tenant_id, user_id, role, status, created_at) + VALUES (v_tenant_id, p_user_id, 'tenant_admin', 'active', now()); + + UPDATE public.profiles + SET account_type = v_account_type + WHERE id = p_user_id; + + PERFORM public.seed_determined_commitments(v_tenant_id); + + RETURN v_tenant_id; +END; +$$; + + +-- +-- Name: FUNCTION provision_account_tenant(p_user_id uuid, p_kind text, p_name text); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) IS 'Cria o tenant do tipo correto e atualiza account_type no profile. Chamar no onboarding ap??s escolha/pagamento de plano therapist ou clinic. p_kind: therapist | clinic_coworking | clinic_reception | clinic_full'; + + +-- +-- Name: reactivate_subscription(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.reactivate_subscription(p_subscription_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_owner_type text; + v_owner_ref uuid; +begin + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + if v_sub.status = 'active' then + return v_sub; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set status = 'active', + cancel_at_period_end = false, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'reactivated', + v_sub.plan_id, + v_sub.plan_id, + auth.uid(), + 'Reativa????o manual via admin', + 'admin_panel', + jsonb_build_object('previous_status', 'canceled') + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation(owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; + +end; +$$; + + +-- +-- Name: rebuild_owner_entitlements(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_plan_id uuid; +begin + -- Plano ativo do owner (owner = subscriptions.user_id) + select s.plan_id + into v_plan_id + from public.subscriptions s + where s.user_id = p_owner_id + and s.status = 'active' + order by s.created_at desc + limit 1; + + -- Sempre zera entitlements do owner (rebuild) + delete from public.owner_feature_entitlements e + where e.owner_id = p_owner_id; + + -- Se n??o tem assinatura ativa, acabou + if v_plan_id is null then + return; + end if; + + -- Recria entitlements esperados pelo plano + insert into public.owner_feature_entitlements (owner_id, feature_key, sources, limits_list) + select + p_owner_id as owner_id, + f.key as feature_key, + array['plan'::text] as sources, + '{}'::jsonb as limits_list + from public.plan_features pf + join public.features f on f.id = pf.feature_id + where pf.plan_id = v_plan_id; + +end; +$$; + + +-- +-- Name: revoke_support_session(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.revoke_support_session(p_token text) RETURNS boolean + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_admin_id uuid; + v_role text; +BEGIN + v_admin_id := auth.uid(); + IF v_admin_id IS NULL THEN + RAISE EXCEPTION 'N??o autenticado.' USING ERRCODE = 'P0001'; + END IF; + + SELECT role INTO v_role FROM public.profiles WHERE id = v_admin_id; + IF v_role <> 'saas_admin' THEN + RAISE EXCEPTION 'Acesso negado.' USING ERRCODE = 'P0002'; + END IF; + + DELETE FROM public.support_sessions + WHERE token = p_token + AND admin_id = v_admin_id; + + RETURN FOUND; +END; +$$; + + +-- +-- Name: rotate_patient_invite_token(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.rotate_patient_invite_token(p_new_token text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_uid uuid; + v_id uuid; +begin + -- pega o usu??rio logado + v_uid := auth.uid(); + if v_uid is null then + raise exception 'Usu??rio n??o autenticado'; + end if; + + -- desativa tokens antigos ativos do usu??rio + update public.patient_invites + set active = false + where owner_id = v_uid + and active = true; + + -- cria novo token + insert into public.patient_invites (owner_id, token, active) + values (v_uid, p_new_token, true) + returning id into v_id; + + return v_id; +end; +$$; + + +-- +-- Name: saas_votar_doc(uuid, boolean); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_uid uuid := auth.uid(); + v_voto_antigo boolean; +begin + if v_uid is null then + raise exception 'N??o autenticado'; + end if; + + -- Verifica se j?? votou + select util into v_voto_antigo + from public.saas_doc_votos + where doc_id = p_doc_id and user_id = v_uid; + + if found then + -- J?? votou igual ??? cancela o voto (toggle) + if v_voto_antigo = p_util then + delete from public.saas_doc_votos + where doc_id = p_doc_id and user_id = v_uid; + + update public.saas_docs set + votos_util = greatest(0, votos_util - (case when p_util then 1 else 0 end)), + votos_nao_util = greatest(0, votos_nao_util - (case when not p_util then 1 else 0 end)), + updated_at = now() + where id = p_doc_id; + + return jsonb_build_object('acao', 'removido', 'util', null); + else + -- Mudou de voto + update public.saas_doc_votos set util = p_util, updated_at = now() + where doc_id = p_doc_id and user_id = v_uid; + + update public.saas_docs set + votos_util = greatest(0, votos_util + (case when p_util then 1 else -1 end)), + votos_nao_util = greatest(0, votos_nao_util + (case when not p_util then 1 else -1 end)), + updated_at = now() + where id = p_doc_id; + + return jsonb_build_object('acao', 'atualizado', 'util', p_util); + end if; + else + -- Primeiro voto + insert into public.saas_doc_votos (doc_id, user_id, util) + values (p_doc_id, v_uid, p_util); + + update public.saas_docs set + votos_util = votos_util + (case when p_util then 1 else 0 end), + votos_nao_util = votos_nao_util + (case when not p_util then 1 else 0 end), + updated_at = now() + where id = p_doc_id; + + return jsonb_build_object('acao', 'registrado', 'util', p_util); + end if; +end; +$$; + + +-- +-- Name: safe_delete_patient(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.safe_delete_patient(p_patient_id uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + -- Bloqueia se houver hist??rico + IF NOT public.can_delete_patient(p_patient_id) THEN + RETURN jsonb_build_object( + 'ok', false, + 'error', 'has_history', + 'message', 'Este paciente possui hist??rico cl??nico ou financeiro e n??o pode ser removido. Voc?? pode desativar ou arquivar o paciente.' + ); + END IF; + + -- Verifica ownership via RLS (owner_id ou responsible_member_id) + IF NOT EXISTS ( + SELECT 1 FROM public.patients + WHERE id = p_patient_id + AND ( + owner_id = auth.uid() + OR responsible_member_id IN ( + SELECT id FROM public.tenant_members WHERE user_id = auth.uid() + ) + ) + ) THEN + RETURN jsonb_build_object( + 'ok', false, + 'error', 'forbidden', + 'message', 'Sem permiss??o para excluir este paciente.' + ); + END IF; + + DELETE FROM public.patients WHERE id = p_patient_id; + + RETURN jsonb_build_object('ok', true); +END; +$$; + + +-- +-- Name: sanitize_phone_br(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.sanitize_phone_br(raw_phone text) RETURNS text + LANGUAGE plpgsql IMMUTABLE + AS $$ DECLARE digits text; + BEGIN + digits := regexp_replace(COALESCE(raw_phone, ''), '[^0-9]', '', 'g'); + IF digits = '' THEN RETURN ''; END IF; + IF length(digits) = 10 OR length(digits) = 11 THEN + digits := '55' || digits; + END IF; + RETURN digits; + END; $$; + + +-- +-- Name: seed_default_financial_categories(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.seed_default_financial_categories(p_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + INSERT INTO public.financial_categories (user_id, name, type, color, icon, sort_order) + VALUES + (p_user_id, 'Sess??o', 'receita', '#22c55e', 'pi pi-heart', 1), + (p_user_id, 'Supervis??o', 'receita', '#6366f1', 'pi pi-users', 2), + (p_user_id, 'Conv??nio', 'receita', '#3b82f6', 'pi pi-building', 3), + (p_user_id, 'Grupo terap??utico', 'receita', '#f59e0b', 'pi pi-sitemap', 4), + (p_user_id, 'Outro (receita)', 'receita', '#8b5cf6', 'pi pi-plus-circle', 5), + (p_user_id, 'Aluguel sala', 'despesa', '#ef4444', 'pi pi-home', 1), + (p_user_id, 'Plataforma/SaaS', 'despesa', '#f97316', 'pi pi-desktop', 2), + (p_user_id, 'Repasse cl??nica', 'despesa', '#64748b', 'pi pi-arrow-right-arrow-left', 3), + (p_user_id, 'Supervis??o (custo)', 'despesa', '#6366f1', 'pi pi-users', 4), + (p_user_id, 'Outro (despesa)', 'despesa', '#94a3b8', 'pi pi-minus-circle', 5) + ON CONFLICT DO NOTHING; +END; +$$; + + +-- +-- Name: seed_default_patient_groups(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.seed_default_patient_groups(p_tenant_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_owner_id uuid; +BEGIN + -- busca o owner (tenant_admin) do tenant + SELECT user_id INTO v_owner_id + FROM public.tenant_members + WHERE tenant_id = p_tenant_id + AND role = 'tenant_admin' + AND status = 'active' + LIMIT 1; + + IF v_owner_id IS NULL THEN + RETURN; + END IF; + + INSERT INTO public.patient_groups (owner_id, nome, cor, is_system, tenant_id) + VALUES + (v_owner_id, 'Crian??as', '#60a5fa', true, p_tenant_id), + (v_owner_id, 'Adolescentes', '#a78bfa', true, p_tenant_id), + (v_owner_id, 'Idosos', '#34d399', true, p_tenant_id) + ON CONFLICT (owner_id, nome) DO NOTHING; +END; +$$; + + +-- +-- Name: seed_determined_commitments(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.seed_determined_commitments(p_tenant_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_id uuid; +begin + -- Sess??o (locked + sempre ativa) + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'session' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'session', true, true, 'Sess??o', 'Sess??o com paciente'); + end if; + + -- Leitura + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'reading' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'reading', false, true, 'Leitura', 'Praticar leitura'); + end if; + + -- Supervis??o + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'supervision' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'supervision', false, true, 'Supervis??o', 'Supervis??o'); + end if; + + -- Aula ??? (corrigido) + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'class' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'class', false, false, 'Aula', 'Dar aula'); + end if; + + -- An??lise pessoal + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'analysis' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'analysis', false, true, 'An??lise Pessoal', 'Minha an??lise pessoal'); + end if; + + -- ------------------------------------------------------- + -- Campos padr??o (idempotentes por (commitment_id, key)) + -- ------------------------------------------------------- + + -- Leitura + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'reading' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'book') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'book', 'Livro', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'author') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'author', 'Autor', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; + + -- Supervis??o + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'supervision' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'supervisor') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'supervisor', 'Supervisor', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'topic') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'topic', 'Assunto', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; + + -- Aula + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'class' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'theme') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'theme', 'Tema', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'group') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'group', 'Turma', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; + + -- An??lise + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'analysis' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'analyst') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'analyst', 'Analista', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'focus') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'focus', 'Foco', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; +end; +$$; + + +-- +-- Name: set_insurance_plans_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_insurance_plans_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN NEW.updated_at = now(); RETURN NEW; END; $$; + + +-- +-- Name: set_owner_id(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_owner_id() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if new.owner_id is null then + new.owner_id := auth.uid(); + end if; + return new; +end; +$$; + + +-- +-- Name: set_services_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_services_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: set_tenant_feature_exception(uuid, text, boolean, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text DEFAULT NULL::text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +begin + -- ??? S?? owner ou admin do tenant podem alterar features + if not exists ( + select 1 from public.tenant_members + where tenant_id = p_tenant_id + and user_id = auth.uid() + and role in ('owner', 'admin') + and status = 'active' + ) then + raise exception 'Acesso negado: apenas owner/admin pode alterar features do tenant.'; + end if; + + insert into public.tenant_features (tenant_id, feature_key, enabled) + values (p_tenant_id, p_feature_key, p_enabled) + on conflict (tenant_id, feature_key) + do update set enabled = excluded.enabled; + + insert into public.tenant_feature_exceptions_log ( + tenant_id, feature_key, enabled, reason, created_by + ) values ( + p_tenant_id, p_feature_key, p_enabled, p_reason, auth.uid() + ); +end; +$$; + + +-- +-- Name: set_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: set_updated_at_recurrence(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_updated_at_recurrence() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN NEW.updated_at = now(); RETURN NEW; END; +$$; + + +-- +-- Name: split_recurrence_at(uuid, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.split_recurrence_at(p_recurrence_id uuid, p_from_date date) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_old public.recurrence_rules; + v_new_id uuid; +BEGIN + -- busca a regra original + SELECT * INTO v_old + FROM public.recurrence_rules + WHERE id = p_recurrence_id; + + IF NOT FOUND THEN + RAISE EXCEPTION 'recurrence_rule % n??o encontrada', p_recurrence_id; + END IF; + + -- encerra a regra antiga na data anterior + UPDATE public.recurrence_rules + SET + end_date = p_from_date - INTERVAL '1 day', + open_ended = false, + updated_at = now() + WHERE id = p_recurrence_id; + + -- cria nova regra a partir de p_from_date + INSERT INTO public.recurrence_rules ( + tenant_id, owner_id, therapist_id, patient_id, + determined_commitment_id, type, interval, weekdays, + start_time, end_time, timezone, duration_min, + start_date, end_date, max_occurrences, open_ended, + modalidade, titulo_custom, observacoes, extra_fields, status + ) + SELECT + tenant_id, owner_id, therapist_id, patient_id, + determined_commitment_id, type, interval, weekdays, + start_time, end_time, timezone, duration_min, + p_from_date, v_old.end_date, v_old.max_occurrences, v_old.open_ended, + modalidade, titulo_custom, observacoes, extra_fields, status + FROM public.recurrence_rules + WHERE id = p_recurrence_id + RETURNING id INTO v_new_id; + + RETURN v_new_id; +END; +$$; + + +-- +-- Name: subscription_intents_view_insert(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.subscription_intents_view_insert() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_target text; + v_plan_id uuid; +begin + select p.id, p.target into v_plan_id, v_target + from public.plans p + where p.key = new.plan_key; + + if v_plan_id is null then + raise exception 'Plano inv??lido: plan_key=%', new.plan_key; + end if; + + if lower(v_target) = 'clinic' then + if new.tenant_id is null then + raise exception 'Inten????o clinic exige tenant_id.'; + end if; + + insert into public.subscription_intents_tenant ( + id, tenant_id, created_by_user_id, email, + plan_id, plan_key, interval, amount_cents, currency, + status, source, notes, created_at, paid_at + ) values ( + coalesce(new.id, gen_random_uuid()), + new.tenant_id, new.created_by_user_id, new.email, + v_plan_id, new.plan_key, coalesce(new.interval,'month'), + new.amount_cents, coalesce(new.currency,'BRL'), + coalesce(new.status,'pending'), coalesce(new.source,'manual'), + new.notes, coalesce(new.created_at, now()), new.paid_at + ); + + new.plan_target := 'clinic'; + return new; + end if; + + -- therapist ou supervisor ??? tabela personal + if lower(v_target) in ('therapist', 'supervisor') then + insert into public.subscription_intents_personal ( + id, user_id, created_by_user_id, email, + plan_id, plan_key, interval, amount_cents, currency, + status, source, notes, created_at, paid_at + ) values ( + coalesce(new.id, gen_random_uuid()), + new.user_id, new.created_by_user_id, new.email, + v_plan_id, new.plan_key, coalesce(new.interval,'month'), + new.amount_cents, coalesce(new.currency,'BRL'), + coalesce(new.status,'pending'), coalesce(new.source,'manual'), + new.notes, coalesce(new.created_at, now()), new.paid_at + ); + + new.plan_target := lower(v_target); -- 'therapist' ou 'supervisor' + return new; + end if; + + raise exception 'Target de plano n??o suportado: %', v_target; +end; +$$; + + +-- +-- Name: subscriptions_validate_scope(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.subscriptions_validate_scope() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_target text; +BEGIN + SELECT lower(p.target) INTO v_target + FROM public.plans p + WHERE p.id = NEW.plan_id; + + IF v_target IS NULL THEN + RAISE EXCEPTION 'Plano inv??lido (target nulo).'; + END IF; + + IF v_target = 'clinic' THEN + IF NEW.tenant_id IS NULL THEN + RAISE EXCEPTION 'Assinatura clinic exige tenant_id.'; + END IF; + IF NEW.user_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura clinic n??o pode ter user_id (XOR).'; + END IF; + + ELSIF v_target IN ('therapist', 'supervisor') THEN + -- supervisor ?? pessoal como therapist + IF NEW.tenant_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura % n??o deve ter tenant_id.', v_target; + END IF; + IF NEW.user_id IS NULL THEN + RAISE EXCEPTION 'Assinatura % exige user_id.', v_target; + END IF; + + ELSIF v_target = 'patient' THEN + IF NEW.tenant_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura patient n??o deve ter tenant_id.'; + END IF; + IF NEW.user_id IS NULL THEN + RAISE EXCEPTION 'Assinatura patient exige user_id.'; + END IF; + + ELSE + RAISE EXCEPTION 'Target de plano inv??lido: %', v_target; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: sync_busy_mirror_agenda_eventos(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.sync_busy_mirror_agenda_eventos() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + clinic_tenant uuid; + is_personal boolean; + should_mirror boolean; +begin + -- Anti-recurs??o: espelho n??o espelha + if (tg_op <> 'DELETE') then + if new.mirror_of_event_id is not null then + return new; + end if; + else + if old.mirror_of_event_id is not null then + return old; + end if; + end if; + + -- Define se ?? pessoal e se deve espelhar + if (tg_op = 'DELETE') then + is_personal := (old.tenant_id = old.owner_id); + should_mirror := (old.visibility_scope in ('busy_only','private')); + else + is_personal := (new.tenant_id = new.owner_id); + should_mirror := (new.visibility_scope in ('busy_only','private')); + end if; + + -- Se n??o ?? pessoal, n??o faz nada + if not is_personal then + if (tg_op = 'DELETE') then + return old; + end if; + return new; + end if; + + -- DELETE: remove espelhos existentes + if (tg_op = 'DELETE') then + delete from public.agenda_eventos e + where e.mirror_of_event_id = old.id + and e.mirror_source = 'personal_busy_mirror'; + + return old; + end if; + + -- INSERT/UPDATE: + -- Se n??o deve espelhar, remove espelhos e sai + if not should_mirror then + delete from public.agenda_eventos e + where e.mirror_of_event_id = new.id + and e.mirror_source = 'personal_busy_mirror'; + + return new; + end if; + + -- Para cada cl??nica onde o usu??rio ?? therapist active, cria/atualiza o "Ocupado" + for clinic_tenant in + select tm.tenant_id + from public.tenant_members tm + where tm.user_id = new.owner_id + and tm.role = 'therapist' + and tm.status = 'active' + and tm.tenant_id <> new.owner_id + loop + insert into public.agenda_eventos ( + tenant_id, + owner_id, + terapeuta_id, + paciente_id, + tipo, + status, + titulo, + observacoes, + inicio_em, + fim_em, + mirror_of_event_id, + mirror_source, + visibility_scope, + created_at, + updated_at + ) values ( + clinic_tenant, + new.owner_id, + new.owner_id, + null, + 'bloqueio'::public.tipo_evento_agenda, + 'agendado'::public.status_evento_agenda, + 'Ocupado', + null, + new.inicio_em, + new.fim_em, + new.id, + 'personal_busy_mirror', + 'public', + now(), + now() + ) + on conflict (tenant_id, mirror_of_event_id) where mirror_of_event_id is not null + do update set + owner_id = excluded.owner_id, + terapeuta_id = excluded.terapeuta_id, + tipo = excluded.tipo, + status = excluded.status, + titulo = excluded.titulo, + observacoes = excluded.observacoes, + inicio_em = excluded.inicio_em, + fim_em = excluded.fim_em, + updated_at = now(); + end loop; + + -- Limpa espelhos de cl??nicas onde o v??nculo therapist active n??o existe mais + delete from public.agenda_eventos e + where e.mirror_of_event_id = new.id + and e.mirror_source = 'personal_busy_mirror' + and not exists ( + select 1 + from public.tenant_members tm + where tm.user_id = new.owner_id + and tm.role = 'therapist' + and tm.status = 'active' + and tm.tenant_id = e.tenant_id + ); + + return new; +end; +$$; + + +-- +-- Name: sync_overdue_financial_records(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.sync_overdue_financial_records() RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_count integer; +BEGIN + UPDATE public.financial_records + SET + status = 'overdue', + updated_at = NOW() + WHERE status = 'pending' + AND due_date IS NOT NULL + AND due_date < CURRENT_DATE + AND deleted_at IS NULL; + + GET DIAGNOSTICS v_count = ROW_COUNT; + RETURN v_count; +END; +$$; + + +-- +-- Name: FUNCTION sync_overdue_financial_records(); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.sync_overdue_financial_records() IS 'Marca como overdue todos os financial_records pendentes com due_date vencido. Pode ser chamada manualmente, via pg_cron ou via Supabase Edge Function agendada.'; + + +-- +-- Name: tenant_accept_invite(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_accept_invite(p_token uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_uid uuid; + v_email text; + v_invite public.tenant_invites%rowtype; +begin + -- 1) precisa estar autenticado + v_uid := auth.uid(); + if v_uid is null then + raise exception 'not_authenticated' using errcode = 'P0001'; + end if; + + -- 2) pega email real do usu??rio logado sem depender do JWT claim + select u.email + into v_email + from auth.users u + where u.id = v_uid; + + if v_email is null or length(trim(v_email)) = 0 then + raise exception 'missing_user_email' using errcode = 'P0001'; + end if; + + -- 3) carrega o invite e trava linha (evita 2 aceites concorrentes) + select * + into v_invite + from public.tenant_invites i + where i.token = p_token + for update; + + if not found then + raise exception 'invite_not_found' using errcode = 'P0001'; + end if; + + -- 4) valida????es de estado + if v_invite.revoked_at is not null then + raise exception 'invite_revoked' using errcode = 'P0001'; + end if; + + if v_invite.accepted_at is not null then + raise exception 'invite_already_accepted' using errcode = 'P0001'; + end if; + + if v_invite.expires_at is not null and v_invite.expires_at <= now() then + raise exception 'invite_expired' using errcode = 'P0001'; + end if; + + -- 5) valida email (case-insensitive) + if lower(trim(v_invite.email)) <> lower(trim(v_email)) then + raise exception 'email_mismatch' using errcode = 'P0001'; + end if; + + -- 6) consome o invite + update public.tenant_invites + set accepted_at = now(), + accepted_by = v_uid + where id = v_invite.id; + + -- 7) cria ou reativa o membership + insert into public.tenant_members (tenant_id, user_id, role, status, created_at) + values (v_invite.tenant_id, v_uid, v_invite.role, 'active', now()) + on conflict (tenant_id, user_id) + do update set + role = excluded.role, + status = 'active'; + + -- 8) retorno ??til pro front (voc?? j?? tenta ler tenant_id no AcceptInvitePage) + return jsonb_build_object( + 'ok', true, + 'tenant_id', v_invite.tenant_id, + 'role', v_invite.role + ); +end; +$$; + + +-- +-- Name: tenant_members; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_members ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + user_id uuid NOT NULL, + role text NOT NULL, + status text DEFAULT 'active'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_add_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text DEFAULT 'therapist'::text) RETURNS public.tenant_members + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_target_uid uuid; + v_member public.tenant_members%rowtype; + v_is_admin boolean; + v_email text; +begin + if p_tenant_id is null then + raise exception 'tenant_id ?? obrigat??rio'; + end if; + + v_email := lower(trim(coalesce(p_email, ''))); + if v_email = '' then + raise exception 'email ?? obrigat??rio'; + end if; + + -- valida role permitida + if p_role not in ('tenant_admin','therapist','secretary','patient') then + raise exception 'role inv??lida: %', p_role; + end if; + + -- apenas admin do tenant (role real no banco) + select exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.role = 'tenant_admin' + and coalesce(tm.status,'active') = 'active' + ) into v_is_admin; + + if not v_is_admin then + raise exception 'sem permiss??o: apenas admin da cl??nica pode adicionar membros'; + end if; + + -- acha usu??rio pelo e-mail no Supabase Auth + select u.id + into v_target_uid + from auth.users u + where lower(u.email) = v_email + limit 1; + + if v_target_uid is null then + raise exception 'nenhum usu??rio encontrado com este e-mail'; + end if; + + -- cria ou reativa membro + insert into public.tenant_members (tenant_id, user_id, role, status) + values (p_tenant_id, v_target_uid, p_role, 'active') + on conflict (tenant_id, user_id) + do update set + role = excluded.role, + status = 'active' + returning * into v_member; + + return v_member; +end; +$$; + + +-- +-- Name: tenant_feature_allowed(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.v_tenant_entitlements v + where v.tenant_id = p_tenant_id + and v.feature_key = p_feature_key + and coalesce(v.allowed, false) = true + ); +$$; + + +-- +-- Name: tenant_feature_enabled(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select coalesce( + (select tf.enabled + from public.tenant_features tf + where tf.tenant_id = p_tenant_id and tf.feature_key = p_feature_key), + false + ); +$$; + + +-- +-- Name: tenant_features_guard_with_plan(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_features_guard_with_plan() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_allowed boolean; +begin + -- s?? valida quando est?? habilitando + if new.enabled is distinct from true then + return new; + end if; + + -- permitido pelo plano do tenant? + select exists ( + select 1 + from public.v_tenant_entitlements_full v + where v.tenant_id = new.tenant_id + and v.feature_key = new.feature_key + and v.allowed = true + ) + into v_allowed; + + if not v_allowed then + raise exception 'Feature % n??o permitida pelo plano atual do tenant %.', + new.feature_key, new.tenant_id + using errcode = 'P0001'; + end if; + + return new; +end; +$$; + + +-- +-- Name: tenant_has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select + exists ( + select 1 + from public.v_tenant_entitlements e + where e.tenant_id = _tenant_id + and e.feature_key = _feature + and e.allowed = true + ) + or exists ( + select 1 + from public.tenant_features tf + where tf.tenant_id = _tenant_id + and tf.feature_key = _feature + and tf.enabled = true + ); +$$; + + +-- +-- Name: tenant_invite_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_email text; + v_my_email text; + v_token uuid; + v_updated int; +begin + -- valida????es b??sicas + if p_tenant_id is null then + raise exception 'tenant_id inv??lido' using errcode = 'P0001'; + end if; + + v_email := lower(trim(coalesce(p_email, ''))); + if v_email = '' then + raise exception 'Informe um email' using errcode = 'P0001'; + end if; + + -- role permitido (ajuste se quiser) + if p_role is null or p_role not in ('therapist', 'secretary') then + raise exception 'Role inv??lido (use therapist/secretary)' using errcode = 'P0001'; + end if; + + -- ??? bloqueio: auto-convite + v_my_email := public.get_my_email(); + if v_my_email is not null and v_email = v_my_email then + raise exception 'Voc?? n??o pode convidar o seu pr??prio email.' using errcode = 'P0001'; + end if; + + -- ??? bloqueio: j?? ?? membro ativo do tenant + if exists ( + select 1 + from tenant_members tm + join auth.users au on au.id = tm.user_id + where tm.tenant_id = p_tenant_id + and tm.status = 'active' + and lower(au.email) = v_email + ) then + raise exception 'Este email j?? est?? vinculado a esta cl??nica.' using errcode = 'P0001'; + end if; + + -- ??? permiss??o: s?? admin do tenant pode convidar + if not exists ( + select 1 + from tenant_members me + where me.tenant_id = p_tenant_id + and me.user_id = auth.uid() + and me.status = 'active' + and me.role in ('tenant_admin','clinic_admin') + ) then + raise exception 'Sem permiss??o para convidar membros.' using errcode = 'P0001'; + end if; + + -- Gera token (reenvio simples / regenera????o) + v_token := gen_random_uuid(); + + -- 1) tenta "regerar" um convite pendente existente (mesmo email) + update tenant_invites + set token = v_token, + role = p_role, + created_at = now(), + expires_at = now() + interval '7 days', + accepted_at = null, + revoked_at = null + where tenant_id = p_tenant_id + and lower(email) = v_email + and accepted_at is null + and revoked_at is null; + + get diagnostics v_updated = row_count; + + -- 2) se n??o atualizou nada, cria convite novo + if v_updated = 0 then + insert into tenant_invites (tenant_id, email, role, token, created_at, expires_at) + values (p_tenant_id, v_email, p_role, v_token, now(), now() + interval '7 days'); + end if; + + return v_token; +end; +$$; + + +-- +-- Name: tenant_reactivate_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + update public.tenant_members + set status = 'active' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_remove_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +declare + v_role text; +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + if p_member_user_id = auth.uid() then + raise exception 'cannot_remove_self'; + end if; + + -- pega role atual do membro (se n??o existir, erro) + select role into v_role + from public.tenant_members + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if v_role is null then + raise exception 'membership_not_found'; + end if; + + -- trava: se for therapist, n??o pode remover com eventos futuros + if v_role = 'therapist' then + if exists ( + select 1 + from public.agenda_eventos e + where e.owner_id = p_tenant_id + and e.terapeuta_id = p_member_user_id + and e.inicio_em >= now() + and e.status::text not in ('cancelado','cancelled','canceled') + limit 1 + ) then + raise exception 'cannot_remove_therapist_with_future_events'; + end if; + end if; + + update public.tenant_members + set status = 'inactive' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_remove_member_soft(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + if p_member_user_id = auth.uid() then + raise exception 'cannot_remove_self'; + end if; + + update public.tenant_members + set status = 'inactive' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_revoke_invite(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +declare + v_email text; +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + v_email := lower(trim(p_email)); + + update public.tenant_invites + set revoked_at = now(), + revoked_by = auth.uid() + where tenant_id = p_tenant_id + and lower(email) = v_email + and role = p_role + and accepted_at is null + and revoked_at is null; + + if not found then + raise exception 'invite_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_set_member_status(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + -- valida status (adapte aos seus valores reais) + if p_new_status not in ('active','inactive','suspended','invited') then + raise exception 'invalid_status: %', p_new_status; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + -- evita desativar a si mesmo (opcional) + if p_member_user_id = auth.uid() and p_new_status <> 'active' then + raise exception 'cannot_disable_self'; + end if; + + update public.tenant_members + set status = p_new_status + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_update_member_role(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + -- exige auth + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + -- valida role + if p_new_role not in ('tenant_admin','therapist','secretary','patient') then + raise exception 'invalid_role: %', p_new_role; + end if; + + -- somente tenant_admin ativo pode alterar role + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + -- evita o admin remover o pr??prio admin sem querer (opcional mas recomendado) + if p_member_user_id = auth.uid() and p_new_role <> 'tenant_admin' then + raise exception 'cannot_demote_self'; + end if; + + update public.tenant_members + set role = p_new_role + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: toggle_plan(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.toggle_plan(owner uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + current_key text; + new_key text; +begin + select p.key into current_key + from subscriptions s + join plans p on p.id = s.plan_id + where s.owner_id = owner + and s.status = 'active'; + + new_key := case + when current_key = 'pro' then 'free' + else 'pro' + end; + + update subscriptions s + set plan_id = p.id + from plans p + where p.key = new_key + and s.owner_id = owner + and s.status = 'active'; +end; +$$; + + +-- +-- Name: transition_subscription(uuid, text, text, jsonb); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text DEFAULT NULL::text, p_metadata jsonb DEFAULT NULL::jsonb) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_uid uuid; + v_is_allowed boolean := false; +begin + v_uid := auth.uid(); + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id; + + if not found then + raise exception 'Assinatura n??o encontrada'; + end if; + + -- ===================================================== + -- ???? BLOCO DE AUTORIZA????O + -- ===================================================== + + -- 1) SaaS admin pode tudo + if is_saas_admin() then + v_is_allowed := true; + end if; + + -- 2) Assinatura pessoal (therapist) + if not v_is_allowed + and v_sub.tenant_id is null + and v_sub.user_id = v_uid then + v_is_allowed := true; + end if; + + -- 3) Assinatura de clinic (tenant) + if not v_is_allowed + and v_sub.tenant_id is not null then + + if exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = v_sub.tenant_id + and tm.user_id = v_uid + and tm.status = 'active' + and tm.role = 'tenant_admin' + ) then + v_is_allowed := true; + end if; + + end if; + + if not v_is_allowed then + raise exception 'Sem permiss??o para transicionar esta assinatura'; + end if; + + -- ===================================================== + -- ???? TRANSI????O + -- ===================================================== + + update public.subscriptions + set status = p_to_status, + updated_at = now(), + cancelled_at = case when p_to_status = 'cancelled' then now() else cancelled_at end, + suspended_at = case when p_to_status = 'suspended' then now() else suspended_at end, + past_due_since = case when p_to_status = 'past_due' then now() else past_due_since end, + expired_at = case when p_to_status = 'expired' then now() else expired_at end, + activated_at = case when p_to_status = 'active' then now() else activated_at end + where id = p_subscription_id + returning * into v_sub; + + -- ===================================================== + -- ???? EVENT LOG + -- ===================================================== + + insert into public.subscription_events ( + subscription_id, + owner_id, + event_type, + created_at, + created_by, + source, + reason, + metadata, + owner_type, + owner_ref + ) + values ( + v_sub.id, + coalesce(v_sub.tenant_id, v_sub.user_id), + 'status_changed', + now(), + v_uid, + 'manual_transition', + p_reason, + p_metadata, + case when v_sub.tenant_id is not null then 'tenant' else 'personal' end, + coalesce(v_sub.tenant_id, v_sub.user_id) + ); + + return v_sub; +end; +$$; + + +-- +-- Name: trg_fn_financial_records_auto_overdue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.trg_fn_financial_records_auto_overdue() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + IF NEW.status = 'pending' + AND NEW.due_date IS NOT NULL + AND NEW.due_date < CURRENT_DATE + THEN + NEW.status := 'overdue'; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: unstick_notification_queue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.unstick_notification_queue() RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_unstuck integer; +BEGIN + UPDATE public.notification_queue + SET status = 'pendente', + attempts = attempts + 1, + last_error = 'Timeout: preso em processando por >10min', + next_retry_at = now() + interval '2 minutes' + WHERE status = 'processando' + AND updated_at < now() - interval '10 minutes'; + + GET DIAGNOSTICS v_unstuck = ROW_COUNT; + RETURN v_unstuck; +END; +$$; + + +-- +-- Name: update_payment_settings_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.update_payment_settings_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: update_professional_pricing_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.update_professional_pricing_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: user_has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.user_has_feature(_user_id uuid, _feature text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.v_user_entitlements e + where e.user_id = _user_id + and e.feature_key = _feature + and e.allowed = true + ); +$$; + + +-- +-- Name: validate_support_session(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.validate_support_session(p_token text) RETURNS json + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_session support_sessions; +BEGIN + IF p_token IS NULL OR length(trim(p_token)) < 32 THEN + RETURN json_build_object('valid', false, 'tenant_id', null); + END IF; + + SELECT * INTO v_session + FROM public.support_sessions + WHERE token = p_token + AND expires_at > now() + LIMIT 1; + + IF NOT FOUND THEN + RETURN json_build_object('valid', false, 'tenant_id', null); + END IF; + + RETURN json_build_object( + 'valid', true, + 'tenant_id', v_session.tenant_id + ); +END; +$$; + + +-- +-- Name: whoami(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.whoami() RETURNS TABLE(uid uuid, role text) + LANGUAGE sql STABLE + AS $$ + select auth.uid() as uid, auth.role() as role; +$$; + + +-- +-- Name: apply_rls(jsonb, integer); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer DEFAULT (1024 * 1024)) RETURNS SETOF realtime.wal_rls + LANGUAGE plpgsql + AS $$ +declare +-- Regclass of the table e.g. public.notes +entity_ regclass = (quote_ident(wal ->> 'schema') || '.' || quote_ident(wal ->> 'table'))::regclass; + +-- I, U, D, T: insert, update ... +action realtime.action = ( + case wal ->> 'action' + when 'I' then 'INSERT' + when 'U' then 'UPDATE' + when 'D' then 'DELETE' + else 'ERROR' + end +); + +-- Is row level security enabled for the table +is_rls_enabled bool = relrowsecurity from pg_class where oid = entity_; + +subscriptions realtime.subscription[] = array_agg(subs) + from + realtime.subscription subs + where + subs.entity = entity_; + +-- Subscription vars +roles regrole[] = array_agg(distinct us.claims_role::text) + from + unnest(subscriptions) us; + +working_role regrole; +claimed_role regrole; +claims jsonb; + +subscription_id uuid; +subscription_has_access bool; +visible_to_subscription_ids uuid[] = '{}'; + +-- structured info for wal's columns +columns realtime.wal_column[]; +-- previous identity values for update/delete +old_columns realtime.wal_column[]; + +error_record_exceeds_max_size boolean = octet_length(wal::text) > max_record_bytes; + +-- Primary jsonb output for record +output jsonb; + +begin +perform set_config('role', null, true); + +columns = + array_agg( + ( + x->>'name', + x->>'type', + x->>'typeoid', + realtime.cast( + (x->'value') #>> '{}', + coalesce( + (x->>'typeoid')::regtype, -- null when wal2json version <= 2.4 + (x->>'type')::regtype + ) + ), + (pks ->> 'name') is not null, + true + )::realtime.wal_column + ) + from + jsonb_array_elements(wal -> 'columns') x + left join jsonb_array_elements(wal -> 'pk') pks + on (x ->> 'name') = (pks ->> 'name'); + +old_columns = + array_agg( + ( + x->>'name', + x->>'type', + x->>'typeoid', + realtime.cast( + (x->'value') #>> '{}', + coalesce( + (x->>'typeoid')::regtype, -- null when wal2json version <= 2.4 + (x->>'type')::regtype + ) + ), + (pks ->> 'name') is not null, + true + )::realtime.wal_column + ) + from + jsonb_array_elements(wal -> 'identity') x + left join jsonb_array_elements(wal -> 'pk') pks + on (x ->> 'name') = (pks ->> 'name'); + +for working_role in select * from unnest(roles) loop + + -- Update `is_selectable` for columns and old_columns + columns = + array_agg( + ( + c.name, + c.type_name, + c.type_oid, + c.value, + c.is_pkey, + pg_catalog.has_column_privilege(working_role, entity_, c.name, 'SELECT') + )::realtime.wal_column + ) + from + unnest(columns) c; + + old_columns = + array_agg( + ( + c.name, + c.type_name, + c.type_oid, + c.value, + c.is_pkey, + pg_catalog.has_column_privilege(working_role, entity_, c.name, 'SELECT') + )::realtime.wal_column + ) + from + unnest(old_columns) c; + + if action <> 'DELETE' and count(1) = 0 from unnest(columns) c where c.is_pkey then + return next ( + jsonb_build_object( + 'schema', wal ->> 'schema', + 'table', wal ->> 'table', + 'type', action + ), + is_rls_enabled, + -- subscriptions is already filtered by entity + (select array_agg(s.subscription_id) from unnest(subscriptions) as s where claims_role = working_role), + array['Error 400: Bad Request, no primary key'] + )::realtime.wal_rls; + + -- The claims role does not have SELECT permission to the primary key of entity + elsif action <> 'DELETE' and sum(c.is_selectable::int) <> count(1) from unnest(columns) c where c.is_pkey then + return next ( + jsonb_build_object( + 'schema', wal ->> 'schema', + 'table', wal ->> 'table', + 'type', action + ), + is_rls_enabled, + (select array_agg(s.subscription_id) from unnest(subscriptions) as s where claims_role = working_role), + array['Error 401: Unauthorized'] + )::realtime.wal_rls; + + else + output = jsonb_build_object( + 'schema', wal ->> 'schema', + 'table', wal ->> 'table', + 'type', action, + 'commit_timestamp', to_char( + ((wal ->> 'timestamp')::timestamptz at time zone 'utc'), + 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"' + ), + 'columns', ( + select + jsonb_agg( + jsonb_build_object( + 'name', pa.attname, + 'type', pt.typname + ) + order by pa.attnum asc + ) + from + pg_attribute pa + join pg_type pt + on pa.atttypid = pt.oid + where + attrelid = entity_ + and attnum > 0 + and pg_catalog.has_column_privilege(working_role, entity_, pa.attname, 'SELECT') + ) + ) + -- Add "record" key for insert and update + || case + when action in ('INSERT', 'UPDATE') then + jsonb_build_object( + 'record', + ( + select + jsonb_object_agg( + -- if unchanged toast, get column name and value from old record + coalesce((c).name, (oc).name), + case + when (c).name is null then (oc).value + else (c).value + end + ) + from + unnest(columns) c + full outer join unnest(old_columns) oc + on (c).name = (oc).name + where + coalesce((c).is_selectable, (oc).is_selectable) + and ( not error_record_exceeds_max_size or (octet_length((c).value::text) <= 64)) + ) + ) + else '{}'::jsonb + end + -- Add "old_record" key for update and delete + || case + when action = 'UPDATE' then + jsonb_build_object( + 'old_record', + ( + select jsonb_object_agg((c).name, (c).value) + from unnest(old_columns) c + where + (c).is_selectable + and ( not error_record_exceeds_max_size or (octet_length((c).value::text) <= 64)) + ) + ) + when action = 'DELETE' then + jsonb_build_object( + 'old_record', + ( + select jsonb_object_agg((c).name, (c).value) + from unnest(old_columns) c + where + (c).is_selectable + and ( not error_record_exceeds_max_size or (octet_length((c).value::text) <= 64)) + and ( not is_rls_enabled or (c).is_pkey ) -- if RLS enabled, we can't secure deletes so filter to pkey + ) + ) + else '{}'::jsonb + end; + + -- Create the prepared statement + if is_rls_enabled and action <> 'DELETE' then + if (select 1 from pg_prepared_statements where name = 'walrus_rls_stmt' limit 1) > 0 then + deallocate walrus_rls_stmt; + end if; + execute realtime.build_prepared_statement_sql('walrus_rls_stmt', entity_, columns); + end if; + + visible_to_subscription_ids = '{}'; + + for subscription_id, claims in ( + select + subs.subscription_id, + subs.claims + from + unnest(subscriptions) subs + where + subs.entity = entity_ + and subs.claims_role = working_role + and ( + realtime.is_visible_through_filters(columns, subs.filters) + or ( + action = 'DELETE' + and realtime.is_visible_through_filters(old_columns, subs.filters) + ) + ) + ) loop + + if not is_rls_enabled or action = 'DELETE' then + visible_to_subscription_ids = visible_to_subscription_ids || subscription_id; + else + -- Check if RLS allows the role to see the record + perform + -- Trim leading and trailing quotes from working_role because set_config + -- doesn't recognize the role as valid if they are included + set_config('role', trim(both '"' from working_role::text), true), + set_config('request.jwt.claims', claims::text, true); + + execute 'execute walrus_rls_stmt' into subscription_has_access; + + if subscription_has_access then + visible_to_subscription_ids = visible_to_subscription_ids || subscription_id; + end if; + end if; + end loop; + + perform set_config('role', null, true); + + return next ( + output, + is_rls_enabled, + visible_to_subscription_ids, + case + when error_record_exceeds_max_size then array['Error 413: Payload Too Large'] + else '{}' + end + )::realtime.wal_rls; + + end if; +end loop; + +perform set_config('role', null, true); +end; +$$; + + +-- +-- Name: broadcast_changes(text, text, text, text, text, record, record, text); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.broadcast_changes(topic_name text, event_name text, operation text, table_name text, table_schema text, new record, old record, level text DEFAULT 'ROW'::text) RETURNS void + LANGUAGE plpgsql + AS $$ +DECLARE + -- Declare a variable to hold the JSONB representation of the row + row_data jsonb := '{}'::jsonb; +BEGIN + IF level = 'STATEMENT' THEN + RAISE EXCEPTION 'function can only be triggered for each row, not for each statement'; + END IF; + -- Check the operation type and handle accordingly + IF operation = 'INSERT' OR operation = 'UPDATE' OR operation = 'DELETE' THEN + row_data := jsonb_build_object('old_record', OLD, 'record', NEW, 'operation', operation, 'table', table_name, 'schema', table_schema); + PERFORM realtime.send (row_data, event_name, topic_name); + ELSE + RAISE EXCEPTION 'Unexpected operation type: %', operation; + END IF; +EXCEPTION + WHEN OTHERS THEN + RAISE EXCEPTION 'Failed to process the row: %', SQLERRM; +END; + +$$; + + +-- +-- Name: build_prepared_statement_sql(text, regclass, realtime.wal_column[]); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) RETURNS text + LANGUAGE sql + AS $$ + /* + Builds a sql string that, if executed, creates a prepared statement to + tests retrive a row from *entity* by its primary key columns. + Example + select realtime.build_prepared_statement_sql('public.notes', '{"id"}'::text[], '{"bigint"}'::text[]) + */ + select + 'prepare ' || prepared_statement_name || ' as + select + exists( + select + 1 + from + ' || entity || ' + where + ' || string_agg(quote_ident(pkc.name) || '=' || quote_nullable(pkc.value #>> '{}') , ' and ') || ' + )' + from + unnest(columns) pkc + where + pkc.is_pkey + group by + entity + $$; + + +-- +-- Name: cast(text, regtype); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime."cast"(val text, type_ regtype) RETURNS jsonb + LANGUAGE plpgsql IMMUTABLE + AS $$ + declare + res jsonb; + begin + execute format('select to_jsonb(%L::'|| type_::text || ')', val) into res; + return res; + end + $$; + + +-- +-- Name: check_equality_op(realtime.equality_op, regtype, text, text); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) RETURNS boolean + LANGUAGE plpgsql IMMUTABLE + AS $$ + /* + Casts *val_1* and *val_2* as type *type_* and check the *op* condition for truthiness + */ + declare + op_symbol text = ( + case + when op = 'eq' then '=' + when op = 'neq' then '!=' + when op = 'lt' then '<' + when op = 'lte' then '<=' + when op = 'gt' then '>' + when op = 'gte' then '>=' + when op = 'in' then '= any' + else 'UNKNOWN OP' + end + ); + res boolean; + begin + execute format( + 'select %L::'|| type_::text || ' ' || op_symbol + || ' ( %L::' + || ( + case + when op = 'in' then type_::text || '[]' + else type_::text end + ) + || ')', val_1, val_2) into res; + return res; + end; + $$; + + +-- +-- Name: is_visible_through_filters(realtime.wal_column[], realtime.user_defined_filter[]); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) RETURNS boolean + LANGUAGE sql IMMUTABLE + AS $_$ + /* + Should the record be visible (true) or filtered out (false) after *filters* are applied + */ + select + -- Default to allowed when no filters present + $2 is null -- no filters. this should not happen because subscriptions has a default + or array_length($2, 1) is null -- array length of an empty array is null + or bool_and( + coalesce( + realtime.check_equality_op( + op:=f.op, + type_:=coalesce( + col.type_oid::regtype, -- null when wal2json version <= 2.4 + col.type_name::regtype + ), + -- cast jsonb to text + val_1:=col.value #>> '{}', + val_2:=f.value + ), + false -- if null, filter does not match + ) + ) + from + unnest(filters) f + join unnest(columns) col + on f.column_name = col.name; + $_$; + + +-- +-- Name: list_changes(name, name, integer, integer); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) RETURNS SETOF realtime.wal_rls + LANGUAGE sql + SET log_min_messages TO 'fatal' + AS $$ + with pub as ( + select + concat_ws( + ',', + case when bool_or(pubinsert) then 'insert' else null end, + case when bool_or(pubupdate) then 'update' else null end, + case when bool_or(pubdelete) then 'delete' else null end + ) as w2j_actions, + coalesce( + string_agg( + realtime.quote_wal2json(format('%I.%I', schemaname, tablename)::regclass), + ',' + ) filter (where ppt.tablename is not null and ppt.tablename not like '% %'), + '' + ) w2j_add_tables + from + pg_publication pp + left join pg_publication_tables ppt + on pp.pubname = ppt.pubname + where + pp.pubname = publication + group by + pp.pubname + limit 1 + ), + w2j as ( + select + x.*, pub.w2j_add_tables + from + pub, + pg_logical_slot_get_changes( + slot_name, null, max_changes, + 'include-pk', 'true', + 'include-transaction', 'false', + 'include-timestamp', 'true', + 'include-type-oids', 'true', + 'format-version', '2', + 'actions', pub.w2j_actions, + 'add-tables', pub.w2j_add_tables + ) x + ) + select + xyz.wal, + xyz.is_rls_enabled, + xyz.subscription_ids, + xyz.errors + from + w2j, + realtime.apply_rls( + wal := w2j.data::jsonb, + max_record_bytes := max_record_bytes + ) xyz(wal, is_rls_enabled, subscription_ids, errors) + where + w2j.w2j_add_tables <> '' + and xyz.subscription_ids[1] is not null + $$; + + +-- +-- Name: quote_wal2json(regclass); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.quote_wal2json(entity regclass) RETURNS text + LANGUAGE sql IMMUTABLE STRICT + AS $$ + select + ( + select string_agg('' || ch,'') + from unnest(string_to_array(nsp.nspname::text, null)) with ordinality x(ch, idx) + where + not (x.idx = 1 and x.ch = '"') + and not ( + x.idx = array_length(string_to_array(nsp.nspname::text, null), 1) + and x.ch = '"' + ) + ) + || '.' + || ( + select string_agg('' || ch,'') + from unnest(string_to_array(pc.relname::text, null)) with ordinality x(ch, idx) + where + not (x.idx = 1 and x.ch = '"') + and not ( + x.idx = array_length(string_to_array(nsp.nspname::text, null), 1) + and x.ch = '"' + ) + ) + from + pg_class pc + join pg_namespace nsp + on pc.relnamespace = nsp.oid + where + pc.oid = entity + $$; + + +-- +-- Name: send(jsonb, text, text, boolean); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.send(payload jsonb, event text, topic text, private boolean DEFAULT true) RETURNS void + LANGUAGE plpgsql + AS $$ +DECLARE + generated_id uuid; + final_payload jsonb; +BEGIN + BEGIN + -- Generate a new UUID for the id + generated_id := gen_random_uuid(); + + -- Check if payload has an 'id' key, if not, add the generated UUID + IF payload ? 'id' THEN + final_payload := payload; + ELSE + final_payload := jsonb_set(payload, '{id}', to_jsonb(generated_id)); + END IF; + + -- Set the topic configuration + EXECUTE format('SET LOCAL realtime.topic TO %L', topic); + + -- Attempt to insert the message + INSERT INTO realtime.messages (id, payload, event, topic, private, extension) + VALUES (generated_id, final_payload, event, topic, private, 'broadcast'); + EXCEPTION + WHEN OTHERS THEN + -- Capture and notify the error + RAISE WARNING 'ErrorSendingBroadcastMessage: %', SQLERRM; + END; +END; +$$; + + +-- +-- Name: subscription_check_filters(); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.subscription_check_filters() RETURNS trigger + LANGUAGE plpgsql + AS $$ + /* + Validates that the user defined filters for a subscription: + - refer to valid columns that the claimed role may access + - values are coercable to the correct column type + */ + declare + col_names text[] = coalesce( + array_agg(c.column_name order by c.ordinal_position), + '{}'::text[] + ) + from + information_schema.columns c + where + format('%I.%I', c.table_schema, c.table_name)::regclass = new.entity + and pg_catalog.has_column_privilege( + (new.claims ->> 'role'), + format('%I.%I', c.table_schema, c.table_name)::regclass, + c.column_name, + 'SELECT' + ); + filter realtime.user_defined_filter; + col_type regtype; + + in_val jsonb; + begin + for filter in select * from unnest(new.filters) loop + -- Filtered column is valid + if not filter.column_name = any(col_names) then + raise exception 'invalid column for filter %', filter.column_name; + end if; + + -- Type is sanitized and safe for string interpolation + col_type = ( + select atttypid::regtype + from pg_catalog.pg_attribute + where attrelid = new.entity + and attname = filter.column_name + ); + if col_type is null then + raise exception 'failed to lookup type for column %', filter.column_name; + end if; + + -- Set maximum number of entries for in filter + if filter.op = 'in'::realtime.equality_op then + in_val = realtime.cast(filter.value, (col_type::text || '[]')::regtype); + if coalesce(jsonb_array_length(in_val), 0) > 100 then + raise exception 'too many values for `in` filter. Maximum 100'; + end if; + else + -- raises an exception if value is not coercable to type + perform realtime.cast(filter.value, col_type); + end if; + + end loop; + + -- Apply consistent order to filters so the unique constraint on + -- (subscription_id, entity, filters) can't be tricked by a different filter order + new.filters = coalesce( + array_agg(f order by f.column_name, f.op, f.value), + '{}' + ) from unnest(new.filters) f; + + return new; + end; + $$; + + +-- +-- Name: to_regrole(text); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.to_regrole(role_name text) RETURNS regrole + LANGUAGE sql IMMUTABLE + AS $$ select role_name::regrole $$; + + +-- +-- Name: topic(); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.topic() RETURNS text + LANGUAGE sql STABLE + AS $$ +select nullif(current_setting('realtime.topic', true), '')::text; +$$; + + +-- +-- Name: can_insert_object(text, text, uuid, jsonb); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.can_insert_object(bucketid text, name text, owner uuid, metadata jsonb) RETURNS void + LANGUAGE plpgsql + AS $$ +BEGIN + INSERT INTO "storage"."objects" ("bucket_id", "name", "owner", "metadata") VALUES (bucketid, name, owner, metadata); + -- hack to rollback the successful insert + RAISE sqlstate 'PT200' using + message = 'ROLLBACK', + detail = 'rollback successful insert'; +END +$$; + + +-- +-- Name: enforce_bucket_name_length(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.enforce_bucket_name_length() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if length(new.name) > 100 then + raise exception 'bucket name "%" is too long (% characters). Max is 100.', new.name, length(new.name); + end if; + return new; +end; +$$; + + +-- +-- Name: extension(text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.extension(name text) RETURNS text + LANGUAGE plpgsql + AS $$ +DECLARE +_parts text[]; +_filename text; +BEGIN + select string_to_array(name, '/') into _parts; + select _parts[array_length(_parts,1)] into _filename; + -- @todo return the last part instead of 2 + return reverse(split_part(reverse(_filename), '.', 1)); +END +$$; + + +-- +-- Name: filename(text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.filename(name text) RETURNS text + LANGUAGE plpgsql + AS $$ +DECLARE +_parts text[]; +BEGIN + select string_to_array(name, '/') into _parts; + return _parts[array_length(_parts,1)]; +END +$$; + + +-- +-- Name: foldername(text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.foldername(name text) RETURNS text[] + LANGUAGE plpgsql + AS $$ +DECLARE +_parts text[]; +BEGIN + select string_to_array(name, '/') into _parts; + return _parts[1:array_length(_parts,1)-1]; +END +$$; + + +-- +-- Name: get_common_prefix(text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.get_common_prefix(p_key text, p_prefix text, p_delimiter text) RETURNS text + LANGUAGE sql IMMUTABLE + AS $$ +SELECT CASE + WHEN position(p_delimiter IN substring(p_key FROM length(p_prefix) + 1)) > 0 + THEN left(p_key, length(p_prefix) + position(p_delimiter IN substring(p_key FROM length(p_prefix) + 1))) + ELSE NULL +END; +$$; + + +-- +-- Name: get_size_by_bucket(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.get_size_by_bucket() RETURNS TABLE(size bigint, bucket_id text) + LANGUAGE plpgsql + AS $$ +BEGIN + return query + select sum((metadata->>'size')::int) as size, obj.bucket_id + from "storage".objects as obj + group by obj.bucket_id; +END +$$; + + +-- +-- Name: list_multipart_uploads_with_delimiter(text, text, text, integer, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.list_multipart_uploads_with_delimiter(bucket_id text, prefix_param text, delimiter_param text, max_keys integer DEFAULT 100, next_key_token text DEFAULT ''::text, next_upload_token text DEFAULT ''::text) RETURNS TABLE(key text, id text, created_at timestamp with time zone) + LANGUAGE plpgsql + AS $_$ +BEGIN + RETURN QUERY EXECUTE + 'SELECT DISTINCT ON(key COLLATE "C") * from ( + SELECT + CASE + WHEN position($2 IN substring(key from length($1) + 1)) > 0 THEN + substring(key from 1 for length($1) + position($2 IN substring(key from length($1) + 1))) + ELSE + key + END AS key, id, created_at + FROM + storage.s3_multipart_uploads + WHERE + bucket_id = $5 AND + key ILIKE $1 || ''%'' AND + CASE + WHEN $4 != '''' AND $6 = '''' THEN + CASE + WHEN position($2 IN substring(key from length($1) + 1)) > 0 THEN + substring(key from 1 for length($1) + position($2 IN substring(key from length($1) + 1))) COLLATE "C" > $4 + ELSE + key COLLATE "C" > $4 + END + ELSE + true + END AND + CASE + WHEN $6 != '''' THEN + id COLLATE "C" > $6 + ELSE + true + END + ORDER BY + key COLLATE "C" ASC, created_at ASC) as e order by key COLLATE "C" LIMIT $3' + USING prefix_param, delimiter_param, max_keys, next_key_token, bucket_id, next_upload_token; +END; +$_$; + + +-- +-- Name: list_objects_with_delimiter(text, text, text, integer, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.list_objects_with_delimiter(_bucket_id text, prefix_param text, delimiter_param text, max_keys integer DEFAULT 100, start_after text DEFAULT ''::text, next_token text DEFAULT ''::text, sort_order text DEFAULT 'asc'::text) RETURNS TABLE(name text, id uuid, metadata jsonb, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone) + LANGUAGE plpgsql STABLE + AS $_$ +DECLARE + v_peek_name TEXT; + v_current RECORD; + v_common_prefix TEXT; + + -- Configuration + v_is_asc BOOLEAN; + v_prefix TEXT; + v_start TEXT; + v_upper_bound TEXT; + v_file_batch_size INT; + + -- Seek state + v_next_seek TEXT; + v_count INT := 0; + + -- Dynamic SQL for batch query only + v_batch_query TEXT; + +BEGIN + -- ======================================================================== + -- INITIALIZATION + -- ======================================================================== + v_is_asc := lower(coalesce(sort_order, 'asc')) = 'asc'; + v_prefix := coalesce(prefix_param, ''); + v_start := CASE WHEN coalesce(next_token, '') <> '' THEN next_token ELSE coalesce(start_after, '') END; + v_file_batch_size := LEAST(GREATEST(max_keys * 2, 100), 1000); + + -- Calculate upper bound for prefix filtering (bytewise, using COLLATE "C") + IF v_prefix = '' THEN + v_upper_bound := NULL; + ELSIF right(v_prefix, 1) = delimiter_param THEN + v_upper_bound := left(v_prefix, -1) || chr(ascii(delimiter_param) + 1); + ELSE + v_upper_bound := left(v_prefix, -1) || chr(ascii(right(v_prefix, 1)) + 1); + END IF; + + -- Build batch query (dynamic SQL - called infrequently, amortized over many rows) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" >= $2 ' || + 'AND o.name COLLATE "C" < $3 ORDER BY o.name COLLATE "C" ASC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" >= $2 ' || + 'ORDER BY o.name COLLATE "C" ASC LIMIT $4'; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" < $2 ' || + 'AND o.name COLLATE "C" >= $3 ORDER BY o.name COLLATE "C" DESC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" < $2 ' || + 'ORDER BY o.name COLLATE "C" DESC LIMIT $4'; + END IF; + END IF; + + -- ======================================================================== + -- SEEK INITIALIZATION: Determine starting position + -- ======================================================================== + IF v_start = '' THEN + IF v_is_asc THEN + v_next_seek := v_prefix; + ELSE + -- DESC without cursor: find the last item in range + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_next_seek FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_prefix AND o.name COLLATE "C" < v_upper_bound + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix <> '' THEN + SELECT o.name INTO v_next_seek FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_prefix + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_next_seek FROM storage.objects o + WHERE o.bucket_id = _bucket_id + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + END IF; + + IF v_next_seek IS NOT NULL THEN + v_next_seek := v_next_seek || delimiter_param; + ELSE + RETURN; + END IF; + END IF; + ELSE + -- Cursor provided: determine if it refers to a folder or leaf + IF EXISTS ( + SELECT 1 FROM storage.objects o + WHERE o.bucket_id = _bucket_id + AND o.name COLLATE "C" LIKE v_start || delimiter_param || '%' + LIMIT 1 + ) THEN + -- Cursor refers to a folder + IF v_is_asc THEN + v_next_seek := v_start || chr(ascii(delimiter_param) + 1); + ELSE + v_next_seek := v_start || delimiter_param; + END IF; + ELSE + -- Cursor refers to a leaf object + IF v_is_asc THEN + v_next_seek := v_start || delimiter_param; + ELSE + v_next_seek := v_start; + END IF; + END IF; + END IF; + + -- ======================================================================== + -- MAIN LOOP: Hybrid peek-then-batch algorithm + -- Uses STATIC SQL for peek (hot path) and DYNAMIC SQL for batch + -- ======================================================================== + LOOP + EXIT WHEN v_count >= max_keys; + + -- STEP 1: PEEK using STATIC SQL (plan cached, very fast) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_next_seek AND o.name COLLATE "C" < v_upper_bound + ORDER BY o.name COLLATE "C" ASC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_next_seek + ORDER BY o.name COLLATE "C" ASC LIMIT 1; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" < v_next_seek AND o.name COLLATE "C" >= v_prefix + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix <> '' THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" < v_next_seek AND o.name COLLATE "C" >= v_prefix + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" < v_next_seek + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + END IF; + END IF; + + EXIT WHEN v_peek_name IS NULL; + + -- STEP 2: Check if this is a FOLDER or FILE + v_common_prefix := storage.get_common_prefix(v_peek_name, v_prefix, delimiter_param); + + IF v_common_prefix IS NOT NULL THEN + -- FOLDER: Emit and skip to next folder (no heap access needed) + name := rtrim(v_common_prefix, delimiter_param); + id := NULL; + updated_at := NULL; + created_at := NULL; + last_accessed_at := NULL; + metadata := NULL; + RETURN NEXT; + v_count := v_count + 1; + + -- Advance seek past the folder range + IF v_is_asc THEN + v_next_seek := left(v_common_prefix, -1) || chr(ascii(delimiter_param) + 1); + ELSE + v_next_seek := v_common_prefix; + END IF; + ELSE + -- FILE: Batch fetch using DYNAMIC SQL (overhead amortized over many rows) + -- For ASC: upper_bound is the exclusive upper limit (< condition) + -- For DESC: prefix is the inclusive lower limit (>= condition) + FOR v_current IN EXECUTE v_batch_query USING _bucket_id, v_next_seek, + CASE WHEN v_is_asc THEN COALESCE(v_upper_bound, v_prefix) ELSE v_prefix END, v_file_batch_size + LOOP + v_common_prefix := storage.get_common_prefix(v_current.name, v_prefix, delimiter_param); + + IF v_common_prefix IS NOT NULL THEN + -- Hit a folder: exit batch, let peek handle it + v_next_seek := v_current.name; + EXIT; + END IF; + + -- Emit file + name := v_current.name; + id := v_current.id; + updated_at := v_current.updated_at; + created_at := v_current.created_at; + last_accessed_at := v_current.last_accessed_at; + metadata := v_current.metadata; + RETURN NEXT; + v_count := v_count + 1; + + -- Advance seek past this file + IF v_is_asc THEN + v_next_seek := v_current.name || delimiter_param; + ELSE + v_next_seek := v_current.name; + END IF; + + EXIT WHEN v_count >= max_keys; + END LOOP; + END IF; + END LOOP; +END; +$_$; + + +-- +-- Name: operation(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.operation() RETURNS text + LANGUAGE plpgsql STABLE + AS $$ +BEGIN + RETURN current_setting('storage.operation', true); +END; +$$; + + +-- +-- Name: protect_delete(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.protect_delete() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + -- Check if storage.allow_delete_query is set to 'true' + IF COALESCE(current_setting('storage.allow_delete_query', true), 'false') != 'true' THEN + RAISE EXCEPTION 'Direct deletion from storage tables is not allowed. Use the Storage API instead.' + USING HINT = 'This prevents accidental data loss from orphaned objects.', + ERRCODE = '42501'; + END IF; + RETURN NULL; +END; +$$; + + +-- +-- Name: search(text, text, integer, integer, integer, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.search(prefix text, bucketname text, limits integer DEFAULT 100, levels integer DEFAULT 1, offsets integer DEFAULT 0, search text DEFAULT ''::text, sortcolumn text DEFAULT 'name'::text, sortorder text DEFAULT 'asc'::text) RETURNS TABLE(name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb) + LANGUAGE plpgsql STABLE + AS $_$ +DECLARE + v_peek_name TEXT; + v_current RECORD; + v_common_prefix TEXT; + v_delimiter CONSTANT TEXT := '/'; + + -- Configuration + v_limit INT; + v_prefix TEXT; + v_prefix_lower TEXT; + v_is_asc BOOLEAN; + v_order_by TEXT; + v_sort_order TEXT; + v_upper_bound TEXT; + v_file_batch_size INT; + + -- Dynamic SQL for batch query only + v_batch_query TEXT; + + -- Seek state + v_next_seek TEXT; + v_count INT := 0; + v_skipped INT := 0; +BEGIN + -- ======================================================================== + -- INITIALIZATION + -- ======================================================================== + v_limit := LEAST(coalesce(limits, 100), 1500); + v_prefix := coalesce(prefix, '') || coalesce(search, ''); + v_prefix_lower := lower(v_prefix); + v_is_asc := lower(coalesce(sortorder, 'asc')) = 'asc'; + v_file_batch_size := LEAST(GREATEST(v_limit * 2, 100), 1000); + + -- Validate sort column + CASE lower(coalesce(sortcolumn, 'name')) + WHEN 'name' THEN v_order_by := 'name'; + WHEN 'updated_at' THEN v_order_by := 'updated_at'; + WHEN 'created_at' THEN v_order_by := 'created_at'; + WHEN 'last_accessed_at' THEN v_order_by := 'last_accessed_at'; + ELSE v_order_by := 'name'; + END CASE; + + v_sort_order := CASE WHEN v_is_asc THEN 'asc' ELSE 'desc' END; + + -- ======================================================================== + -- NON-NAME SORTING: Use path_tokens approach (unchanged) + -- ======================================================================== + IF v_order_by != 'name' THEN + RETURN QUERY EXECUTE format( + $sql$ + WITH folders AS ( + SELECT path_tokens[$1] AS folder + FROM storage.objects + WHERE objects.name ILIKE $2 || '%%' + AND bucket_id = $3 + AND array_length(objects.path_tokens, 1) <> $1 + GROUP BY folder + ORDER BY folder %s + ) + (SELECT folder AS "name", + NULL::uuid AS id, + NULL::timestamptz AS updated_at, + NULL::timestamptz AS created_at, + NULL::timestamptz AS last_accessed_at, + NULL::jsonb AS metadata FROM folders) + UNION ALL + (SELECT path_tokens[$1] AS "name", + id, updated_at, created_at, last_accessed_at, metadata + FROM storage.objects + WHERE objects.name ILIKE $2 || '%%' + AND bucket_id = $3 + AND array_length(objects.path_tokens, 1) = $1 + ORDER BY %I %s) + LIMIT $4 OFFSET $5 + $sql$, v_sort_order, v_order_by, v_sort_order + ) USING levels, v_prefix, bucketname, v_limit, offsets; + RETURN; + END IF; + + -- ======================================================================== + -- NAME SORTING: Hybrid skip-scan with batch optimization + -- ======================================================================== + + -- Calculate upper bound for prefix filtering + IF v_prefix_lower = '' THEN + v_upper_bound := NULL; + ELSIF right(v_prefix_lower, 1) = v_delimiter THEN + v_upper_bound := left(v_prefix_lower, -1) || chr(ascii(v_delimiter) + 1); + ELSE + v_upper_bound := left(v_prefix_lower, -1) || chr(ascii(right(v_prefix_lower, 1)) + 1); + END IF; + + -- Build batch query (dynamic SQL - called infrequently, amortized over many rows) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" >= $2 ' || + 'AND lower(o.name) COLLATE "C" < $3 ORDER BY lower(o.name) COLLATE "C" ASC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" >= $2 ' || + 'ORDER BY lower(o.name) COLLATE "C" ASC LIMIT $4'; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" < $2 ' || + 'AND lower(o.name) COLLATE "C" >= $3 ORDER BY lower(o.name) COLLATE "C" DESC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" < $2 ' || + 'ORDER BY lower(o.name) COLLATE "C" DESC LIMIT $4'; + END IF; + END IF; + + -- Initialize seek position + IF v_is_asc THEN + v_next_seek := v_prefix_lower; + ELSE + -- DESC: find the last item in range first (static SQL) + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_prefix_lower AND lower(o.name) COLLATE "C" < v_upper_bound + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix_lower <> '' THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_prefix_lower + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + END IF; + + IF v_peek_name IS NOT NULL THEN + v_next_seek := lower(v_peek_name) || v_delimiter; + ELSE + RETURN; + END IF; + END IF; + + -- ======================================================================== + -- MAIN LOOP: Hybrid peek-then-batch algorithm + -- Uses STATIC SQL for peek (hot path) and DYNAMIC SQL for batch + -- ======================================================================== + LOOP + EXIT WHEN v_count >= v_limit; + + -- STEP 1: PEEK using STATIC SQL (plan cached, very fast) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_next_seek AND lower(o.name) COLLATE "C" < v_upper_bound + ORDER BY lower(o.name) COLLATE "C" ASC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_next_seek + ORDER BY lower(o.name) COLLATE "C" ASC LIMIT 1; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" < v_next_seek AND lower(o.name) COLLATE "C" >= v_prefix_lower + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix_lower <> '' THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" < v_next_seek AND lower(o.name) COLLATE "C" >= v_prefix_lower + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" < v_next_seek + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + END IF; + END IF; + + EXIT WHEN v_peek_name IS NULL; + + -- STEP 2: Check if this is a FOLDER or FILE + v_common_prefix := storage.get_common_prefix(lower(v_peek_name), v_prefix_lower, v_delimiter); + + IF v_common_prefix IS NOT NULL THEN + -- FOLDER: Handle offset, emit if needed, skip to next folder + IF v_skipped < offsets THEN + v_skipped := v_skipped + 1; + ELSE + name := split_part(rtrim(v_common_prefix, v_delimiter), v_delimiter, levels); + id := NULL; + updated_at := NULL; + created_at := NULL; + last_accessed_at := NULL; + metadata := NULL; + RETURN NEXT; + v_count := v_count + 1; + END IF; + + -- Advance seek past the folder range + IF v_is_asc THEN + v_next_seek := lower(left(v_common_prefix, -1)) || chr(ascii(v_delimiter) + 1); + ELSE + v_next_seek := lower(v_common_prefix); + END IF; + ELSE + -- FILE: Batch fetch using DYNAMIC SQL (overhead amortized over many rows) + -- For ASC: upper_bound is the exclusive upper limit (< condition) + -- For DESC: prefix_lower is the inclusive lower limit (>= condition) + FOR v_current IN EXECUTE v_batch_query + USING bucketname, v_next_seek, + CASE WHEN v_is_asc THEN COALESCE(v_upper_bound, v_prefix_lower) ELSE v_prefix_lower END, v_file_batch_size + LOOP + v_common_prefix := storage.get_common_prefix(lower(v_current.name), v_prefix_lower, v_delimiter); + + IF v_common_prefix IS NOT NULL THEN + -- Hit a folder: exit batch, let peek handle it + v_next_seek := lower(v_current.name); + EXIT; + END IF; + + -- Handle offset skipping + IF v_skipped < offsets THEN + v_skipped := v_skipped + 1; + ELSE + -- Emit file + name := split_part(v_current.name, v_delimiter, levels); + id := v_current.id; + updated_at := v_current.updated_at; + created_at := v_current.created_at; + last_accessed_at := v_current.last_accessed_at; + metadata := v_current.metadata; + RETURN NEXT; + v_count := v_count + 1; + END IF; + + -- Advance seek past this file + IF v_is_asc THEN + v_next_seek := lower(v_current.name) || v_delimiter; + ELSE + v_next_seek := lower(v_current.name); + END IF; + + EXIT WHEN v_count >= v_limit; + END LOOP; + END IF; + END LOOP; +END; +$_$; + + +-- +-- Name: search_by_timestamp(text, text, integer, integer, text, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.search_by_timestamp(p_prefix text, p_bucket_id text, p_limit integer, p_level integer, p_start_after text, p_sort_order text, p_sort_column text, p_sort_column_after text) RETURNS TABLE(key text, name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb) + LANGUAGE plpgsql STABLE + AS $_$ +DECLARE + v_cursor_op text; + v_query text; + v_prefix text; +BEGIN + v_prefix := coalesce(p_prefix, ''); + + IF p_sort_order = 'asc' THEN + v_cursor_op := '>'; + ELSE + v_cursor_op := '<'; + END IF; + + v_query := format($sql$ + WITH raw_objects AS ( + SELECT + o.name AS obj_name, + o.id AS obj_id, + o.updated_at AS obj_updated_at, + o.created_at AS obj_created_at, + o.last_accessed_at AS obj_last_accessed_at, + o.metadata AS obj_metadata, + storage.get_common_prefix(o.name, $1, '/') AS common_prefix + FROM storage.objects o + WHERE o.bucket_id = $2 + AND o.name COLLATE "C" LIKE $1 || '%%' + ), + -- Aggregate common prefixes (folders) + -- Both created_at and updated_at use MIN(obj_created_at) to match the old prefixes table behavior + aggregated_prefixes AS ( + SELECT + rtrim(common_prefix, '/') AS name, + NULL::uuid AS id, + MIN(obj_created_at) AS updated_at, + MIN(obj_created_at) AS created_at, + NULL::timestamptz AS last_accessed_at, + NULL::jsonb AS metadata, + TRUE AS is_prefix + FROM raw_objects + WHERE common_prefix IS NOT NULL + GROUP BY common_prefix + ), + leaf_objects AS ( + SELECT + obj_name AS name, + obj_id AS id, + obj_updated_at AS updated_at, + obj_created_at AS created_at, + obj_last_accessed_at AS last_accessed_at, + obj_metadata AS metadata, + FALSE AS is_prefix + FROM raw_objects + WHERE common_prefix IS NULL + ), + combined AS ( + SELECT * FROM aggregated_prefixes + UNION ALL + SELECT * FROM leaf_objects + ), + filtered AS ( + SELECT * + FROM combined + WHERE ( + $5 = '' + OR ROW( + date_trunc('milliseconds', %I), + name COLLATE "C" + ) %s ROW( + COALESCE(NULLIF($6, '')::timestamptz, 'epoch'::timestamptz), + $5 + ) + ) + ) + SELECT + split_part(name, '/', $3) AS key, + name, + id, + updated_at, + created_at, + last_accessed_at, + metadata + FROM filtered + ORDER BY + COALESCE(date_trunc('milliseconds', %I), 'epoch'::timestamptz) %s, + name COLLATE "C" %s + LIMIT $4 + $sql$, + p_sort_column, + v_cursor_op, + p_sort_column, + p_sort_order, + p_sort_order + ); + + RETURN QUERY EXECUTE v_query + USING v_prefix, p_bucket_id, p_level, p_limit, p_start_after, p_sort_column_after; +END; +$_$; + + +-- +-- Name: search_v2(text, text, integer, integer, text, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.search_v2(prefix text, bucket_name text, limits integer DEFAULT 100, levels integer DEFAULT 1, start_after text DEFAULT ''::text, sort_order text DEFAULT 'asc'::text, sort_column text DEFAULT 'name'::text, sort_column_after text DEFAULT ''::text) RETURNS TABLE(key text, name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb) + LANGUAGE plpgsql STABLE + AS $$ +DECLARE + v_sort_col text; + v_sort_ord text; + v_limit int; +BEGIN + -- Cap limit to maximum of 1500 records + v_limit := LEAST(coalesce(limits, 100), 1500); + + -- Validate and normalize sort_order + v_sort_ord := lower(coalesce(sort_order, 'asc')); + IF v_sort_ord NOT IN ('asc', 'desc') THEN + v_sort_ord := 'asc'; + END IF; + + -- Validate and normalize sort_column + v_sort_col := lower(coalesce(sort_column, 'name')); + IF v_sort_col NOT IN ('name', 'updated_at', 'created_at') THEN + v_sort_col := 'name'; + END IF; + + -- Route to appropriate implementation + IF v_sort_col = 'name' THEN + -- Use list_objects_with_delimiter for name sorting (most efficient: O(k * log n)) + RETURN QUERY + SELECT + split_part(l.name, '/', levels) AS key, + l.name AS name, + l.id, + l.updated_at, + l.created_at, + l.last_accessed_at, + l.metadata + FROM storage.list_objects_with_delimiter( + bucket_name, + coalesce(prefix, ''), + '/', + v_limit, + start_after, + '', + v_sort_ord + ) l; + ELSE + -- Use aggregation approach for timestamp sorting + -- Not efficient for large datasets but supports correct pagination + RETURN QUERY SELECT * FROM storage.search_by_timestamp( + prefix, bucket_name, v_limit, levels, start_after, + v_sort_ord, v_sort_col, sort_column_after + ); + END IF; +END; +$$; + + +-- +-- Name: update_updated_at_column(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.update_updated_at_column() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: http_request(); Type: FUNCTION; Schema: supabase_functions; Owner: - +-- + +CREATE FUNCTION supabase_functions.http_request() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'supabase_functions' + AS $$ + DECLARE + request_id bigint; + payload jsonb; + url text := TG_ARGV[0]::text; + method text := TG_ARGV[1]::text; + headers jsonb DEFAULT '{}'::jsonb; + params jsonb DEFAULT '{}'::jsonb; + timeout_ms integer DEFAULT 1000; + BEGIN + IF url IS NULL OR url = 'null' THEN + RAISE EXCEPTION 'url argument is missing'; + END IF; + + IF method IS NULL OR method = 'null' THEN + RAISE EXCEPTION 'method argument is missing'; + END IF; + + IF TG_ARGV[2] IS NULL OR TG_ARGV[2] = 'null' THEN + headers = '{"Content-Type": "application/json"}'::jsonb; + ELSE + headers = TG_ARGV[2]::jsonb; + END IF; + + IF TG_ARGV[3] IS NULL OR TG_ARGV[3] = 'null' THEN + params = '{}'::jsonb; + ELSE + params = TG_ARGV[3]::jsonb; + END IF; + + IF TG_ARGV[4] IS NULL OR TG_ARGV[4] = 'null' THEN + timeout_ms = 1000; + ELSE + timeout_ms = TG_ARGV[4]::integer; + END IF; + + CASE + WHEN method = 'GET' THEN + SELECT http_get INTO request_id FROM net.http_get( + url, + params, + headers, + timeout_ms + ); + WHEN method = 'POST' THEN + payload = jsonb_build_object( + 'old_record', OLD, + 'record', NEW, + 'type', TG_OP, + 'table', TG_TABLE_NAME, + 'schema', TG_TABLE_SCHEMA + ); + + SELECT http_post INTO request_id FROM net.http_post( + url, + payload, + params, + headers, + timeout_ms + ); + ELSE + RAISE EXCEPTION 'method argument % is invalid', method; + END CASE; + + INSERT INTO supabase_functions.hooks + (hook_table_id, hook_name, request_id) + VALUES + (TG_RELID, TG_NAME, request_id); + + RETURN NEW; + END +$$; + + +-- +-- Name: extensions; Type: TABLE; Schema: _realtime; Owner: - +-- + +CREATE TABLE _realtime.extensions ( + id uuid NOT NULL, + type text, + settings jsonb, + tenant_external_id text, + inserted_at timestamp(0) without time zone NOT NULL, + updated_at timestamp(0) without time zone NOT NULL +); + + +-- +-- Name: schema_migrations; Type: TABLE; Schema: _realtime; Owner: - +-- + +CREATE TABLE _realtime.schema_migrations ( + version bigint NOT NULL, + inserted_at timestamp(0) without time zone +); + + +-- +-- Name: tenants; Type: TABLE; Schema: _realtime; Owner: - +-- + +CREATE TABLE _realtime.tenants ( + id uuid NOT NULL, + name text, + external_id text, + jwt_secret text, + max_concurrent_users integer DEFAULT 200 NOT NULL, + inserted_at timestamp(0) without time zone NOT NULL, + updated_at timestamp(0) without time zone NOT NULL, + max_events_per_second integer DEFAULT 100 NOT NULL, + postgres_cdc_default text DEFAULT 'postgres_cdc_rls'::text, + max_bytes_per_second integer DEFAULT 100000 NOT NULL, + max_channels_per_client integer DEFAULT 100 NOT NULL, + max_joins_per_second integer DEFAULT 500 NOT NULL, + suspend boolean DEFAULT false, + jwt_jwks jsonb, + notify_private_alpha boolean DEFAULT false, + private_only boolean DEFAULT false NOT NULL, + migrations_ran integer DEFAULT 0, + broadcast_adapter character varying(255) DEFAULT 'gen_rpc'::character varying, + max_presence_events_per_second integer DEFAULT 1000, + max_payload_size_in_kb integer DEFAULT 3000, + CONSTRAINT jwt_secret_or_jwt_jwks_required CHECK (((jwt_secret IS NOT NULL) OR (jwt_jwks IS NOT NULL))) +); + + +-- +-- Name: audit_log_entries; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.audit_log_entries ( + instance_id uuid, + id uuid NOT NULL, + payload json, + created_at timestamp with time zone, + ip_address character varying(64) DEFAULT ''::character varying NOT NULL +); + + +-- +-- Name: TABLE audit_log_entries; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.audit_log_entries IS 'Auth: Audit trail for user actions.'; + + +-- +-- Name: flow_state; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.flow_state ( + id uuid NOT NULL, + user_id uuid, + auth_code text, + code_challenge_method auth.code_challenge_method, + code_challenge text, + provider_type text NOT NULL, + provider_access_token text, + provider_refresh_token text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + authentication_method text NOT NULL, + auth_code_issued_at timestamp with time zone, + invite_token text, + referrer text, + oauth_client_state_id uuid, + linking_target_id uuid, + email_optional boolean DEFAULT false NOT NULL +); + + +-- +-- Name: TABLE flow_state; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.flow_state IS 'Stores metadata for all OAuth/SSO login flows'; + + +-- +-- Name: identities; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.identities ( + provider_id text NOT NULL, + user_id uuid NOT NULL, + identity_data jsonb NOT NULL, + provider text NOT NULL, + last_sign_in_at timestamp with time zone, + created_at timestamp with time zone, + updated_at timestamp with time zone, + email text GENERATED ALWAYS AS (lower((identity_data ->> 'email'::text))) STORED, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: TABLE identities; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.identities IS 'Auth: Stores identities associated to a user.'; + + +-- +-- Name: COLUMN identities.email; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.identities.email IS 'Auth: Email is a generated column that references the optional email property in the identity_data'; + + +-- +-- Name: instances; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.instances ( + id uuid NOT NULL, + uuid uuid, + raw_base_config text, + created_at timestamp with time zone, + updated_at timestamp with time zone +); + + +-- +-- Name: TABLE instances; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.instances IS 'Auth: Manages users across multiple sites.'; + + +-- +-- Name: mfa_amr_claims; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.mfa_amr_claims ( + session_id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + authentication_method text NOT NULL, + id uuid NOT NULL +); + + +-- +-- Name: TABLE mfa_amr_claims; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.mfa_amr_claims IS 'auth: stores authenticator method reference claims for multi factor authentication'; + + +-- +-- Name: mfa_challenges; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.mfa_challenges ( + id uuid NOT NULL, + factor_id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + verified_at timestamp with time zone, + ip_address inet NOT NULL, + otp_code text, + web_authn_session_data jsonb +); + + +-- +-- Name: TABLE mfa_challenges; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.mfa_challenges IS 'auth: stores metadata about challenge requests made'; + + +-- +-- Name: mfa_factors; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.mfa_factors ( + id uuid NOT NULL, + user_id uuid NOT NULL, + friendly_name text, + factor_type auth.factor_type NOT NULL, + status auth.factor_status NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + secret text, + phone text, + last_challenged_at timestamp with time zone, + web_authn_credential jsonb, + web_authn_aaguid uuid, + last_webauthn_challenge_data jsonb +); + + +-- +-- Name: TABLE mfa_factors; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.mfa_factors IS 'auth: stores metadata about factors'; + + +-- +-- Name: COLUMN mfa_factors.last_webauthn_challenge_data; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.mfa_factors.last_webauthn_challenge_data IS 'Stores the latest WebAuthn challenge data including attestation/assertion for customer verification'; + + +-- +-- Name: oauth_authorizations; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_authorizations ( + id uuid NOT NULL, + authorization_id text NOT NULL, + client_id uuid NOT NULL, + user_id uuid, + redirect_uri text NOT NULL, + scope text NOT NULL, + state text, + resource text, + code_challenge text, + code_challenge_method auth.code_challenge_method, + response_type auth.oauth_response_type DEFAULT 'code'::auth.oauth_response_type NOT NULL, + status auth.oauth_authorization_status DEFAULT 'pending'::auth.oauth_authorization_status NOT NULL, + authorization_code text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + expires_at timestamp with time zone DEFAULT (now() + '00:03:00'::interval) NOT NULL, + approved_at timestamp with time zone, + nonce text, + CONSTRAINT oauth_authorizations_authorization_code_length CHECK ((char_length(authorization_code) <= 255)), + CONSTRAINT oauth_authorizations_code_challenge_length CHECK ((char_length(code_challenge) <= 128)), + CONSTRAINT oauth_authorizations_expires_at_future CHECK ((expires_at > created_at)), + CONSTRAINT oauth_authorizations_nonce_length CHECK ((char_length(nonce) <= 255)), + CONSTRAINT oauth_authorizations_redirect_uri_length CHECK ((char_length(redirect_uri) <= 2048)), + CONSTRAINT oauth_authorizations_resource_length CHECK ((char_length(resource) <= 2048)), + CONSTRAINT oauth_authorizations_scope_length CHECK ((char_length(scope) <= 4096)), + CONSTRAINT oauth_authorizations_state_length CHECK ((char_length(state) <= 4096)) +); + + +-- +-- Name: oauth_client_states; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_client_states ( + id uuid NOT NULL, + provider_type text NOT NULL, + code_verifier text, + created_at timestamp with time zone NOT NULL +); + + +-- +-- Name: TABLE oauth_client_states; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.oauth_client_states IS 'Stores OAuth states for third-party provider authentication flows where Supabase acts as the OAuth client.'; + + +-- +-- Name: oauth_clients; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_clients ( + id uuid NOT NULL, + client_secret_hash text, + registration_type auth.oauth_registration_type NOT NULL, + redirect_uris text NOT NULL, + grant_types text NOT NULL, + client_name text, + client_uri text, + logo_uri text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + client_type auth.oauth_client_type DEFAULT 'confidential'::auth.oauth_client_type NOT NULL, + token_endpoint_auth_method text NOT NULL, + CONSTRAINT oauth_clients_client_name_length CHECK ((char_length(client_name) <= 1024)), + CONSTRAINT oauth_clients_client_uri_length CHECK ((char_length(client_uri) <= 2048)), + CONSTRAINT oauth_clients_logo_uri_length CHECK ((char_length(logo_uri) <= 2048)), + CONSTRAINT oauth_clients_token_endpoint_auth_method_check CHECK ((token_endpoint_auth_method = ANY (ARRAY['client_secret_basic'::text, 'client_secret_post'::text, 'none'::text]))) +); + + +-- +-- Name: oauth_consents; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_consents ( + id uuid NOT NULL, + user_id uuid NOT NULL, + client_id uuid NOT NULL, + scopes text NOT NULL, + granted_at timestamp with time zone DEFAULT now() NOT NULL, + revoked_at timestamp with time zone, + CONSTRAINT oauth_consents_revoked_after_granted CHECK (((revoked_at IS NULL) OR (revoked_at >= granted_at))), + CONSTRAINT oauth_consents_scopes_length CHECK ((char_length(scopes) <= 2048)), + CONSTRAINT oauth_consents_scopes_not_empty CHECK ((char_length(TRIM(BOTH FROM scopes)) > 0)) +); + + +-- +-- Name: one_time_tokens; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.one_time_tokens ( + id uuid NOT NULL, + user_id uuid NOT NULL, + token_type auth.one_time_token_type NOT NULL, + token_hash text NOT NULL, + relates_to text NOT NULL, + created_at timestamp without time zone DEFAULT now() NOT NULL, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + CONSTRAINT one_time_tokens_token_hash_check CHECK ((char_length(token_hash) > 0)) +); + + +-- +-- Name: refresh_tokens; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.refresh_tokens ( + instance_id uuid, + id bigint NOT NULL, + token character varying(255), + user_id character varying(255), + revoked boolean, + created_at timestamp with time zone, + updated_at timestamp with time zone, + parent character varying(255), + session_id uuid +); + + +-- +-- Name: TABLE refresh_tokens; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.refresh_tokens IS 'Auth: Store of tokens used to refresh JWT tokens once they expire.'; + + +-- +-- Name: refresh_tokens_id_seq; Type: SEQUENCE; Schema: auth; Owner: - +-- + +CREATE SEQUENCE auth.refresh_tokens_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: refresh_tokens_id_seq; Type: SEQUENCE OWNED BY; Schema: auth; Owner: - +-- + +ALTER SEQUENCE auth.refresh_tokens_id_seq OWNED BY auth.refresh_tokens.id; + + +-- +-- Name: saml_providers; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.saml_providers ( + id uuid NOT NULL, + sso_provider_id uuid NOT NULL, + entity_id text NOT NULL, + metadata_xml text NOT NULL, + metadata_url text, + attribute_mapping jsonb, + created_at timestamp with time zone, + updated_at timestamp with time zone, + name_id_format text, + CONSTRAINT "entity_id not empty" CHECK ((char_length(entity_id) > 0)), + CONSTRAINT "metadata_url not empty" CHECK (((metadata_url = NULL::text) OR (char_length(metadata_url) > 0))), + CONSTRAINT "metadata_xml not empty" CHECK ((char_length(metadata_xml) > 0)) +); + + +-- +-- Name: TABLE saml_providers; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.saml_providers IS 'Auth: Manages SAML Identity Provider connections.'; + + +-- +-- Name: saml_relay_states; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.saml_relay_states ( + id uuid NOT NULL, + sso_provider_id uuid NOT NULL, + request_id text NOT NULL, + for_email text, + redirect_to text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + flow_state_id uuid, + CONSTRAINT "request_id not empty" CHECK ((char_length(request_id) > 0)) +); + + +-- +-- Name: TABLE saml_relay_states; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.saml_relay_states IS 'Auth: Contains SAML Relay State information for each Service Provider initiated login.'; + + +-- +-- Name: schema_migrations; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.schema_migrations ( + version character varying(255) NOT NULL +); + + +-- +-- Name: TABLE schema_migrations; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.schema_migrations IS 'Auth: Manages updates to the auth system.'; + + +-- +-- Name: sessions; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.sessions ( + id uuid NOT NULL, + user_id uuid NOT NULL, + created_at timestamp with time zone, + updated_at timestamp with time zone, + factor_id uuid, + aal auth.aal_level, + not_after timestamp with time zone, + refreshed_at timestamp without time zone, + user_agent text, + ip inet, + tag text, + oauth_client_id uuid, + refresh_token_hmac_key text, + refresh_token_counter bigint, + scopes text, + CONSTRAINT sessions_scopes_length CHECK ((char_length(scopes) <= 4096)) +); + + +-- +-- Name: TABLE sessions; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.sessions IS 'Auth: Stores session data associated to a user.'; + + +-- +-- Name: COLUMN sessions.not_after; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sessions.not_after IS 'Auth: Not after is a nullable column that contains a timestamp after which the session should be regarded as expired.'; + + +-- +-- Name: COLUMN sessions.refresh_token_hmac_key; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sessions.refresh_token_hmac_key IS 'Holds a HMAC-SHA256 key used to sign refresh tokens for this session.'; + + +-- +-- Name: COLUMN sessions.refresh_token_counter; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sessions.refresh_token_counter IS 'Holds the ID (counter) of the last issued refresh token.'; + + +-- +-- Name: sso_domains; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.sso_domains ( + id uuid NOT NULL, + sso_provider_id uuid NOT NULL, + domain text NOT NULL, + created_at timestamp with time zone, + updated_at timestamp with time zone, + CONSTRAINT "domain not empty" CHECK ((char_length(domain) > 0)) +); + + +-- +-- Name: TABLE sso_domains; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.sso_domains IS 'Auth: Manages SSO email address domain mapping to an SSO Identity Provider.'; + + +-- +-- Name: sso_providers; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.sso_providers ( + id uuid NOT NULL, + resource_id text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + disabled boolean, + CONSTRAINT "resource_id not empty" CHECK (((resource_id = NULL::text) OR (char_length(resource_id) > 0))) +); + + +-- +-- Name: TABLE sso_providers; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.sso_providers IS 'Auth: Manages SSO identity provider information; see saml_providers for SAML.'; + + +-- +-- Name: COLUMN sso_providers.resource_id; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sso_providers.resource_id IS 'Auth: Uniquely identifies a SSO provider according to a user-chosen resource ID (case insensitive), useful in infrastructure as code.'; + + +-- +-- Name: users; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.users ( + instance_id uuid, + id uuid NOT NULL, + aud character varying(255), + role character varying(255), + email character varying(255), + encrypted_password character varying(255), + email_confirmed_at timestamp with time zone, + invited_at timestamp with time zone, + confirmation_token character varying(255), + confirmation_sent_at timestamp with time zone, + recovery_token character varying(255), + recovery_sent_at timestamp with time zone, + email_change_token_new character varying(255), + email_change character varying(255), + email_change_sent_at timestamp with time zone, + last_sign_in_at timestamp with time zone, + raw_app_meta_data jsonb, + raw_user_meta_data jsonb, + is_super_admin boolean, + created_at timestamp with time zone, + updated_at timestamp with time zone, + phone text DEFAULT NULL::character varying, + phone_confirmed_at timestamp with time zone, + phone_change text DEFAULT ''::character varying, + phone_change_token character varying(255) DEFAULT ''::character varying, + phone_change_sent_at timestamp with time zone, + confirmed_at timestamp with time zone GENERATED ALWAYS AS (LEAST(email_confirmed_at, phone_confirmed_at)) STORED, + email_change_token_current character varying(255) DEFAULT ''::character varying, + email_change_confirm_status smallint DEFAULT 0, + banned_until timestamp with time zone, + reauthentication_token character varying(255) DEFAULT ''::character varying, + reauthentication_sent_at timestamp with time zone, + is_sso_user boolean DEFAULT false NOT NULL, + deleted_at timestamp with time zone, + is_anonymous boolean DEFAULT false NOT NULL, + CONSTRAINT users_email_change_confirm_status_check CHECK (((email_change_confirm_status >= 0) AND (email_change_confirm_status <= 2))) +); + + +-- +-- Name: TABLE users; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.users IS 'Auth: Stores user login data within a secure schema.'; + + +-- +-- Name: COLUMN users.is_sso_user; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.users.is_sso_user IS 'Auth: Set this column to true when the account comes from SSO. These accounts can have duplicate emails.'; + + +-- +-- Name: _db_migrations; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public._db_migrations ( + id integer NOT NULL, + filename text NOT NULL, + hash text NOT NULL, + category text DEFAULT 'migration'::text NOT NULL, + applied_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: _db_migrations_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public._db_migrations_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: _db_migrations_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public._db_migrations_id_seq OWNED BY public._db_migrations.id; + + +-- +-- Name: addon_credits; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.addon_credits ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid, + addon_type text NOT NULL, + balance integer DEFAULT 0 NOT NULL, + total_purchased integer DEFAULT 0 NOT NULL, + total_consumed integer DEFAULT 0 NOT NULL, + low_balance_threshold integer DEFAULT 10, + low_balance_notified boolean DEFAULT false, + daily_limit integer, + hourly_limit integer, + daily_used integer DEFAULT 0, + hourly_used integer DEFAULT 0, + daily_reset_at timestamp with time zone, + hourly_reset_at timestamp with time zone, + from_number_override text, + expires_at timestamp with time zone, + is_active boolean DEFAULT true, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: TABLE addon_credits; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.addon_credits IS 'Saldo de cr??ditos de add-ons por tenant. Um registro por (tenant_id, addon_type).'; + + +-- +-- Name: addon_products; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.addon_products ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + slug text NOT NULL, + name text NOT NULL, + description text, + addon_type text NOT NULL, + icon text DEFAULT 'pi pi-box'::text, + credits_amount integer DEFAULT 0, + price_cents integer DEFAULT 0 NOT NULL, + currency text DEFAULT 'BRL'::text, + is_active boolean DEFAULT true, + is_visible boolean DEFAULT true, + sort_order integer DEFAULT 0, + metadata jsonb DEFAULT '{}'::jsonb, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + deleted_at timestamp with time zone +); + + +-- +-- Name: TABLE addon_products; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.addon_products IS 'Cat??logo de recursos extras vendidos pela plataforma (SMS, servidor, dom??nio, etc.)'; + + +-- +-- Name: addon_transactions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.addon_transactions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid, + addon_type text NOT NULL, + type text NOT NULL, + amount integer NOT NULL, + balance_before integer DEFAULT 0 NOT NULL, + balance_after integer DEFAULT 0 NOT NULL, + product_id uuid, + queue_id uuid, + description text, + admin_user_id uuid, + payment_method text, + payment_reference text, + price_cents integer, + currency text DEFAULT 'BRL'::text, + created_at timestamp with time zone DEFAULT now(), + metadata jsonb DEFAULT '{}'::jsonb +); + + +-- +-- Name: TABLE addon_transactions; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.addon_transactions IS 'Hist??rico de todas as transa????es de cr??ditos: compras, consumo, ajustes, reembolsos.'; + + +-- +-- Name: agenda_bloqueios; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_bloqueios ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + tipo text NOT NULL, + titulo text NOT NULL, + data_inicio date NOT NULL, + data_fim date, + hora_inicio time without time zone, + hora_fim time without time zone, + recorrente boolean DEFAULT false NOT NULL, + dia_semana smallint, + observacao text, + origem text DEFAULT 'manual'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT agenda_bloqueios_tipo_check CHECK ((tipo = ANY (ARRAY['feriado_nacional'::text, 'feriado_municipal'::text, 'bloqueio'::text]))) +); + + +-- +-- Name: agenda_configuracoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_configuracoes ( + owner_id uuid NOT NULL, + duracao_padrao_minutos integer DEFAULT 50 NOT NULL, + intervalo_padrao_minutos integer DEFAULT 0 NOT NULL, + timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL, + usar_horario_admin_custom boolean DEFAULT false NOT NULL, + admin_inicio_visualizacao time without time zone, + admin_fim_visualizacao time without time zone, + admin_slot_visual_minutos integer DEFAULT 30 NOT NULL, + online_ativo boolean DEFAULT false NOT NULL, + online_min_antecedencia_horas integer DEFAULT 24 NOT NULL, + online_max_dias_futuro integer DEFAULT 60 NOT NULL, + online_cancelar_ate_horas integer DEFAULT 12 NOT NULL, + online_reagendar_ate_horas integer DEFAULT 12 NOT NULL, + online_limite_agendamentos_futuros integer DEFAULT 1 NOT NULL, + online_modo text DEFAULT 'automatico'::text NOT NULL, + online_buffer_antes_min integer DEFAULT 0 NOT NULL, + online_buffer_depois_min integer DEFAULT 0 NOT NULL, + online_modalidade text DEFAULT 'ambos'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + usar_granularidade_custom boolean DEFAULT false NOT NULL, + granularidade_min integer, + setup_concluido boolean DEFAULT false NOT NULL, + setup_concluido_em timestamp with time zone, + agenda_view_mode text DEFAULT 'full_24h'::text NOT NULL, + agenda_custom_start time without time zone, + agenda_custom_end time without time zone, + session_duration_min integer DEFAULT 50 NOT NULL, + session_break_min integer DEFAULT 10 NOT NULL, + pausas_semanais jsonb DEFAULT '[]'::jsonb NOT NULL, + setup_clinica_concluido boolean DEFAULT false NOT NULL, + setup_clinica_concluido_em timestamp with time zone, + tenant_id uuid, + jornada_igual_todos boolean DEFAULT true, + slot_mode text DEFAULT 'fixed'::text NOT NULL, + atendimento_mode text DEFAULT 'particular'::text, + CONSTRAINT agenda_configuracoes_admin_slot_visual_minutos_check CHECK ((admin_slot_visual_minutos = ANY (ARRAY[5, 10, 15, 20, 30, 60]))), + CONSTRAINT agenda_configuracoes_atendimento_mode_check CHECK (((atendimento_mode IS NULL) OR (atendimento_mode = ANY (ARRAY['particular'::text, 'convenio'::text, 'ambos'::text])))), + CONSTRAINT agenda_configuracoes_check CHECK (((usar_horario_admin_custom = false) OR ((admin_inicio_visualizacao IS NOT NULL) AND (admin_fim_visualizacao IS NOT NULL) AND (admin_fim_visualizacao > admin_inicio_visualizacao)))), + CONSTRAINT agenda_configuracoes_duracao_padrao_minutos_check CHECK (((duracao_padrao_minutos >= 10) AND (duracao_padrao_minutos <= 240))), + CONSTRAINT agenda_configuracoes_granularidade_min_check CHECK (((granularidade_min IS NULL) OR (granularidade_min = ANY (ARRAY[5, 10, 15, 20, 30, 45, 50, 60])))), + CONSTRAINT agenda_configuracoes_intervalo_padrao_minutos_check CHECK (((intervalo_padrao_minutos >= 0) AND (intervalo_padrao_minutos <= 120))), + CONSTRAINT agenda_configuracoes_online_buffer_antes_min_check CHECK (((online_buffer_antes_min >= 0) AND (online_buffer_antes_min <= 120))), + CONSTRAINT agenda_configuracoes_online_buffer_depois_min_check CHECK (((online_buffer_depois_min >= 0) AND (online_buffer_depois_min <= 120))), + CONSTRAINT agenda_configuracoes_online_cancelar_ate_horas_check CHECK (((online_cancelar_ate_horas >= 0) AND (online_cancelar_ate_horas <= 720))), + CONSTRAINT agenda_configuracoes_online_limite_agendamentos_futuros_check CHECK (((online_limite_agendamentos_futuros >= 1) AND (online_limite_agendamentos_futuros <= 10))), + CONSTRAINT agenda_configuracoes_online_max_dias_futuro_check CHECK (((online_max_dias_futuro >= 1) AND (online_max_dias_futuro <= 365))), + CONSTRAINT agenda_configuracoes_online_min_antecedencia_horas_check CHECK (((online_min_antecedencia_horas >= 0) AND (online_min_antecedencia_horas <= 720))), + CONSTRAINT agenda_configuracoes_online_modalidade_check CHECK ((online_modalidade = ANY (ARRAY['online'::text, 'presencial'::text, 'ambos'::text]))), + CONSTRAINT agenda_configuracoes_online_modo_check CHECK ((online_modo = ANY (ARRAY['automatico'::text, 'aprovacao'::text]))), + CONSTRAINT agenda_configuracoes_online_reagendar_ate_horas_check CHECK (((online_reagendar_ate_horas >= 0) AND (online_reagendar_ate_horas <= 720))), + CONSTRAINT agenda_configuracoes_slot_mode_chk CHECK ((slot_mode = ANY (ARRAY['fixed'::text, 'dynamic'::text]))), + CONSTRAINT session_break_min_chk CHECK (((session_break_min >= 0) AND (session_break_min <= 60))), + CONSTRAINT session_duration_min_chk CHECK (((session_duration_min >= 10) AND (session_duration_min <= 240))) +); + + +-- +-- Name: COLUMN agenda_configuracoes.atendimento_mode; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agenda_configuracoes.atendimento_mode IS 'Modo de atendimento: particular | convenio | ambos'; + + +-- +-- Name: agenda_eventos; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_eventos ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tipo public.tipo_evento_agenda DEFAULT 'sessao'::public.tipo_evento_agenda NOT NULL, + status public.status_evento_agenda DEFAULT 'agendado'::public.status_evento_agenda NOT NULL, + titulo text, + observacoes text, + inicio_em timestamp with time zone NOT NULL, + fim_em timestamp with time zone NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + terapeuta_id uuid, + tenant_id uuid NOT NULL, + visibility_scope text DEFAULT 'public'::text NOT NULL, + mirror_of_event_id uuid, + mirror_source text, + patient_id uuid, + determined_commitment_id uuid, + link_online text, + titulo_custom text, + extra_fields jsonb, + recurrence_id uuid, + recurrence_date date, + modalidade text DEFAULT 'presencial'::text, + price numeric(10,2), + billing_contract_id uuid, + billed boolean DEFAULT false NOT NULL, + services_customized boolean DEFAULT false NOT NULL, + insurance_plan_id uuid, + insurance_guide_number text, + insurance_value numeric(10,2), + insurance_plan_service_id uuid, + CONSTRAINT agenda_eventos_check CHECK ((fim_em > inicio_em)) +); + + +-- +-- Name: COLUMN agenda_eventos.price; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agenda_eventos.price IS 'Valor da sess??o em BRL. Preenchido automaticamente pela tabela professional_pricing do profissional.'; + + +-- +-- Name: agenda_excecoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_excecoes ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + data date NOT NULL, + hora_inicio time without time zone, + hora_fim time without time zone, + tipo public.tipo_excecao_agenda NOT NULL, + motivo text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + status public.status_excecao_agenda DEFAULT 'ativo'::public.status_excecao_agenda NOT NULL, + fonte text DEFAULT 'manual'::text NOT NULL, + aplicavel_online boolean DEFAULT true NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_excecoes_check CHECK ((((hora_inicio IS NULL) AND (hora_fim IS NULL)) OR ((hora_inicio IS NOT NULL) AND (hora_fim IS NOT NULL) AND (hora_fim > hora_inicio)))), + CONSTRAINT agenda_excecoes_fonte_check CHECK ((fonte = ANY (ARRAY['manual'::text, 'feriado_google'::text, 'sistema'::text]))) +); + + +-- +-- Name: agenda_online_slots; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_online_slots ( + id bigint NOT NULL, + owner_id uuid NOT NULL, + weekday integer NOT NULL, + "time" time without time zone NOT NULL, + enabled boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_online_slots_weekday_check CHECK ((weekday = ANY (ARRAY[0, 1, 2, 3, 4, 5, 6]))) +); + + +-- +-- Name: agenda_online_slots_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.agenda_online_slots_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: agenda_online_slots_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.agenda_online_slots_id_seq OWNED BY public.agenda_online_slots.id; + + +-- +-- Name: agenda_regras_semanais; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_regras_semanais ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + dia_semana smallint NOT NULL, + hora_inicio time without time zone NOT NULL, + hora_fim time without time zone NOT NULL, + modalidade text DEFAULT 'ambos'::text NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_regras_semanais_check CHECK ((hora_fim > hora_inicio)), + CONSTRAINT agenda_regras_semanais_dia_semana_check CHECK (((dia_semana >= 0) AND (dia_semana <= 6))), + CONSTRAINT agenda_regras_semanais_modalidade_check CHECK (((modalidade = ANY (ARRAY['online'::text, 'presencial'::text, 'ambos'::text])) OR (modalidade IS NULL))) +); + + +-- +-- Name: agenda_slots_bloqueados_semanais; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_slots_bloqueados_semanais ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + dia_semana smallint NOT NULL, + hora_inicio time without time zone NOT NULL, + motivo text, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_slots_bloqueados_semanais_dia_semana_check CHECK (((dia_semana >= 0) AND (dia_semana <= 6))) +); + + +-- +-- Name: agenda_slots_regras; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_slots_regras ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + dia_semana smallint NOT NULL, + passo_minutos integer NOT NULL, + offset_minutos integer DEFAULT 0 NOT NULL, + buffer_antes_min integer DEFAULT 0 NOT NULL, + buffer_depois_min integer DEFAULT 0 NOT NULL, + min_antecedencia_horas integer DEFAULT 0 NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_slots_regras_buffer_antes_min_check CHECK (((buffer_antes_min >= 0) AND (buffer_antes_min <= 240))), + CONSTRAINT agenda_slots_regras_buffer_depois_min_check CHECK (((buffer_depois_min >= 0) AND (buffer_depois_min <= 240))), + CONSTRAINT agenda_slots_regras_dia_semana_check CHECK (((dia_semana >= 0) AND (dia_semana <= 6))), + CONSTRAINT agenda_slots_regras_min_antecedencia_horas_check CHECK (((min_antecedencia_horas >= 0) AND (min_antecedencia_horas <= 720))), + CONSTRAINT agenda_slots_regras_offset_minutos_check CHECK (((offset_minutos >= 0) AND (offset_minutos <= 55))), + CONSTRAINT agenda_slots_regras_passo_minutos_check CHECK (((passo_minutos >= 5) AND (passo_minutos <= 240))) +); + + +-- +-- Name: agendador_configuracoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agendador_configuracoes ( + owner_id uuid NOT NULL, + tenant_id uuid, + ativo boolean DEFAULT false NOT NULL, + link_slug text, + imagem_fundo_url text, + imagem_header_url text, + logomarca_url text, + cor_primaria text DEFAULT '#4b6bff'::text, + nome_exibicao text, + endereco text, + botao_como_chegar_ativo boolean DEFAULT true NOT NULL, + maps_url text, + modo_aprovacao text DEFAULT 'aprovacao'::text NOT NULL, + modalidade text DEFAULT 'presencial'::text NOT NULL, + tipos_habilitados jsonb DEFAULT '["primeira", "retorno"]'::jsonb NOT NULL, + duracao_sessao_min integer DEFAULT 50 NOT NULL, + antecedencia_minima_horas integer DEFAULT 24 NOT NULL, + prazo_resposta_horas integer DEFAULT 2 NOT NULL, + reserva_horas integer DEFAULT 2 NOT NULL, + pagamento_obrigatorio boolean DEFAULT false NOT NULL, + pix_chave text, + pix_countdown_minutos integer DEFAULT 20 NOT NULL, + triagem_motivo boolean DEFAULT true NOT NULL, + triagem_como_conheceu boolean DEFAULT false NOT NULL, + verificacao_email boolean DEFAULT false NOT NULL, + exigir_aceite_lgpd boolean DEFAULT true NOT NULL, + mensagem_boas_vindas text, + texto_como_se_preparar text, + texto_termos_lgpd text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + pagamento_modo text DEFAULT 'sem_pagamento'::text NOT NULL, + pagamento_metodos_visiveis text[] DEFAULT '{}'::text[] NOT NULL, + CONSTRAINT agendador_configuracoes_antecedencia_check CHECK (((antecedencia_minima_horas >= 0) AND (antecedencia_minima_horas <= 720))), + CONSTRAINT agendador_configuracoes_duracao_check CHECK (((duracao_sessao_min >= 10) AND (duracao_sessao_min <= 240))), + CONSTRAINT agendador_configuracoes_modalidade_check CHECK ((modalidade = ANY (ARRAY['presencial'::text, 'online'::text, 'ambos'::text]))), + CONSTRAINT agendador_configuracoes_modo_check CHECK ((modo_aprovacao = ANY (ARRAY['automatico'::text, 'aprovacao'::text]))), + CONSTRAINT agendador_configuracoes_pix_countdown_check CHECK (((pix_countdown_minutos >= 5) AND (pix_countdown_minutos <= 120))), + CONSTRAINT agendador_configuracoes_prazo_check CHECK (((prazo_resposta_horas >= 1) AND (prazo_resposta_horas <= 72))), + CONSTRAINT agendador_configuracoes_reserva_check CHECK (((reserva_horas >= 1) AND (reserva_horas <= 48))) +); + + +-- +-- Name: COLUMN agendador_configuracoes.pagamento_modo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agendador_configuracoes.pagamento_modo IS 'sem_pagamento | pagar_na_hora | pix_antecipado'; + + +-- +-- Name: COLUMN agendador_configuracoes.pagamento_metodos_visiveis; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agendador_configuracoes.pagamento_metodos_visiveis IS 'M??todos exibidos ao paciente quando pagamento_modo = pagar_na_hora. Ex: {pix, deposito, dinheiro, cartao, convenio}'; + + +-- +-- Name: agendador_solicitacoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agendador_solicitacoes ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + paciente_nome text NOT NULL, + paciente_sobrenome text, + paciente_email text NOT NULL, + paciente_celular text, + paciente_cpf text, + tipo text NOT NULL, + modalidade text NOT NULL, + data_solicitada date NOT NULL, + hora_solicitada time without time zone NOT NULL, + reservado_ate timestamp with time zone, + motivo text, + como_conheceu text, + pix_status text DEFAULT 'pendente'::text, + pix_pago_em timestamp with time zone, + status text DEFAULT 'pendente'::text NOT NULL, + recusado_motivo text, + autorizado_em timestamp with time zone, + autorizado_por uuid, + user_id uuid, + patient_id uuid, + evento_id uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT agendador_sol_modalidade_check CHECK ((modalidade = ANY (ARRAY['presencial'::text, 'online'::text]))), + CONSTRAINT agendador_sol_pix_check CHECK (((pix_status IS NULL) OR (pix_status = ANY (ARRAY['pendente'::text, 'pago'::text, 'expirado'::text])))), + CONSTRAINT agendador_sol_status_check CHECK ((status = ANY (ARRAY['pendente'::text, 'autorizado'::text, 'recusado'::text, 'expirado'::text, 'convertido'::text]))), + CONSTRAINT agendador_sol_tipo_check CHECK ((tipo = ANY (ARRAY['primeira'::text, 'retorno'::text, 'reagendar'::text]))) +); + + +-- +-- Name: billing_contracts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.billing_contracts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + patient_id uuid NOT NULL, + type text NOT NULL, + total_sessions integer, + sessions_used integer DEFAULT 0, + package_price numeric(10,2), + amount numeric(10,2), + billing_interval text, + active_from timestamp with time zone DEFAULT now(), + active_to timestamp with time zone, + status text DEFAULT 'active'::text NOT NULL, + created_at timestamp with time zone DEFAULT now(), + CONSTRAINT billing_contracts_interval_chk CHECK (((billing_interval IS NULL) OR (billing_interval = ANY (ARRAY['monthly'::text, 'weekly'::text])))), + CONSTRAINT billing_contracts_sess_used_chk CHECK (((sessions_used IS NULL) OR (sessions_used >= 0))), + CONSTRAINT billing_contracts_status_chk CHECK ((status = ANY (ARRAY['active'::text, 'completed'::text, 'cancelled'::text]))), + CONSTRAINT billing_contracts_total_sess_chk CHECK (((total_sessions IS NULL) OR (total_sessions > 0))), + CONSTRAINT billing_contracts_type_chk CHECK ((type = ANY (ARRAY['per_session'::text, 'package'::text, 'subscription'::text]))) +); + + +-- +-- Name: commitment_services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.commitment_services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + commitment_id uuid NOT NULL, + service_id uuid NOT NULL, + quantity integer DEFAULT 1 NOT NULL, + unit_price numeric(10,2) NOT NULL, + discount_pct numeric(5,2) DEFAULT 0, + discount_flat numeric(10,2) DEFAULT 0, + final_price numeric(10,2) NOT NULL, + created_at timestamp with time zone DEFAULT now(), + CONSTRAINT commitment_services_disc_flat_chk CHECK ((discount_flat >= (0)::numeric)), + CONSTRAINT commitment_services_disc_pct_chk CHECK (((discount_pct >= (0)::numeric) AND (discount_pct <= (100)::numeric))), + CONSTRAINT commitment_services_final_price_chk CHECK ((final_price >= (0)::numeric)), + CONSTRAINT commitment_services_quantity_chk CHECK ((quantity > 0)) +); + + +-- +-- Name: commitment_time_logs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.commitment_time_logs ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + commitment_id uuid NOT NULL, + calendar_event_id uuid, + source public.commitment_log_source DEFAULT 'manual'::public.commitment_log_source NOT NULL, + started_at timestamp with time zone NOT NULL, + ended_at timestamp with time zone NOT NULL, + minutes integer NOT NULL, + created_by uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: company_profiles; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.company_profiles ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + nome_fantasia text, + razao_social text, + tipo_empresa text, + cnpj text, + ie text, + im text, + cep text, + logradouro text, + numero text, + complemento text, + bairro text, + cidade text, + estado text, + email text, + telefone text, + site text, + logo_url text, + redes_sociais jsonb DEFAULT '[]'::jsonb NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: current_tenant_id; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.current_tenant_id AS + SELECT current_setting('request.jwt.claim.tenant_id'::text, true) AS current_setting; + + +-- +-- Name: determined_commitment_fields; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.determined_commitment_fields ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + commitment_id uuid NOT NULL, + key text NOT NULL, + label text NOT NULL, + field_type public.determined_field_type DEFAULT 'text'::public.determined_field_type NOT NULL, + required boolean DEFAULT false NOT NULL, + sort_order integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: determined_commitments; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.determined_commitments ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + created_by uuid, + is_native boolean DEFAULT false NOT NULL, + native_key text, + is_locked boolean DEFAULT false NOT NULL, + active boolean DEFAULT true NOT NULL, + name text NOT NULL, + description text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + bg_color text, + text_color text +); + + +-- +-- Name: dev_user_credentials; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.dev_user_credentials ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid, + email text NOT NULL, + password_dev text NOT NULL, + kind text DEFAULT 'custom'::text NOT NULL, + note text, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: email_layout_config; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.email_layout_config ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + header_config jsonb DEFAULT '{"layout": null, "content": "", "enabled": false}'::jsonb NOT NULL, + footer_config jsonb DEFAULT '{"layout": null, "content": "", "enabled": false}'::jsonb NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: email_templates_global; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.email_templates_global ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + domain text NOT NULL, + channel text DEFAULT 'email'::text NOT NULL, + subject text NOT NULL, + body_html text NOT NULL, + body_text text, + version integer DEFAULT 1 NOT NULL, + is_active boolean DEFAULT true NOT NULL, + variables jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: email_templates_tenant; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.email_templates_tenant ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid, + template_key text NOT NULL, + subject text, + body_html text, + body_text text, + enabled boolean DEFAULT true NOT NULL, + synced_version integer, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: entitlements_invalidation; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.entitlements_invalidation ( + owner_id uuid NOT NULL, + changed_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.features ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + description text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + descricao text DEFAULT ''::text NOT NULL, + name text DEFAULT ''::text NOT NULL +); + + +-- +-- Name: COLUMN features.descricao; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.features.descricao IS 'Descri????o humana da feature (exibi????o no admin e documenta????o).'; + + +-- +-- Name: feriados; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.feriados ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid, + owner_id uuid, + tipo text DEFAULT 'municipal'::text NOT NULL, + nome text NOT NULL, + data date NOT NULL, + cidade text, + estado text, + observacao text, + bloqueia_sessoes boolean DEFAULT false NOT NULL, + criado_em timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT feriados_tipo_check CHECK ((tipo = ANY (ARRAY['municipal'::text, 'personalizado'::text]))) +); + + +-- +-- Name: financial_categories; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.financial_categories ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid NOT NULL, + name text NOT NULL, + type public.financial_record_type DEFAULT 'receita'::public.financial_record_type NOT NULL, + color text DEFAULT '#6366f1'::text, + icon text DEFAULT 'pi pi-tag'::text, + sort_order integer DEFAULT 0, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: financial_exceptions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.financial_exceptions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid, + tenant_id uuid NOT NULL, + exception_type text NOT NULL, + charge_mode text NOT NULL, + charge_value numeric(10,2), + charge_pct numeric(5,2), + min_hours_notice integer, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + CONSTRAINT financial_exceptions_charge_chk CHECK ((charge_mode = ANY (ARRAY['none'::text, 'full'::text, 'fixed_fee'::text, 'percentage'::text]))), + CONSTRAINT financial_exceptions_type_chk CHECK ((exception_type = ANY (ARRAY['patient_no_show'::text, 'patient_cancellation'::text, 'professional_cancellation'::text]))) +); + + +-- +-- Name: global_notices; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.global_notices ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + title text, + message text DEFAULT ''::text NOT NULL, + variant text DEFAULT 'info'::text NOT NULL, + roles text[] DEFAULT '{}'::text[] NOT NULL, + contexts text[] DEFAULT '{}'::text[] NOT NULL, + starts_at timestamp with time zone, + ends_at timestamp with time zone, + is_active boolean DEFAULT true NOT NULL, + priority integer DEFAULT 0 NOT NULL, + dismissible boolean DEFAULT true NOT NULL, + persist_dismiss boolean DEFAULT true NOT NULL, + dismiss_scope text DEFAULT 'device'::text NOT NULL, + show_once boolean DEFAULT false NOT NULL, + max_views integer, + cooldown_minutes integer, + version integer DEFAULT 1 NOT NULL, + action_type text DEFAULT 'none'::text NOT NULL, + action_label text, + action_url text, + action_route text, + views_count integer DEFAULT 0 NOT NULL, + clicks_count integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + created_by uuid, + content_align text DEFAULT 'left'::text NOT NULL, + link_target text DEFAULT '_blank'::text NOT NULL, + CONSTRAINT global_notices_action_type_check CHECK ((action_type = ANY (ARRAY['none'::text, 'internal'::text, 'external'::text]))), + CONSTRAINT global_notices_content_align_check CHECK ((content_align = ANY (ARRAY['left'::text, 'center'::text, 'right'::text, 'justify'::text]))), + CONSTRAINT global_notices_dismiss_scope_check CHECK ((dismiss_scope = ANY (ARRAY['session'::text, 'device'::text, 'user'::text]))), + CONSTRAINT global_notices_link_target_check CHECK ((link_target = ANY (ARRAY['_blank'::text, '_self'::text, '_parent'::text, '_top'::text]))), + CONSTRAINT global_notices_variant_check CHECK ((variant = ANY (ARRAY['info'::text, 'success'::text, 'warning'::text, 'error'::text]))) +); + + +-- +-- Name: insurance_plan_services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.insurance_plan_services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + insurance_plan_id uuid NOT NULL, + name text NOT NULL, + value numeric(10,2) NOT NULL, + active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: insurance_plans; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.insurance_plans ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + name text NOT NULL, + notes text, + default_value numeric(10,2), + active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: login_carousel_slides; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.login_carousel_slides ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + title text NOT NULL, + body text NOT NULL, + icon text DEFAULT 'pi-star'::text NOT NULL, + ordem integer DEFAULT 0 NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: module_features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.module_features ( + module_id uuid NOT NULL, + feature_id uuid NOT NULL, + enabled boolean DEFAULT true NOT NULL, + limits jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: modules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.modules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + name text NOT NULL, + description text, + is_active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: notice_dismissals; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notice_dismissals ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + notice_id uuid NOT NULL, + user_id uuid NOT NULL, + version integer DEFAULT 1 NOT NULL, + dismissed_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: notification_channels; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_channels ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + channel text NOT NULL, + provider text NOT NULL, + is_active boolean DEFAULT false NOT NULL, + display_name text, + sender_address text, + credentials jsonb DEFAULT '{}'::jsonb NOT NULL, + connection_status text DEFAULT 'disconnected'::text, + last_health_check timestamp with time zone, + metadata jsonb DEFAULT '{}'::jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + twilio_subaccount_sid text, + twilio_phone_number text, + twilio_phone_sid text, + webhook_url text, + cost_per_message_usd numeric(8,6) DEFAULT 0, + price_per_message_brl numeric(8,4) DEFAULT 0, + provisioned_at timestamp with time zone, + CONSTRAINT notification_channels_channel_check CHECK ((channel = ANY (ARRAY['whatsapp'::text, 'email'::text, 'sms'::text]))), + CONSTRAINT notification_channels_connection_status_check CHECK ((connection_status = ANY (ARRAY['connected'::text, 'disconnected'::text, 'connecting'::text, 'qr_pending'::text, 'error'::text]))), + CONSTRAINT notification_channels_provider_check CHECK ((provider = ANY (ARRAY['evolution_api'::text, 'meta_official'::text, 'twilio'::text, 'zenvia'::text, 'sendgrid'::text, 'resend'::text, 'smtp'::text, 'zapi'::text]))) +); + + +-- +-- Name: COLUMN notification_channels.twilio_subaccount_sid; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.twilio_subaccount_sid IS 'SID da subconta Twilio criada para este tenant'; + + +-- +-- Name: COLUMN notification_channels.twilio_phone_number; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.twilio_phone_number IS 'Número WhatsApp provisionado (E.164, ex: +5511999990000)'; + + +-- +-- Name: COLUMN notification_channels.twilio_phone_sid; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.twilio_phone_sid IS 'SID do número de telefone na subconta Twilio'; + + +-- +-- Name: COLUMN notification_channels.webhook_url; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.webhook_url IS 'URL do webhook configurada na Twilio para receber callbacks de status'; + + +-- +-- Name: COLUMN notification_channels.cost_per_message_usd; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.cost_per_message_usd IS 'Custo real Twilio por mensagem WhatsApp (USD)'; + + +-- +-- Name: COLUMN notification_channels.price_per_message_brl; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.price_per_message_brl IS 'Valor cobrado do tenant por mensagem (BRL, inclui margem SaaS)'; + + +-- +-- Name: COLUMN notification_channels.provisioned_at; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.provisioned_at IS 'Timestamp do provisionamento da subconta'; + + +-- +-- Name: notification_logs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_logs ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + queue_id uuid, + agenda_evento_id uuid, + patient_id uuid NOT NULL, + channel text NOT NULL, + template_key text NOT NULL, + schedule_key text, + recipient_address text NOT NULL, + resolved_message text, + resolved_vars jsonb, + status text NOT NULL, + provider text, + provider_message_id text, + provider_status text, + provider_response jsonb, + sent_at timestamp with time zone, + delivered_at timestamp with time zone, + read_at timestamp with time zone, + failed_at timestamp with time zone, + failure_reason text, + estimated_cost_brl numeric(8,4) DEFAULT 0, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT notification_logs_status_check CHECK ((status = ANY (ARRAY['sent'::text, 'delivered'::text, 'read'::text, 'failed'::text, 'bounced'::text, 'opted_out'::text]))) +); + + +-- +-- Name: notification_preferences; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_preferences ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + patient_id uuid NOT NULL, + whatsapp_opt_in boolean DEFAULT true NOT NULL, + email_opt_in boolean DEFAULT true NOT NULL, + sms_opt_in boolean DEFAULT false NOT NULL, + preferred_time_start time without time zone DEFAULT '08:00:00'::time without time zone, + preferred_time_end time without time zone DEFAULT '20:00:00'::time without time zone, + lgpd_consent_given boolean DEFAULT false NOT NULL, + lgpd_consent_date timestamp with time zone, + lgpd_consent_version text, + lgpd_consent_ip inet, + lgpd_opt_out_date timestamp with time zone, + lgpd_opt_out_reason text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone +); + + +-- +-- Name: notification_queue; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_queue ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + agenda_evento_id uuid, + patient_id uuid NOT NULL, + channel text NOT NULL, + template_key text NOT NULL, + schedule_key text NOT NULL, + resolved_vars jsonb DEFAULT '{}'::jsonb NOT NULL, + recipient_address text NOT NULL, + status text DEFAULT 'pendente'::text NOT NULL, + scheduled_at timestamp with time zone NOT NULL, + sent_at timestamp with time zone, + next_retry_at timestamp with time zone, + attempts integer DEFAULT 0 NOT NULL, + max_attempts integer DEFAULT 5 NOT NULL, + last_error text, + idempotency_key text NOT NULL, + provider_message_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT notification_queue_channel_check CHECK ((channel = ANY (ARRAY['whatsapp'::text, 'email'::text, 'sms'::text]))), + CONSTRAINT notification_queue_status_check CHECK ((status = ANY (ARRAY['pendente'::text, 'processando'::text, 'enviado'::text, 'falhou'::text, 'cancelado'::text, 'ignorado'::text]))) +); + + +-- +-- Name: notification_schedules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_schedules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + schedule_key text NOT NULL, + event_type text NOT NULL, + trigger_type text NOT NULL, + offset_minutes integer DEFAULT 0, + whatsapp_enabled boolean DEFAULT true NOT NULL, + email_enabled boolean DEFAULT true NOT NULL, + sms_enabled boolean DEFAULT false NOT NULL, + allowed_time_start time without time zone DEFAULT '08:00:00'::time without time zone, + allowed_time_end time without time zone DEFAULT '20:00:00'::time without time zone, + skip_weekends boolean DEFAULT false, + skip_holidays boolean DEFAULT false, + is_active boolean DEFAULT true NOT NULL, + sort_order integer DEFAULT 0, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + CONSTRAINT notification_schedules_event_type_check CHECK ((event_type = ANY (ARRAY['lembrete_sessao'::text, 'confirmacao_sessao'::text, 'cancelamento_sessao'::text, 'reagendamento'::text, 'cobranca_pendente'::text, 'boas_vindas_paciente'::text]))), + CONSTRAINT notification_schedules_trigger_type_check CHECK ((trigger_type = ANY (ARRAY['before_event'::text, 'after_event'::text, 'immediate'::text]))) +); + + +-- +-- Name: notification_templates; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_templates ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid, + owner_id uuid, + key text NOT NULL, + domain text NOT NULL, + channel text NOT NULL, + event_type text NOT NULL, + body_text text NOT NULL, + meta_template_name text, + meta_template_namespace text, + meta_components jsonb, + meta_status text DEFAULT 'draft'::text, + variables jsonb DEFAULT '[]'::jsonb, + version integer DEFAULT 1 NOT NULL, + is_active boolean DEFAULT true NOT NULL, + is_default boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + CONSTRAINT notification_templates_channel_check CHECK ((channel = ANY (ARRAY['whatsapp'::text, 'sms'::text]))), + CONSTRAINT notification_templates_domain_check CHECK ((domain = ANY (ARRAY['session'::text, 'intake'::text, 'billing'::text, 'system'::text]))), + CONSTRAINT notification_templates_event_type_check CHECK ((event_type = ANY (ARRAY['lembrete_sessao'::text, 'confirmacao_sessao'::text, 'cancelamento_sessao'::text, 'reagendamento'::text, 'cobranca_pendente'::text, 'boas_vindas_paciente'::text, 'intake_recebido'::text, 'intake_aprovado'::text, 'intake_rejeitado'::text]))), + CONSTRAINT notification_templates_meta_status_check CHECK ((meta_status = ANY (ARRAY['draft'::text, 'pending_approval'::text, 'approved'::text, 'rejected'::text]))) +); + + +-- +-- Name: notifications; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notifications ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + type text NOT NULL, + ref_id uuid, + ref_table text, + payload jsonb DEFAULT '{}'::jsonb NOT NULL, + read_at timestamp with time zone, + archived boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT notifications_type_check CHECK ((type = ANY (ARRAY['new_scheduling'::text, 'new_patient'::text, 'recurrence_alert'::text, 'session_status'::text]))) +); + + +-- +-- Name: plan_features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_features ( + plan_id uuid NOT NULL, + feature_id uuid NOT NULL, + enabled boolean DEFAULT true NOT NULL, + limits jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_modules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_modules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + module_id uuid NOT NULL, + status text DEFAULT 'active'::text NOT NULL, + settings jsonb, + provider text DEFAULT 'manual'::text NOT NULL, + provider_item_id text, + installed_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: owner_feature_entitlements; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.owner_feature_entitlements AS + WITH base AS ( + SELECT s.user_id AS owner_id, + f.key AS feature_key, + pf.limits, + 'plan'::text AS source + FROM ((public.subscriptions s + JOIN public.plan_features pf ON (((pf.plan_id = s.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))) + WHERE ((s.status = 'active'::text) AND (s.user_id IS NOT NULL)) + UNION ALL + SELECT tm.owner_id, + f.key AS feature_key, + mf.limits, + 'module'::text AS source + FROM (((public.tenant_modules tm + JOIN public.modules m ON (((m.id = tm.module_id) AND (m.is_active = true)))) + JOIN public.module_features mf ON (((mf.module_id = m.id) AND (mf.enabled = true)))) + JOIN public.features f ON ((f.id = mf.feature_id))) + WHERE ((tm.status = 'active'::text) AND (tm.owner_id IS NOT NULL)) + ) + SELECT owner_id, + feature_key, + array_agg(DISTINCT source) AS sources, + jsonb_agg(limits) FILTER (WHERE (limits IS NOT NULL)) AS limits_list + FROM base + GROUP BY owner_id, feature_key; + + +-- +-- Name: owner_users; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.owner_users ( + owner_id uuid NOT NULL, + user_id uuid NOT NULL, + role text DEFAULT 'admin'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: patient_discounts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_discounts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + patient_id uuid NOT NULL, + discount_pct numeric(5,2) DEFAULT 0, + discount_flat numeric(10,2) DEFAULT 0, + reason text, + active boolean DEFAULT true NOT NULL, + active_from timestamp with time zone DEFAULT now(), + active_to timestamp with time zone, + created_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: patient_group_patient; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_group_patient ( + patient_group_id uuid NOT NULL, + patient_id uuid NOT NULL, + created_at timestamp with time zone DEFAULT now(), + tenant_id uuid NOT NULL +); + + +-- +-- Name: patient_groups; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_groups ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + nome text NOT NULL, + descricao text, + cor text, + is_active boolean DEFAULT true NOT NULL, + is_system boolean DEFAULT false NOT NULL, + owner_id uuid DEFAULT auth.uid() NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + therapist_id uuid, + tenant_id uuid NOT NULL +); + + +-- +-- Name: patient_intake_requests; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_intake_requests ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + token text NOT NULL, + consent boolean DEFAULT false NOT NULL, + status text DEFAULT 'new'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + converted_patient_id uuid, + rejected_reason text, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + cpf text, + rg text, + cep text, + nome_completo text, + email_principal text, + telefone text, + pais text, + cidade text, + estado text, + endereco text, + numero text, + bairro text, + complemento text, + data_nascimento date, + naturalidade text, + genero text, + estado_civil text, + onde_nos_conheceu text, + encaminhado_por text, + observacoes text, + notas_internas text, + email_alternativo text, + telefone_alternativo text, + profissao text, + escolaridade text, + nacionalidade text, + avatar_url text, + tenant_id uuid, + CONSTRAINT chk_intakes_status CHECK ((status = ANY (ARRAY['new'::text, 'converted'::text, 'rejected'::text]))) +); + + +-- +-- Name: patient_invites; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_invites ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + token text NOT NULL, + active boolean DEFAULT true NOT NULL, + expires_at timestamp with time zone, + max_uses integer, + uses integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid +); + + +-- +-- Name: patient_patient_tag; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_patient_tag ( + owner_id uuid NOT NULL, + patient_id uuid NOT NULL, + tag_id uuid NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL +); + + +-- +-- Name: patient_tags; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_tags ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + nome text NOT NULL, + cor text, + is_padrao boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone, + tenant_id uuid NOT NULL +); + + +-- +-- Name: patients; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patients ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + nome_completo text NOT NULL, + email_principal text, + telefone text, + created_at timestamp with time zone DEFAULT now(), + owner_id uuid, + avatar_url text, + status text DEFAULT 'Ativo'::text, + last_attended_at timestamp with time zone, + is_native boolean DEFAULT false, + naturalidade text, + data_nascimento date, + rg text, + cpf text, + identification_color text, + genero text, + estado_civil text, + email_alternativo text, + pais text DEFAULT 'Brasil'::text, + cep text, + cidade text, + estado text, + endereco text, + numero text, + bairro text, + complemento text, + escolaridade text, + profissao text, + nome_parente text, + grau_parentesco text, + telefone_alternativo text, + onde_nos_conheceu text, + encaminhado_por text, + nome_responsavel text, + telefone_responsavel text, + cpf_responsavel text, + observacao_responsavel text, + cobranca_no_responsavel boolean DEFAULT false, + observacoes text, + notas_internas text, + updated_at timestamp with time zone DEFAULT now(), + telefone_parente text, + tenant_id uuid NOT NULL, + responsible_member_id uuid NOT NULL, + user_id uuid, + patient_scope text DEFAULT 'clinic'::text NOT NULL, + therapist_member_id uuid, + CONSTRAINT cpf_responsavel_format_check CHECK (((cpf_responsavel IS NULL) OR (cpf_responsavel ~ '^\d{11}$'::text))), + CONSTRAINT patients_cpf_format_check CHECK (((cpf IS NULL) OR (cpf ~ '^\d{11}$'::text))), + CONSTRAINT patients_patient_scope_check CHECK ((patient_scope = ANY (ARRAY['clinic'::text, 'therapist'::text]))), + CONSTRAINT patients_status_check CHECK ((status = ANY (ARRAY['Ativo'::text, 'Inativo'::text, 'Alta'::text, 'Encaminhado'::text, 'Arquivado'::text]))), + CONSTRAINT patients_therapist_scope_consistency CHECK ((((patient_scope = 'clinic'::text) AND (therapist_member_id IS NULL)) OR ((patient_scope = 'therapist'::text) AND (therapist_member_id IS NOT NULL)))) +); + + +-- +-- Name: COLUMN patients.avatar_url; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.avatar_url IS 'URL p??blica da imagem de avatar armazenada no Supabase Storage'; + + +-- +-- Name: payment_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.payment_settings ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + pix_ativo boolean DEFAULT false NOT NULL, + pix_tipo text DEFAULT 'cpf'::text NOT NULL, + pix_chave text DEFAULT ''::text NOT NULL, + pix_nome_titular text DEFAULT ''::text NOT NULL, + deposito_ativo boolean DEFAULT false NOT NULL, + deposito_banco text DEFAULT ''::text NOT NULL, + deposito_agencia text DEFAULT ''::text NOT NULL, + deposito_conta text DEFAULT ''::text NOT NULL, + deposito_tipo_conta text DEFAULT 'corrente'::text NOT NULL, + deposito_titular text DEFAULT ''::text NOT NULL, + deposito_cpf_cnpj text DEFAULT ''::text NOT NULL, + dinheiro_ativo boolean DEFAULT false NOT NULL, + cartao_ativo boolean DEFAULT false NOT NULL, + cartao_instrucao text DEFAULT ''::text NOT NULL, + convenio_ativo boolean DEFAULT false NOT NULL, + convenio_lista text DEFAULT ''::text NOT NULL, + observacoes_pagamento text DEFAULT ''::text NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: plan_prices; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_prices ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + plan_id uuid NOT NULL, + currency text DEFAULT 'BRL'::text NOT NULL, + "interval" text NOT NULL, + amount_cents integer NOT NULL, + is_active boolean DEFAULT true NOT NULL, + active_from timestamp with time zone DEFAULT now() NOT NULL, + active_to timestamp with time zone, + source text DEFAULT 'manual'::text NOT NULL, + provider text, + provider_price_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT plan_prices_amount_cents_check CHECK ((amount_cents >= 0)), + CONSTRAINT plan_prices_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text]))) +); + + +-- +-- Name: TABLE plan_prices; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.plan_prices IS 'Hist??rico de pre??os por plano (fonte: manual/gateway).'; + + +-- +-- Name: plan_public; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_public ( + plan_id uuid NOT NULL, + public_name text DEFAULT ''::text NOT NULL, + public_description text DEFAULT ''::text NOT NULL, + badge text, + is_featured boolean DEFAULT false NOT NULL, + is_visible boolean DEFAULT true NOT NULL, + sort_order integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: TABLE plan_public; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.plan_public IS 'Configura????o de vitrine (p??gina p??blica) dos planos.'; + + +-- +-- Name: plan_public_bullets; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_public_bullets ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + plan_id uuid NOT NULL, + text text NOT NULL, + sort_order integer DEFAULT 0 NOT NULL, + highlight boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: plans; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plans ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + name text NOT NULL, + description text, + is_active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + price_cents integer DEFAULT 0 NOT NULL, + currency text DEFAULT 'BRL'::text NOT NULL, + billing_interval text DEFAULT 'month'::text NOT NULL, + target text, + max_supervisees integer, + CONSTRAINT plans_target_check CHECK ((target = ANY (ARRAY['patient'::text, 'therapist'::text, 'clinic'::text, 'supervisor'::text]))) +); + + +-- +-- Name: COLUMN plans.name; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.plans.name IS 'Nome interno do plano (admin). A key ?? t??cnica/imut??vel.'; + + +-- +-- Name: COLUMN plans.target; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.plans.target IS 'P??blico-alvo do plano: patient, therapist ou clinic.'; + + +-- +-- Name: COLUMN plans.max_supervisees; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.plans.max_supervisees IS 'Limite de terapeutas que podem ser supervisionados. Apenas para planos target=supervisor. NULL = sem limite.'; + + +-- +-- Name: professional_pricing; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.professional_pricing ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + determined_commitment_id uuid, + price numeric(10,2) NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: TABLE professional_pricing; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.professional_pricing IS 'DEPRECATED: substitu??da por public.services. Manter at?? pr??xima release de limpeza.'; + + +-- +-- Name: profiles; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.profiles ( + id uuid NOT NULL, + role text DEFAULT 'tenant_member'::text NOT NULL, + full_name text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + avatar_url text, + phone text, + bio text, + language text DEFAULT 'pt-BR'::text NOT NULL, + timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL, + notify_system_email boolean DEFAULT true NOT NULL, + notify_reminders boolean DEFAULT true NOT NULL, + notify_news boolean DEFAULT false NOT NULL, + account_type text DEFAULT 'free'::text NOT NULL, + platform_roles text[] DEFAULT '{}'::text[] NOT NULL, + nickname text, + work_description text, + work_description_other text, + site_url text, + social_instagram text, + social_youtube text, + social_facebook text, + social_x text, + social_custom jsonb DEFAULT '[]'::jsonb NOT NULL, + CONSTRAINT profiles_account_type_check CHECK ((account_type = ANY (ARRAY['free'::text, 'patient'::text, 'therapist'::text, 'clinic'::text]))), + CONSTRAINT profiles_role_check CHECK ((role = ANY (ARRAY['saas_admin'::text, 'tenant_member'::text, 'portal_user'::text, 'patient'::text]))) +); + + +-- +-- Name: COLUMN profiles.phone; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.phone IS 'WhatsApp / telefone no formato (99) 99999-9999'; + + +-- +-- Name: COLUMN profiles.bio; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.bio IS 'Breve apresenta????o p??blica (m??x 300 chars no front)'; + + +-- +-- Name: COLUMN profiles.account_type; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.account_type IS 'Tipo de conta: free=sem perfil ainda, patient=paciente (imut??vel), therapist=terapeuta (imut??vel), clinic=cl??nica (imut??vel).'; + + +-- +-- Name: COLUMN profiles.platform_roles; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.platform_roles IS 'Papéis globais de plataforma, independentes de tenant. Ex: editor de microlearning. Atribuído pelo saas_admin.'; + + +-- +-- Name: COLUMN profiles.nickname; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.nickname IS 'Apelido preferido para comunica????o interna (Ag??ncia PSI)'; + + +-- +-- Name: COLUMN profiles.work_description; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.work_description IS 'Categoria de trabalho selecionada no perfil p??blico'; + + +-- +-- Name: COLUMN profiles.work_description_other; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.work_description_other IS 'Descri????o livre quando work_description = ''outro'''; + + +-- +-- Name: COLUMN profiles.site_url; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.site_url IS 'Endere??o do site pessoal ou profissional'; + + +-- +-- Name: COLUMN profiles.social_instagram; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_instagram IS 'Handle ou URL do Instagram'; + + +-- +-- Name: COLUMN profiles.social_youtube; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_youtube IS 'Handle ou URL do canal no YouTube'; + + +-- +-- Name: COLUMN profiles.social_facebook; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_facebook IS 'Handle ou URL da p??gina no Facebook'; + + +-- +-- Name: COLUMN profiles.social_x; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_x IS 'Handle ou URL do perfil no X (Twitter)'; + + +-- +-- Name: COLUMN profiles.social_custom; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_custom IS 'Array JSON com redes adicionais livres: [{name, url}]'; + + +-- +-- Name: recurrence_exceptions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.recurrence_exceptions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + recurrence_id uuid NOT NULL, + tenant_id uuid NOT NULL, + original_date date NOT NULL, + type public.recurrence_exception_type NOT NULL, + new_date date, + new_start_time time without time zone, + new_end_time time without time zone, + modalidade text, + observacoes text, + titulo_custom text, + extra_fields jsonb, + reason text, + agenda_evento_id uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: recurrence_rule_services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.recurrence_rule_services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + rule_id uuid NOT NULL, + service_id uuid NOT NULL, + quantity integer DEFAULT 1 NOT NULL, + unit_price numeric(10,2) NOT NULL, + discount_pct numeric(5,2) DEFAULT 0, + discount_flat numeric(10,2) DEFAULT 0, + final_price numeric(10,2) NOT NULL, + created_at timestamp with time zone DEFAULT now(), + CONSTRAINT recurrence_rule_services_disc_flat_chk CHECK ((discount_flat >= (0)::numeric)), + CONSTRAINT recurrence_rule_services_disc_pct_chk CHECK (((discount_pct >= (0)::numeric) AND (discount_pct <= (100)::numeric))), + CONSTRAINT recurrence_rule_services_final_price_chk CHECK ((final_price >= (0)::numeric)), + CONSTRAINT recurrence_rule_services_quantity_chk CHECK ((quantity > 0)) +); + + +-- +-- Name: recurrence_rules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.recurrence_rules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + therapist_id uuid, + patient_id uuid, + determined_commitment_id uuid, + type public.recurrence_type DEFAULT 'weekly'::public.recurrence_type NOT NULL, + "interval" smallint DEFAULT 1 NOT NULL, + weekdays smallint[] DEFAULT '{}'::smallint[] NOT NULL, + start_time time without time zone NOT NULL, + end_time time without time zone NOT NULL, + timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL, + duration_min smallint DEFAULT 50 NOT NULL, + start_date date NOT NULL, + end_date date, + max_occurrences integer, + open_ended boolean DEFAULT true NOT NULL, + modalidade text DEFAULT 'presencial'::text, + titulo_custom text, + observacoes text, + extra_fields jsonb, + status text DEFAULT 'ativo'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + price numeric(10,2), + insurance_plan_id uuid, + insurance_guide_number text, + insurance_value numeric(10,2), + insurance_plan_service_id uuid, + CONSTRAINT recurrence_rules_dates_chk CHECK (((end_date IS NULL) OR (end_date >= start_date))), + CONSTRAINT recurrence_rules_interval_chk CHECK (("interval" >= 1)), + CONSTRAINT recurrence_rules_status_check CHECK ((status = ANY (ARRAY['ativo'::text, 'pausado'::text, 'cancelado'::text]))), + CONSTRAINT recurrence_rules_times_chk CHECK ((end_time > start_time)) +); + + +-- +-- Name: saas_admins; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_admins ( + user_id uuid NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: saas_doc_votos; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_doc_votos ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + doc_id uuid NOT NULL, + user_id uuid NOT NULL, + util boolean NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: saas_docs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_docs ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + titulo text NOT NULL, + conteudo text DEFAULT ''::text NOT NULL, + medias jsonb DEFAULT '[]'::jsonb NOT NULL, + tipo_acesso text DEFAULT 'usuario'::text NOT NULL, + pagina_path text NOT NULL, + docs_relacionados uuid[] DEFAULT '{}'::uuid[] NOT NULL, + ativo boolean DEFAULT true NOT NULL, + ordem integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + categoria text, + exibir_no_faq boolean DEFAULT false NOT NULL, + votos_util integer DEFAULT 0 NOT NULL, + votos_nao_util integer DEFAULT 0 NOT NULL, + CONSTRAINT saas_docs_tipo_acesso_check CHECK ((tipo_acesso = ANY (ARRAY['admin'::text, 'usuario'::text]))) +); + + +-- +-- Name: COLUMN saas_docs.categoria; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.saas_docs.categoria IS 'Agrupa docs no portal FAQ (ex: Conta, Agenda, Pagamentos)'; + + +-- +-- Name: COLUMN saas_docs.exibir_no_faq; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.saas_docs.exibir_no_faq IS 'Se true, a doc e seus itens FAQ aparecem no portal de FAQ'; + + +-- +-- Name: saas_faq; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_faq ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + pergunta text NOT NULL, + categoria text, + publico boolean DEFAULT false NOT NULL, + votos integer DEFAULT 0 NOT NULL, + titulo text, + conteudo text, + tipo_acesso text DEFAULT 'usuario'::text NOT NULL, + pagina_path text NOT NULL, + pagina_label text, + medias jsonb DEFAULT '[]'::jsonb NOT NULL, + faqs_relacionados uuid[] DEFAULT '{}'::uuid[] NOT NULL, + ativo boolean DEFAULT true NOT NULL, + ordem integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: saas_faq_itens; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_faq_itens ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + doc_id uuid NOT NULL, + pergunta text NOT NULL, + resposta text, + ordem integer DEFAULT 0 NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: TABLE saas_faq_itens; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.saas_faq_itens IS 'Pares pergunta/resposta vinculados a um documento de ajuda'; + + +-- +-- Name: services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + name text NOT NULL, + description text, + price numeric(10,2) NOT NULL, + duration_min integer, + active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: subscription_events; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_events ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + subscription_id uuid NOT NULL, + owner_id uuid NOT NULL, + event_type text NOT NULL, + old_plan_id uuid, + new_plan_id uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL, + created_by uuid, + source text DEFAULT 'admin_ui'::text, + reason text, + metadata jsonb, + owner_type text NOT NULL, + owner_ref uuid NOT NULL, + CONSTRAINT subscription_events_owner_ref_consistency_chk CHECK ((owner_id = owner_ref)), + CONSTRAINT subscription_events_owner_type_chk CHECK (((owner_type IS NULL) OR (owner_type = ANY (ARRAY['clinic'::text, 'therapist'::text])))) +); + + +-- +-- Name: subscription_intents_personal; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_intents_personal ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid NOT NULL, + created_by_user_id uuid, + email text NOT NULL, + plan_id uuid NOT NULL, + plan_key text, + "interval" text, + amount_cents integer, + currency text, + status text DEFAULT 'new'::text NOT NULL, + source text DEFAULT 'manual'::text NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + paid_at timestamp with time zone, + subscription_id uuid, + CONSTRAINT sint_personal_interval_check CHECK ((("interval" IS NULL) OR ("interval" = ANY (ARRAY['month'::text, 'year'::text])))), + CONSTRAINT sint_personal_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text]))) +); + + +-- +-- Name: subscription_intents_tenant; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_intents_tenant ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid NOT NULL, + created_by_user_id uuid, + email text NOT NULL, + plan_id uuid NOT NULL, + plan_key text, + "interval" text, + amount_cents integer, + currency text, + status text DEFAULT 'new'::text NOT NULL, + source text DEFAULT 'manual'::text NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + paid_at timestamp with time zone, + tenant_id uuid NOT NULL, + subscription_id uuid, + CONSTRAINT sint_tenant_interval_check CHECK ((("interval" IS NULL) OR ("interval" = ANY (ARRAY['month'::text, 'year'::text])))), + CONSTRAINT sint_tenant_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text]))) +); + + +-- +-- Name: subscription_intents; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.subscription_intents AS + SELECT t.id, + t.user_id, + t.created_by_user_id, + t.email, + t.plan_id, + t.plan_key, + t."interval", + t.amount_cents, + t.currency, + t.status, + t.source, + t.notes, + t.created_at, + t.paid_at, + t.tenant_id, + t.subscription_id, + 'clinic'::text AS plan_target + FROM public.subscription_intents_tenant t +UNION ALL + SELECT p.id, + p.user_id, + p.created_by_user_id, + p.email, + p.plan_id, + p.plan_key, + p."interval", + p.amount_cents, + p.currency, + p.status, + p.source, + p.notes, + p.created_at, + p.paid_at, + NULL::uuid AS tenant_id, + p.subscription_id, + 'therapist'::text AS plan_target + FROM public.subscription_intents_personal p; + + +-- +-- Name: subscription_intents_legacy; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_intents_legacy ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid, + email text, + plan_key text NOT NULL, + "interval" text NOT NULL, + amount_cents integer NOT NULL, + currency text DEFAULT 'BRL'::text NOT NULL, + status text DEFAULT 'new'::text NOT NULL, + source text DEFAULT 'landing'::text NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + paid_at timestamp with time zone, + tenant_id uuid NOT NULL, + created_by_user_id uuid, + CONSTRAINT subscription_intents_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text]))), + CONSTRAINT subscription_intents_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text]))) +); + + +-- +-- Name: support_sessions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.support_sessions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + admin_id uuid NOT NULL, + token text DEFAULT encode(extensions.gen_random_bytes(32), 'hex'::text) NOT NULL, + expires_at timestamp with time zone DEFAULT (now() + '01:00:00'::interval) NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_feature_exceptions_log; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_feature_exceptions_log ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + feature_key text NOT NULL, + enabled boolean NOT NULL, + reason text, + created_by uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_features ( + tenant_id uuid NOT NULL, + feature_key text NOT NULL, + enabled boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_invites; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_invites ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + email text NOT NULL, + role text NOT NULL, + token uuid DEFAULT gen_random_uuid() NOT NULL, + invited_by uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL, + expires_at timestamp with time zone DEFAULT (now() + '7 days'::interval) NOT NULL, + accepted_at timestamp with time zone, + accepted_by uuid, + revoked_at timestamp with time zone, + revoked_by uuid, + CONSTRAINT tenant_invites_role_check CHECK ((role = ANY (ARRAY['therapist'::text, 'secretary'::text]))) +); + + +-- +-- Name: tenants; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenants ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + name text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + kind text DEFAULT 'saas'::text NOT NULL, + CONSTRAINT tenants_kind_check CHECK ((kind = ANY (ARRAY['therapist'::text, 'clinic_coworking'::text, 'clinic_reception'::text, 'clinic_full'::text, 'clinic'::text, 'saas'::text, 'supervisor'::text]))) +); + + +-- +-- Name: COLUMN tenants.kind; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.tenants.kind IS 'Tipo do tenant. Imut??vel ap??s cria????o. therapist=terapeuta solo. clinic_coworking/clinic_reception/clinic_full=cl??nicas. clinic e saas s??o legados.'; + + +-- +-- Name: therapist_payout_records; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.therapist_payout_records ( + payout_id uuid NOT NULL, + financial_record_id uuid NOT NULL +); + + +-- +-- Name: twilio_subaccount_usage; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.twilio_subaccount_usage ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + channel_id uuid NOT NULL, + twilio_subaccount_sid text NOT NULL, + period_start date NOT NULL, + period_end date NOT NULL, + messages_sent integer DEFAULT 0 NOT NULL, + messages_delivered integer DEFAULT 0 NOT NULL, + messages_failed integer DEFAULT 0 NOT NULL, + cost_usd numeric(12,6) DEFAULT 0 NOT NULL, + cost_brl numeric(12,4) DEFAULT 0 NOT NULL, + revenue_brl numeric(12,4) DEFAULT 0 NOT NULL, + margin_brl numeric(12,4) GENERATED ALWAYS AS ((revenue_brl - cost_brl)) STORED, + usd_brl_rate numeric(8,4) DEFAULT 0, + synced_at timestamp with time zone DEFAULT now(), + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT twilio_subaccount_usage_period_check CHECK ((period_end >= period_start)) +); + + +-- +-- Name: TABLE twilio_subaccount_usage; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.twilio_subaccount_usage IS 'Consumo mensal de mensagens WhatsApp por subconta Twilio. Sincronizado via Edge Function.'; + + +-- +-- Name: user_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.user_settings ( + user_id uuid NOT NULL, + theme_mode text DEFAULT 'dark'::text NOT NULL, + preset text DEFAULT 'Aura'::text NOT NULL, + primary_color text DEFAULT 'noir'::text NOT NULL, + surface_color text DEFAULT 'slate'::text NOT NULL, + menu_mode text DEFAULT 'static'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + layout_variant text DEFAULT 'classic'::text NOT NULL, + CONSTRAINT user_settings_layout_variant_check CHECK ((layout_variant = ANY (ARRAY['classic'::text, 'rail'::text]))), + CONSTRAINT user_settings_menu_mode_check CHECK ((menu_mode = ANY (ARRAY['static'::text, 'overlay'::text]))), + CONSTRAINT user_settings_theme_mode_check CHECK ((theme_mode = ANY (ARRAY['light'::text, 'dark'::text]))) +); + + +-- +-- Name: TABLE user_settings; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.user_settings IS 'Prefer??ncias de apar??ncia e layout por usu??rio'; + + +-- +-- Name: COLUMN user_settings.user_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.user_id IS 'FK = auth.users.id ??? um registro por usu??rio'; + + +-- +-- Name: COLUMN user_settings.theme_mode; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.theme_mode IS 'light | dark'; + + +-- +-- Name: COLUMN user_settings.preset; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.preset IS 'Preset PrimeVue: Aura | Lara | Nora'; + + +-- +-- Name: COLUMN user_settings.primary_color; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.primary_color IS 'Nome da cor prim??ria (ex: blue, emerald, noir)'; + + +-- +-- Name: COLUMN user_settings.surface_color; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.surface_color IS 'Nome da surface (ex: slate, zinc, neutral)'; + + +-- +-- Name: COLUMN user_settings.menu_mode; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.menu_mode IS 'static | overlay'; + + +-- +-- Name: COLUMN user_settings.layout_variant; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.layout_variant IS 'classic (sidebar) | rail (mini rail + painel)'; + + +-- +-- Name: v_auth_users_public; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_auth_users_public AS + SELECT id AS user_id, + email, + created_at, + last_sign_in_at + FROM auth.users u; + + +-- +-- Name: v_cashflow_projection; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_cashflow_projection WITH (security_invoker='on') AS + SELECT gs.mes, + to_char(gs.mes, 'YYYY-MM'::text) AS mes_label, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric) AS receitas_projetadas, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric) AS despesas_projetadas, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = 'pending'::text))), (0)::numeric) AS receitas_pendentes, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = 'overdue'::text))), (0)::numeric) AS receitas_vencidas, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = 'pending'::text))), (0)::numeric) AS despesas_pendentes, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = 'overdue'::text))), (0)::numeric) AS despesas_vencidas, + (COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric) - COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric)) AS saldo_projetado, + count(fr.id) FILTER (WHERE (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text]))) AS count_registros + FROM (generate_series(((date_trunc('month'::text, (CURRENT_DATE)::timestamp with time zone))::date)::timestamp with time zone, (((date_trunc('month'::text, (CURRENT_DATE)::timestamp with time zone) + '5 mons'::interval))::date)::timestamp with time zone, '1 mon'::interval) gs(mes) + LEFT JOIN public.financial_records fr ON (((fr.deleted_at IS NULL) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])) AND ((date_trunc('month'::text, (fr.due_date)::timestamp with time zone))::date = gs.mes)))) + GROUP BY gs.mes + ORDER BY gs.mes; + + +-- +-- Name: VIEW v_cashflow_projection; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.v_cashflow_projection IS 'Fluxo de caixa projetado: pr??ximos 6 meses com totais de pending+overdue por due_date. Usa security_invoker=on ??? filtra automaticamente pelo auth.uid() via RLS de financial_records.'; + + +-- +-- Name: v_commitment_totals; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_commitment_totals AS + SELECT c.tenant_id, + c.id AS commitment_id, + (COALESCE(sum(l.minutes), (0)::bigint))::integer AS total_minutes + FROM (public.determined_commitments c + LEFT JOIN public.commitment_time_logs l ON ((l.commitment_id = c.id))) + GROUP BY c.tenant_id, c.id; + + +-- +-- Name: v_patient_groups_with_counts; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_patient_groups_with_counts AS + SELECT pg.id, + pg.nome, + pg.cor, + pg.owner_id, + pg.is_system, + pg.is_active, + pg.created_at, + pg.updated_at, + (COALESCE(count(pgp.patient_id), (0)::bigint))::integer AS patients_count + FROM (public.patient_groups pg + LEFT JOIN public.patient_group_patient pgp ON ((pgp.patient_group_id = pg.id))) + GROUP BY pg.id, pg.nome, pg.cor, pg.owner_id, pg.is_system, pg.is_active, pg.created_at, pg.updated_at; + + +-- +-- Name: v_plan_active_prices; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_plan_active_prices AS + SELECT plan_id, + max( + CASE + WHEN (("interval" = 'month'::text) AND is_active) THEN amount_cents + ELSE NULL::integer + END) AS monthly_cents, + max( + CASE + WHEN (("interval" = 'year'::text) AND is_active) THEN amount_cents + ELSE NULL::integer + END) AS yearly_cents, + max( + CASE + WHEN (("interval" = 'month'::text) AND is_active) THEN currency + ELSE NULL::text + END) AS monthly_currency, + max( + CASE + WHEN (("interval" = 'year'::text) AND is_active) THEN currency + ELSE NULL::text + END) AS yearly_currency + FROM public.plan_prices + GROUP BY plan_id; + + +-- +-- Name: v_public_pricing; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_public_pricing AS + SELECT p.id AS plan_id, + p.key AS plan_key, + p.name AS plan_name, + COALESCE(pp.public_name, ''::text) AS public_name, + COALESCE(pp.public_description, ''::text) AS public_description, + pp.badge, + COALESCE(pp.is_featured, false) AS is_featured, + COALESCE(pp.is_visible, true) AS is_visible, + COALESCE(pp.sort_order, 0) AS sort_order, + ap.monthly_cents, + ap.yearly_cents, + ap.monthly_currency, + ap.yearly_currency, + COALESCE(( SELECT jsonb_agg(jsonb_build_object('id', b.id, 'text', b.text, 'highlight', b.highlight, 'sort_order', b.sort_order) ORDER BY b.sort_order, b.created_at) AS jsonb_agg + FROM public.plan_public_bullets b + WHERE (b.plan_id = p.id)), '[]'::jsonb) AS bullets, + p.target AS plan_target + FROM ((public.plans p + LEFT JOIN public.plan_public pp ON ((pp.plan_id = p.id))) + LEFT JOIN public.v_plan_active_prices ap ON ((ap.plan_id = p.id))) + ORDER BY COALESCE(pp.sort_order, 0), p.key; + + +-- +-- Name: v_subscription_feature_mismatch; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_subscription_feature_mismatch AS + WITH expected AS ( + SELECT s.user_id AS owner_id, + f.key AS feature_key + FROM ((public.subscriptions s + JOIN public.plan_features pf ON (((pf.plan_id = s.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))) + WHERE ((s.status = 'active'::text) AND (s.tenant_id IS NULL) AND (s.user_id IS NOT NULL)) + ), actual AS ( + SELECT e.owner_id, + e.feature_key + FROM public.owner_feature_entitlements e + ) + SELECT COALESCE(expected.owner_id, actual.owner_id) AS owner_id, + COALESCE(expected.feature_key, actual.feature_key) AS feature_key, + CASE + WHEN ((expected.feature_key IS NOT NULL) AND (actual.feature_key IS NULL)) THEN 'missing_entitlement'::text + WHEN ((expected.feature_key IS NULL) AND (actual.feature_key IS NOT NULL)) THEN 'unexpected_entitlement'::text + ELSE NULL::text + END AS mismatch_type + FROM (expected + FULL JOIN actual ON (((expected.owner_id = actual.owner_id) AND (expected.feature_key = actual.feature_key)))) + WHERE ((expected.feature_key IS NULL) OR (actual.feature_key IS NULL)); + + +-- +-- Name: v_subscription_health; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_subscription_health AS + SELECT s.id AS subscription_id, + s.user_id AS owner_id, + s.status, + s.plan_id, + p.key AS plan_key, + s.current_period_start, + s.current_period_end, + s.updated_at, + CASE + WHEN (s.plan_id IS NULL) THEN 'missing_plan'::text + WHEN (p.id IS NULL) THEN 'invalid_plan'::text + WHEN ((s.status = 'active'::text) AND (s.current_period_end IS NOT NULL) AND (s.current_period_end < now())) THEN 'expired_but_active'::text + WHEN ((s.status = 'canceled'::text) AND (s.current_period_end > now())) THEN 'canceled_but_still_in_period'::text + ELSE 'ok'::text + END AS health_status, + CASE + WHEN (s.tenant_id IS NOT NULL) THEN 'clinic'::text + ELSE 'therapist'::text + END AS owner_type, + COALESCE(s.tenant_id, s.user_id) AS owner_ref + FROM (public.subscriptions s + LEFT JOIN public.plans p ON ((p.id = s.plan_id))); + + +-- +-- Name: v_subscription_health_v2; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_subscription_health_v2 AS + SELECT s.id AS subscription_id, + s.user_id AS owner_id, + CASE + WHEN (s.tenant_id IS NOT NULL) THEN 'clinic'::text + ELSE 'therapist'::text + END AS owner_type, + COALESCE(s.tenant_id, s.user_id) AS owner_ref, + s.status, + s.plan_id, + p.key AS plan_key, + s.current_period_start, + s.current_period_end, + s.updated_at, + CASE + WHEN (s.plan_id IS NULL) THEN 'missing_plan'::text + WHEN (p.id IS NULL) THEN 'invalid_plan'::text + WHEN ((s.status = 'active'::text) AND (s.current_period_end IS NOT NULL) AND (s.current_period_end < now())) THEN 'expired_but_active'::text + WHEN ((s.status = 'canceled'::text) AND (s.current_period_end > now())) THEN 'canceled_but_still_in_period'::text + ELSE 'ok'::text + END AS health_status + FROM (public.subscriptions s + LEFT JOIN public.plans p ON ((p.id = s.plan_id))); + + +-- +-- Name: v_tag_patient_counts; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tag_patient_counts AS + SELECT t.id, + t.owner_id, + t.nome, + t.cor, + t.is_padrao, + t.created_at, + t.updated_at, + (COALESCE(count(ppt.patient_id), (0)::bigint))::integer AS pacientes_count, + (COALESCE(count(ppt.patient_id), (0)::bigint))::integer AS patient_count + FROM (public.patient_tags t + LEFT JOIN public.patient_patient_tag ppt ON (((ppt.tag_id = t.id) AND (ppt.owner_id = t.owner_id)))) + GROUP BY t.id, t.owner_id, t.nome, t.cor, t.is_padrao, t.created_at, t.updated_at; + + +-- +-- Name: v_tenant_active_subscription; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_active_subscription AS + SELECT DISTINCT ON (tenant_id) tenant_id, + plan_id, + plan_key, + "interval", + status, + current_period_start, + current_period_end, + created_at + FROM public.subscriptions s + WHERE ((tenant_id IS NOT NULL) AND (status = 'active'::text) AND ((current_period_end IS NULL) OR (current_period_end > now()))) + ORDER BY tenant_id, created_at DESC; + + +-- +-- Name: v_tenant_entitlements; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_entitlements AS + SELECT a.tenant_id, + f.key AS feature_key, + true AS allowed + FROM ((public.v_tenant_active_subscription a + JOIN public.plan_features pf ON (((pf.plan_id = a.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))); + + +-- +-- Name: v_tenant_entitlements_full; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_entitlements_full AS + SELECT a.tenant_id, + f.key AS feature_key, + (pf.enabled = true) AS allowed, + pf.limits, + a.plan_id, + p.key AS plan_key + FROM (((public.v_tenant_active_subscription a + JOIN public.plan_features pf ON ((pf.plan_id = a.plan_id))) + JOIN public.features f ON ((f.id = pf.feature_id))) + JOIN public.plans p ON ((p.id = a.plan_id))); + + +-- +-- Name: v_tenant_entitlements_json; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_entitlements_json AS + SELECT tenant_id, + max(plan_key) AS plan_key, + jsonb_object_agg(feature_key, jsonb_build_object('allowed', allowed, 'limits', COALESCE(limits, '{}'::jsonb)) ORDER BY feature_key) AS entitlements + FROM public.v_tenant_entitlements_full + GROUP BY tenant_id; + + +-- +-- Name: v_tenant_feature_exceptions; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_feature_exceptions AS + SELECT tf.tenant_id, + a.plan_key, + tf.feature_key, + 'commercial_exception'::text AS exception_type + FROM ((public.tenant_features tf + JOIN public.v_tenant_active_subscription a ON ((a.tenant_id = tf.tenant_id))) + LEFT JOIN public.v_tenant_entitlements_full v ON (((v.tenant_id = tf.tenant_id) AND (v.feature_key = tf.feature_key)))) + WHERE ((tf.enabled = true) AND (COALESCE(v.allowed, false) = false)); + + +-- +-- Name: v_tenant_feature_mismatch; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_feature_mismatch AS + WITH plan_allowed AS ( + SELECT v.tenant_id, + v.feature_key, + v.allowed + FROM public.v_tenant_entitlements_full v + ), overrides AS ( + SELECT tf.tenant_id, + tf.feature_key, + tf.enabled + FROM public.tenant_features tf + ) + SELECT o.tenant_id, + o.feature_key, + CASE + WHEN ((o.enabled = true) AND (COALESCE(p.allowed, false) = false)) THEN 'unexpected_override'::text + ELSE NULL::text + END AS mismatch_type + FROM (overrides o + LEFT JOIN plan_allowed p ON (((p.tenant_id = o.tenant_id) AND (p.feature_key = o.feature_key)))) + WHERE ((o.enabled = true) AND (COALESCE(p.allowed, false) = false)); + + +-- +-- Name: v_tenant_members_with_profiles; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_members_with_profiles AS + SELECT tm.id AS tenant_member_id, + tm.tenant_id, + tm.user_id, + tm.role, + tm.status, + tm.created_at, + p.full_name, + au.email + FROM ((public.tenant_members tm + LEFT JOIN public.profiles p ON ((p.id = tm.user_id))) + LEFT JOIN auth.users au ON ((au.id = tm.user_id))); + + +-- +-- Name: v_tenant_people; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_people AS + SELECT 'member'::text AS type, + m.tenant_id, + m.user_id, + u.email, + m.role, + m.status, + NULL::uuid AS invite_token, + NULL::timestamp with time zone AS expires_at + FROM (public.tenant_members m + JOIN auth.users u ON ((u.id = m.user_id))) +UNION ALL + SELECT 'invite'::text AS type, + i.tenant_id, + NULL::uuid AS user_id, + i.email, + i.role, + 'invited'::text AS status, + i.token AS invite_token, + i.expires_at + FROM public.tenant_invites i + WHERE ((i.accepted_at IS NULL) AND (i.revoked_at IS NULL)); + + +-- +-- Name: v_tenant_staff; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_staff AS + SELECT ('m_'::text || (tm.id)::text) AS row_id, + tm.tenant_id, + tm.user_id, + tm.role, + tm.status, + tm.created_at, + p.full_name, + au.email, + NULL::uuid AS invite_token + FROM ((public.tenant_members tm + LEFT JOIN public.profiles p ON ((p.id = tm.user_id))) + LEFT JOIN auth.users au ON ((au.id = tm.user_id))) +UNION ALL + SELECT ('i_'::text || (ti.id)::text) AS row_id, + ti.tenant_id, + NULL::uuid AS user_id, + ti.role, + 'invited'::text AS status, + ti.created_at, + NULL::text AS full_name, + ti.email, + ti.token AS invite_token + FROM public.tenant_invites ti + WHERE ((ti.accepted_at IS NULL) AND (ti.revoked_at IS NULL) AND (ti.expires_at > now())); + + +-- +-- Name: v_twilio_whatsapp_overview; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_twilio_whatsapp_overview AS + SELECT nc.id AS channel_id, + nc.tenant_id, + nc.owner_id, + nc.is_active, + nc.connection_status, + nc.display_name, + nc.twilio_subaccount_sid, + nc.twilio_phone_number, + nc.twilio_phone_sid, + nc.cost_per_message_usd, + nc.price_per_message_brl, + nc.provisioned_at, + nc.created_at, + nc.updated_at, + COALESCE(u.messages_sent, 0) AS current_month_sent, + COALESCE(u.messages_delivered, 0) AS current_month_delivered, + COALESCE(u.messages_failed, 0) AS current_month_failed, + COALESCE(u.cost_usd, (0)::numeric) AS current_month_cost_usd, + COALESCE(u.cost_brl, (0)::numeric) AS current_month_cost_brl, + COALESCE(u.revenue_brl, (0)::numeric) AS current_month_revenue_brl, + COALESCE(u.margin_brl, (0)::numeric) AS current_month_margin_brl + FROM (public.notification_channels nc + LEFT JOIN public.twilio_subaccount_usage u ON (((u.channel_id = nc.id) AND (u.period_start = (date_trunc('month'::text, (CURRENT_DATE)::timestamp with time zone))::date)))) + WHERE ((nc.channel = 'whatsapp'::text) AND (nc.provider = 'twilio'::text) AND (nc.deleted_at IS NULL)); + + +-- +-- Name: VIEW v_twilio_whatsapp_overview; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.v_twilio_whatsapp_overview IS 'Visão consolidada de subcontas Twilio WhatsApp com uso do mês corrente.'; + + +-- +-- Name: v_user_active_subscription; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_user_active_subscription AS + SELECT DISTINCT ON (user_id) user_id, + plan_id, + plan_key, + "interval", + status, + current_period_start, + current_period_end, + created_at + FROM public.subscriptions s + WHERE ((tenant_id IS NULL) AND (user_id IS NOT NULL) AND (status = 'active'::text) AND ((current_period_end IS NULL) OR (current_period_end > now()))) + ORDER BY user_id, created_at DESC; + + +-- +-- Name: v_user_entitlements; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_user_entitlements AS + SELECT a.user_id, + f.key AS feature_key, + true AS allowed + FROM ((public.v_user_active_subscription a + JOIN public.plan_features pf ON (((pf.plan_id = a.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))); + + +-- +-- Name: messages; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +) +PARTITION BY RANGE (inserted_at); + + +-- +-- Name: messages_2026_03_24; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_24 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_25; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_25 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_26; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_26 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_27; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_27 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_28; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_28 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_29; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_29 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_30; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_30 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: schema_migrations; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.schema_migrations ( + version bigint NOT NULL, + inserted_at timestamp(0) without time zone +); + + +-- +-- Name: subscription; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.subscription ( + id bigint NOT NULL, + subscription_id uuid NOT NULL, + entity regclass NOT NULL, + filters realtime.user_defined_filter[] DEFAULT '{}'::realtime.user_defined_filter[] NOT NULL, + claims jsonb NOT NULL, + claims_role regrole GENERATED ALWAYS AS (realtime.to_regrole((claims ->> 'role'::text))) STORED NOT NULL, + created_at timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL +); + + +-- +-- Name: subscription_id_seq; Type: SEQUENCE; Schema: realtime; Owner: - +-- + +ALTER TABLE realtime.subscription ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY ( + SEQUENCE NAME realtime.subscription_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: buckets; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.buckets ( + id text NOT NULL, + name text NOT NULL, + owner uuid, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + public boolean DEFAULT false, + avif_autodetection boolean DEFAULT false, + file_size_limit bigint, + allowed_mime_types text[], + owner_id text, + type storage.buckettype DEFAULT 'STANDARD'::storage.buckettype NOT NULL +); + + +-- +-- Name: COLUMN buckets.owner; Type: COMMENT; Schema: storage; Owner: - +-- + +COMMENT ON COLUMN storage.buckets.owner IS 'Field is deprecated, use owner_id instead'; + + +-- +-- Name: buckets_analytics; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.buckets_analytics ( + name text NOT NULL, + type storage.buckettype DEFAULT 'ANALYTICS'::storage.buckettype NOT NULL, + format text DEFAULT 'ICEBERG'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL, + deleted_at timestamp with time zone +); + + +-- +-- Name: buckets_vectors; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.buckets_vectors ( + id text NOT NULL, + type storage.buckettype DEFAULT 'VECTOR'::storage.buckettype NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: iceberg_namespaces; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.iceberg_namespaces ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + bucket_name text NOT NULL, + name text NOT NULL COLLATE pg_catalog."C", + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + metadata jsonb DEFAULT '{}'::jsonb NOT NULL, + catalog_id uuid NOT NULL +); + + +-- +-- Name: iceberg_tables; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.iceberg_tables ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + namespace_id uuid NOT NULL, + bucket_name text NOT NULL, + name text NOT NULL COLLATE pg_catalog."C", + location text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + remote_table_id text, + shard_key text, + shard_id text, + catalog_id uuid NOT NULL +); + + +-- +-- Name: migrations; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.migrations ( + id integer NOT NULL, + name character varying(100) NOT NULL, + hash character varying(40) NOT NULL, + executed_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP +); + + +-- +-- Name: objects; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.objects ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + bucket_id text, + name text, + owner uuid, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + last_accessed_at timestamp with time zone DEFAULT now(), + metadata jsonb, + path_tokens text[] GENERATED ALWAYS AS (string_to_array(name, '/'::text)) STORED, + version text, + owner_id text, + user_metadata jsonb +); + + +-- +-- Name: COLUMN objects.owner; Type: COMMENT; Schema: storage; Owner: - +-- + +COMMENT ON COLUMN storage.objects.owner IS 'Field is deprecated, use owner_id instead'; + + +-- +-- Name: s3_multipart_uploads; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.s3_multipart_uploads ( + id text NOT NULL, + in_progress_size bigint DEFAULT 0 NOT NULL, + upload_signature text NOT NULL, + bucket_id text NOT NULL, + key text NOT NULL COLLATE pg_catalog."C", + version text NOT NULL, + owner_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + user_metadata jsonb +); + + +-- +-- Name: s3_multipart_uploads_parts; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.s3_multipart_uploads_parts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + upload_id text NOT NULL, + size bigint DEFAULT 0 NOT NULL, + part_number integer NOT NULL, + bucket_id text NOT NULL, + key text NOT NULL COLLATE pg_catalog."C", + etag text NOT NULL, + owner_id text, + version text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: vector_indexes; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.vector_indexes ( + id text DEFAULT gen_random_uuid() NOT NULL, + name text NOT NULL COLLATE pg_catalog."C", + bucket_id text NOT NULL, + data_type text NOT NULL, + dimension integer NOT NULL, + distance_metric text NOT NULL, + metadata_configuration jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: hooks; Type: TABLE; Schema: supabase_functions; Owner: - +-- + +CREATE TABLE supabase_functions.hooks ( + id bigint NOT NULL, + hook_table_id integer NOT NULL, + hook_name text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + request_id bigint +); + + +-- +-- Name: TABLE hooks; Type: COMMENT; Schema: supabase_functions; Owner: - +-- + +COMMENT ON TABLE supabase_functions.hooks IS 'Supabase Functions Hooks: Audit trail for triggered hooks.'; + + +-- +-- Name: hooks_id_seq; Type: SEQUENCE; Schema: supabase_functions; Owner: - +-- + +CREATE SEQUENCE supabase_functions.hooks_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: hooks_id_seq; Type: SEQUENCE OWNED BY; Schema: supabase_functions; Owner: - +-- + +ALTER SEQUENCE supabase_functions.hooks_id_seq OWNED BY supabase_functions.hooks.id; + + +-- +-- Name: migrations; Type: TABLE; Schema: supabase_functions; Owner: - +-- + +CREATE TABLE supabase_functions.migrations ( + version text NOT NULL, + inserted_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: messages_2026_03_24; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_24 FOR VALUES FROM ('2026-03-24 00:00:00') TO ('2026-03-25 00:00:00'); + + +-- +-- Name: messages_2026_03_25; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_25 FOR VALUES FROM ('2026-03-25 00:00:00') TO ('2026-03-26 00:00:00'); + + +-- +-- Name: messages_2026_03_26; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_26 FOR VALUES FROM ('2026-03-26 00:00:00') TO ('2026-03-27 00:00:00'); + + +-- +-- Name: messages_2026_03_27; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_27 FOR VALUES FROM ('2026-03-27 00:00:00') TO ('2026-03-28 00:00:00'); + + +-- +-- Name: messages_2026_03_28; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_28 FOR VALUES FROM ('2026-03-28 00:00:00') TO ('2026-03-29 00:00:00'); + + +-- +-- Name: messages_2026_03_29; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_29 FOR VALUES FROM ('2026-03-29 00:00:00') TO ('2026-03-30 00:00:00'); + + +-- +-- Name: messages_2026_03_30; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_30 FOR VALUES FROM ('2026-03-30 00:00:00') TO ('2026-03-31 00:00:00'); + + +-- +-- Name: refresh_tokens id; Type: DEFAULT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens ALTER COLUMN id SET DEFAULT nextval('auth.refresh_tokens_id_seq'::regclass); + + +-- +-- Name: _db_migrations id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._db_migrations ALTER COLUMN id SET DEFAULT nextval('public._db_migrations_id_seq'::regclass); + + +-- +-- Name: agenda_online_slots id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots ALTER COLUMN id SET DEFAULT nextval('public.agenda_online_slots_id_seq'::regclass); + + +-- +-- Name: hooks id; Type: DEFAULT; Schema: supabase_functions; Owner: - +-- + +ALTER TABLE ONLY supabase_functions.hooks ALTER COLUMN id SET DEFAULT nextval('supabase_functions.hooks_id_seq'::regclass); + + +-- +-- Data for Name: extensions; Type: TABLE DATA; Schema: _realtime; Owner: - +-- + +COPY _realtime.extensions (id, type, settings, tenant_external_id, inserted_at, updated_at) FROM stdin; +9ddeec51-bff3-4b2a-a80e-e519b2067594 postgres_cdc_rls {"region": "us-east-1", "db_host": "jojNM5epTA6mHrc9dSyLoHNyb6lzYaXqj0adu+DMEsk9UXGm67BsSlbKWPaH8DuL", "db_name": "sWBpZNdjggEPTQVlI52Zfw==", "db_port": "+enMDFi1J/3IrrquHHwUmA==", "db_user": "uxbEq/zz8DXVD53TOI1zmw==", "slot_name": "supabase_realtime_replication_slot", "db_password": "sWBpZNdjggEPTQVlI52Zfw==", "publication": "supabase_realtime", "ssl_enforced": false, "poll_interval_ms": 100, "poll_max_changes": 100, "poll_max_record_bytes": 1048576} realtime-dev 2026-03-27 08:44:57 2026-03-27 08:44:57 +\. + + +-- +-- Data for Name: schema_migrations; Type: TABLE DATA; Schema: _realtime; Owner: - +-- + +COPY _realtime.schema_migrations (version, inserted_at) FROM stdin; +20210706140551 2026-03-23 10:13:14 +20220329161857 2026-03-23 10:13:14 +20220410212326 2026-03-23 10:13:14 +20220506102948 2026-03-23 10:13:14 +20220527210857 2026-03-23 10:13:14 +20220815211129 2026-03-23 10:13:14 +20220815215024 2026-03-23 10:13:14 +20220818141501 2026-03-23 10:13:14 +20221018173709 2026-03-23 10:13:14 +20221102172703 2026-03-23 10:13:14 +20221223010058 2026-03-23 10:13:14 +20230110180046 2026-03-23 10:13:14 +20230810220907 2026-03-23 10:13:14 +20230810220924 2026-03-23 10:13:14 +20231024094642 2026-03-23 10:13:14 +20240306114423 2026-03-23 10:13:14 +20240418082835 2026-03-23 10:13:14 +20240625211759 2026-03-23 10:13:14 +20240704172020 2026-03-23 10:13:14 +20240902173232 2026-03-23 10:13:14 +20241106103258 2026-03-23 10:13:14 +20250424203323 2026-03-23 10:13:14 +20250613072131 2026-03-23 10:13:14 +20250711044927 2026-03-23 10:13:14 +20250811121559 2026-03-23 10:13:14 +20250926223044 2026-03-23 10:13:14 +20251204170944 2026-03-23 10:13:14 +20251218000543 2026-03-23 10:13:14 +\. + + +-- +-- Data for Name: tenants; Type: TABLE DATA; Schema: _realtime; Owner: - +-- + +COPY _realtime.tenants (id, name, external_id, jwt_secret, max_concurrent_users, inserted_at, updated_at, max_events_per_second, postgres_cdc_default, max_bytes_per_second, max_channels_per_client, max_joins_per_second, suspend, jwt_jwks, notify_private_alpha, private_only, migrations_ran, broadcast_adapter, max_presence_events_per_second, max_payload_size_in_kb) FROM stdin; +467dabcc-3c4c-49ef-b109-8a3a0a32fcd6 realtime-dev realtime-dev iNjicxc4+llvc9wovDvqymwfnj9teWMlyOIbJ8Fh6j2WNU8CIJ2ZgjR6MUIKqSmeDmvpsKLsZ9jgXJmQPpwL8w== 200 2026-03-27 08:44:57 2026-03-27 08:44:57 100 postgres_cdc_rls 100000 100 100 f {"keys": [{"x": "M5Sjqn5zwC9Kl1zVfUUGvv9boQjCGd45G8sdopBExB4", "y": "P6IXMvA2WYXSHSOMTBH2jsw_9rrzGy89FjPf6oOsIxQ", "alg": "ES256", "crv": "P-256", "ext": true, "kid": "b81269f1-21d8-4f2e-b719-c2240a840d90", "kty": "EC", "use": "sig", "key_ops": ["verify"]}, {"k": "c3VwZXItc2VjcmV0LWp3dC10b2tlbi13aXRoLWF0LWxlYXN0LTMyLWNoYXJhY3RlcnMtbG9uZw", "kty": "oct"}]} f f 65 gen_rpc 1000 3000 +\. + + +-- +-- Data for Name: audit_log_entries; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.audit_log_entries (instance_id, id, payload, created_at, ip_address) FROM stdin; +00000000-0000-0000-0000-000000000000 f3a9bb5c-a48b-48da-83b0-5e5f71cc1278 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-23 10:58:23.661651+00 +00000000-0000-0000-0000-000000000000 015ae6c3-f000-49f5-a05a-7e43256a5ead {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-23 10:59:13.173068+00 +00000000-0000-0000-0000-000000000000 2222f6e6-4a78-4c44-88cf-65b677a0bdb6 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-23 10:59:17.817071+00 +00000000-0000-0000-0000-000000000000 12c8d5f5-46f0-4e67-96d2-7722167c7b3c {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-23 11:30:05.361177+00 +00000000-0000-0000-0000-000000000000 4aa98446-9aa8-4442-985a-aae3fbd28cd2 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-23 11:30:08.832248+00 +00000000-0000-0000-0000-000000000000 4e5e241b-0ba7-4772-a492-5dce48836688 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 12:28:24.672784+00 +00000000-0000-0000-0000-000000000000 12c376a9-7e40-4701-b163-173b66bbcc05 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 12:28:24.674266+00 +00000000-0000-0000-0000-000000000000 54e723ec-3977-4d92-8595-ce7ab53dbd86 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 13:30:01.59908+00 +00000000-0000-0000-0000-000000000000 de3c62a2-5771-4cb3-8bc5-a0f2c5cdf7bd {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 13:30:01.600518+00 +00000000-0000-0000-0000-000000000000 970016c6-0f0b-41a0-b4eb-5da3a5064f8b {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 14:30:14.581786+00 +00000000-0000-0000-0000-000000000000 a951c68b-1c3f-46ca-82d1-aead121e268f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 14:30:14.582962+00 +00000000-0000-0000-0000-000000000000 34299538-d49f-41a9-b0cc-2abbedbae26e {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 15:28:29.566693+00 +00000000-0000-0000-0000-000000000000 276722cb-b0ee-46a3-93b9-f3d966f16c26 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 15:28:29.56777+00 +00000000-0000-0000-0000-000000000000 bdc467e8-37fa-488e-b21d-e12bb16d5225 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 16:26:59.228619+00 +00000000-0000-0000-0000-000000000000 f00324fe-263c-42fd-b885-1309db0c76d0 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 16:26:59.230039+00 +00000000-0000-0000-0000-000000000000 4e468b7b-c2c1-479f-a6f5-9d7aea99aaa7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 17:25:28.767876+00 +00000000-0000-0000-0000-000000000000 b1e6f592-6413-44bd-82ce-eb4ae4d3873b {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 17:25:28.770199+00 +00000000-0000-0000-0000-000000000000 8ab04c44-66b6-469e-b869-a69b35c1d35f {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 18:25:01.605164+00 +00000000-0000-0000-0000-000000000000 00c18ea4-a6bb-429b-9d0d-7dbddd7b3d94 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 18:25:01.615437+00 +00000000-0000-0000-0000-000000000000 53a407ef-2f1c-4bed-838a-997812acb065 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 19:25:02.095787+00 +00000000-0000-0000-0000-000000000000 2c310c64-0933-439d-81f9-74fb6ffbf68d {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 19:25:02.097455+00 +00000000-0000-0000-0000-000000000000 2720bc5f-9871-4c13-a725-b0c640a04698 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 20:23:51.534132+00 +00000000-0000-0000-0000-000000000000 19fc4616-6242-4e56-b340-48467d9be144 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 20:23:51.535311+00 +00000000-0000-0000-0000-000000000000 1eed06b7-6fa2-4d66-ac2c-3d3bcb5b0592 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 21:22:21.898206+00 +00000000-0000-0000-0000-000000000000 920acd65-8146-4b35-b6d5-5281f4ea1ed1 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 21:22:21.900821+00 +00000000-0000-0000-0000-000000000000 73a31066-566b-42a1-a281-0ec8fc71a2f5 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 22:25:02.312362+00 +00000000-0000-0000-0000-000000000000 44c5a6ee-6bd3-43f1-9a3f-7c13ef1a83b5 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 22:25:02.314918+00 +00000000-0000-0000-0000-000000000000 a28430ae-7a7d-4ce8-9ae3-c83f7b3f8f5d {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 23:24:14.39221+00 +00000000-0000-0000-0000-000000000000 b4f2883f-f222-4862-8cdf-135563aaa257 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 23:24:14.393364+00 +00000000-0000-0000-0000-000000000000 2d425bb6-1ab6-4d54-9502-1132ff7fd14f {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 10:17:05.380495+00 +00000000-0000-0000-0000-000000000000 48dac6fd-0dce-42e1-ba70-3b235a0e1161 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 10:17:05.385002+00 +00000000-0000-0000-0000-000000000000 bb85f6c0-d96a-4eb6-a8b7-5170b4ea74a7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 11:17:55.391428+00 +00000000-0000-0000-0000-000000000000 b063d335-22f8-470b-b0ed-df6f9d68a7ba {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 11:17:55.392611+00 +00000000-0000-0000-0000-000000000000 29529cd0-0680-4d1d-a5ff-e43736f70555 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 12:17:55.375751+00 +00000000-0000-0000-0000-000000000000 f1dc7c02-dd21-4882-83e1-5e83d60156f2 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 12:17:55.378137+00 +00000000-0000-0000-0000-000000000000 e0ce9d96-5ed7-4342-bcb0-13e420b91523 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 19:20:20.869716+00 +00000000-0000-0000-0000-000000000000 0a93156f-a22c-4d18-81f2-54367a3745ba {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 19:20:20.936303+00 +00000000-0000-0000-0000-000000000000 1ddab552-8e6f-4ea6-8cc1-f7d00b92abc7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 20:20:04.2956+00 +00000000-0000-0000-0000-000000000000 bf82dc60-43b8-4d3c-8cb1-01bc4fd4a341 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 20:20:04.297562+00 +00000000-0000-0000-0000-000000000000 c5a9e22f-feb2-4383-b4cd-7910f2266978 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 21:18:48.738276+00 +00000000-0000-0000-0000-000000000000 fb58f5f0-7279-41de-98aa-20fa277e3d84 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 21:18:48.74055+00 +00000000-0000-0000-0000-000000000000 0dda5ef1-f572-4379-b734-f65b9e044830 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 22:17:29.961966+00 +00000000-0000-0000-0000-000000000000 8762f6f6-d22e-4c60-b2a3-c5c8bf99d548 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 22:17:29.965545+00 +00000000-0000-0000-0000-000000000000 51cea4a0-f2cc-4e9b-8b1a-8b2c9cdf8b47 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-24 23:01:52.959606+00 +00000000-0000-0000-0000-000000000000 61e26d26-ce04-490d-802d-4f67cc379fda {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-24 23:01:56.400534+00 +00000000-0000-0000-0000-000000000000 8c73c9b9-2da1-42df-b1a6-f92b98ab9300 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-24 23:07:05.837147+00 +00000000-0000-0000-0000-000000000000 05bf6f2d-60c0-4e5a-8ec1-6f2a7403d0c2 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-24 23:07:09.755293+00 +00000000-0000-0000-0000-000000000000 8d63e967-c616-42b0-b1c5-c8f54f54beaa {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:01:49.505958+00 +00000000-0000-0000-0000-000000000000 cdde610b-ac0f-4d61-91bd-7af34f23acf0 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:01:57.651986+00 +00000000-0000-0000-0000-000000000000 ef89c4e5-e092-4c12-a3b5-608b0e79bd65 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:03:22.169295+00 +00000000-0000-0000-0000-000000000000 ba251517-d704-4aa9-9992-16a8590582e0 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:03:28.975189+00 +00000000-0000-0000-0000-000000000000 b9d81262-4370-4712-8db7-81d51e27fcc8 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:06:51.450521+00 +00000000-0000-0000-0000-000000000000 26ca7ff5-87d3-48a2-ad9f-4ac67a216da8 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:06:55.726041+00 +00000000-0000-0000-0000-000000000000 f8c943c2-6dc0-42f6-87c9-adffc9a336c5 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:07:45.021178+00 +00000000-0000-0000-0000-000000000000 bba67a2b-049e-4d1b-b6a7-286d2b9c45f7 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:07:48.235967+00 +00000000-0000-0000-0000-000000000000 151834d9-2232-43c8-a0e6-a39c6617246e {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:11:54.788614+00 +00000000-0000-0000-0000-000000000000 e903f726-e5fe-4e06-9051-f36dacc780ce {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:12:00.861214+00 +00000000-0000-0000-0000-000000000000 e300a60b-b944-4c72-bb52-9ff7dff08dd2 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:24:51.770222+00 +00000000-0000-0000-0000-000000000000 4580e71b-e19b-4a74-ad85-f08f74791e53 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:24:57.09306+00 +00000000-0000-0000-0000-000000000000 a12cb7e1-768c-4ca2-96de-d68ce160971a {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 01:25:50.776943+00 +00000000-0000-0000-0000-000000000000 ce559936-37f2-411f-ba99-401e6ac4d94f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 01:25:50.778414+00 +00000000-0000-0000-0000-000000000000 46ebfc8e-e65a-4118-8a42-1d873b1436cc {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 02:25:50.797262+00 +00000000-0000-0000-0000-000000000000 d09f6b35-52d2-4f5e-80f5-0b188fef98da {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 02:25:50.798466+00 +00000000-0000-0000-0000-000000000000 7e7a2284-bb63-4b8d-ab19-186394b12f8f {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:13:42.627459+00 +00000000-0000-0000-0000-000000000000 cd92d32d-ac1a-4681-b957-11d8d4bd1a09 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:13:46.932353+00 +00000000-0000-0000-0000-000000000000 54013ee2-2dc3-4e36-8a35-09eb748ccba4 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:20:03.154923+00 +00000000-0000-0000-0000-000000000000 598cd35b-8c6e-4d41-9ec6-c25dcc4c6715 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:20:07.255873+00 +00000000-0000-0000-0000-000000000000 520167bb-ec1c-4e67-8c07-8d2ba457ec3c {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:24:18.554167+00 +00000000-0000-0000-0000-000000000000 4dc11041-8b36-4703-b5c5-2411a13b96da {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:24:24.699031+00 +00000000-0000-0000-0000-000000000000 9cfd2eae-f911-4dc7-9c5d-78499fd608f1 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:25:07.736803+00 +00000000-0000-0000-0000-000000000000 258e6ed2-5959-4893-8433-caa4b9ccb106 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:29:21.141199+00 +00000000-0000-0000-0000-000000000000 c76a59d2-0829-456a-957b-c623b14ae03e {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 10:28:55.843362+00 +00000000-0000-0000-0000-000000000000 f96b1f88-1f48-4a96-bbfc-b8c6527a31df {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 10:29:14.90848+00 +00000000-0000-0000-0000-000000000000 65f06c2b-5ee5-4bf1-a6ff-21dbe657e0b4 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 10:29:19.403154+00 +00000000-0000-0000-0000-000000000000 9e2290b7-dcfa-4bf5-b244-1f91011bfbe3 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 11:27:32.781897+00 +00000000-0000-0000-0000-000000000000 9cc32bd5-ae94-4014-90cb-b468e133cc10 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 11:27:32.783409+00 +00000000-0000-0000-0000-000000000000 2e244065-7456-4aba-a841-0920875e7f1b {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 12:16:54.862937+00 +00000000-0000-0000-0000-000000000000 f7cf0072-bda9-4b59-9532-7354a79c0306 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 12:17:01.538685+00 +00000000-0000-0000-0000-000000000000 5d8407ed-be29-482b-8877-42cf6e78f6c3 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:05:17.486922+00 +00000000-0000-0000-0000-000000000000 a8a0c3ef-6a10-4c8f-b53c-830ee3c2cfaf {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:05:21.871823+00 +00000000-0000-0000-0000-000000000000 2dfcfe2d-3770-42a6-8ceb-f16bd2e52151 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:05:31.190404+00 +00000000-0000-0000-0000-000000000000 c9e57ce5-33e2-4e79-b0b6-204dc22e4c8f {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:05:54.861933+00 +00000000-0000-0000-0000-000000000000 f62ee264-f036-46b9-9bdc-8bababf26492 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:06:00.886992+00 +00000000-0000-0000-0000-000000000000 ace96eee-7f68-4139-9f73-c3aa21d5e1d7 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:29:24.160199+00 +00000000-0000-0000-0000-000000000000 75b60071-d53f-490c-be4c-69f3c6f08da4 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:29:33.800526+00 +00000000-0000-0000-0000-000000000000 4a736318-9980-48ed-b13b-b47e38be5d41 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:30:03.032141+00 +00000000-0000-0000-0000-000000000000 a61bb073-c754-406a-b123-0b40c22915a3 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:37:21.932796+00 +00000000-0000-0000-0000-000000000000 80fd895f-8963-40f4-a9bb-4c59d95e964e {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 14:40:14.190475+00 +00000000-0000-0000-0000-000000000000 d2392bf0-18e8-45af-98f0-1d70e7cbe6f0 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 14:40:14.193129+00 +00000000-0000-0000-0000-000000000000 3fcfde42-9951-4df4-aae3-4abf256ed25d {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 15:38:52.104451+00 +00000000-0000-0000-0000-000000000000 e4c92ebd-11df-4e4c-82a1-7af12b797164 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 15:38:52.121069+00 +00000000-0000-0000-0000-000000000000 e6d4fb6d-fca8-4ee4-992a-59661b86b3d7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 16:39:45.201853+00 +00000000-0000-0000-0000-000000000000 f2606751-7081-4463-8c3f-985860702b7a {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 16:39:45.203238+00 +00000000-0000-0000-0000-000000000000 0db3c68a-49b6-42e3-8df0-a3dd1daa805d {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 17:39:45.220264+00 +00000000-0000-0000-0000-000000000000 20d02e8a-bdae-4c3d-9629-e0c1bc5e9d0a {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 17:39:45.221411+00 +00000000-0000-0000-0000-000000000000 58afa2b2-3d21-4d56-9b99-f96ec194170a {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 18:32:34.529973+00 +00000000-0000-0000-0000-000000000000 4afd9a79-0d8e-462e-8439-8c86ae984aa8 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 18:32:38.624903+00 +00000000-0000-0000-0000-000000000000 ee9fb7dc-392e-4fb2-9ee1-5d6fd7f632a8 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 18:35:17.643808+00 +00000000-0000-0000-0000-000000000000 aa72f08d-bdd6-400e-8798-3ad17598c5a8 {"action":"login","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 18:35:21.349866+00 +00000000-0000-0000-0000-000000000000 7ffd90be-aa82-4fa0-9a6a-6015fccedf99 {"action":"logout","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 18:35:43.306104+00 +00000000-0000-0000-0000-000000000000 ce068f66-0a8a-47a0-9fb1-2c3f727409f3 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 18:35:46.92007+00 +00000000-0000-0000-0000-000000000000 76e76323-7144-4ab0-9350-b1b009a5688a {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 19:33:46.819266+00 +00000000-0000-0000-0000-000000000000 f4ad35f0-f7f4-45c7-9e6f-b76d7075bcbd {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 19:33:46.822119+00 +00000000-0000-0000-0000-000000000000 d8d7a18a-1cd7-4694-a709-1c25bafd47cd {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 20:32:08.57133+00 +00000000-0000-0000-0000-000000000000 c0efd1eb-d3a4-4588-a6e4-6c6316afcbfa {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 20:32:08.573939+00 +00000000-0000-0000-0000-000000000000 663aa064-f89b-4629-9853-6921cec5f658 {"action":"login","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 10:13:28.062141+00 +00000000-0000-0000-0000-000000000000 bada5aa6-4962-42ab-90ac-f10ec7ada174 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 11:12:40.283978+00 +00000000-0000-0000-0000-000000000000 0284d0a9-1045-4d63-84c0-199f8fd57463 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 11:12:40.285424+00 +00000000-0000-0000-0000-000000000000 7fe49307-b9fc-4045-86e6-77ec5b6724d4 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 12:12:40.261029+00 +00000000-0000-0000-0000-000000000000 1cf673eb-6fed-4b60-a756-e5d9d8164ab2 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 12:12:40.262344+00 +00000000-0000-0000-0000-000000000000 89931cd1-3362-4153-9e26-c3a3450315bf {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 15:51:12.813868+00 +00000000-0000-0000-0000-000000000000 d4ae8391-85ed-4d02-be33-fc9895d10993 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 15:51:12.815012+00 +00000000-0000-0000-0000-000000000000 26bfda48-9d72-443a-8324-1944ebe4a701 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 16:49:43.315+00 +00000000-0000-0000-0000-000000000000 0f7adbbc-aaa1-4f3a-b04c-a6845b9e0ca6 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 16:49:43.323835+00 +00000000-0000-0000-0000-000000000000 d9baf33f-c034-49ad-8f16-902bea98ec5e {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 17:48:58.248187+00 +00000000-0000-0000-0000-000000000000 ca2f00e5-300f-4a17-b87e-ccbec14f2c7e {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 17:48:58.250458+00 +00000000-0000-0000-0000-000000000000 fa02ec44-7c86-418c-99c7-de2622acfb31 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 18:49:41.829309+00 +00000000-0000-0000-0000-000000000000 136da06f-a9bb-4523-bb16-47717edd8eb5 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 18:49:41.831653+00 +00000000-0000-0000-0000-000000000000 e8a3a012-3e8d-499a-aa7a-4cbca7999463 {"action":"logout","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:46:23.049908+00 +00000000-0000-0000-0000-000000000000 c31208e4-7589-44be-9f3d-5f3f201a4df6 {"action":"login","actor_id":"aaaaaaaa-0008-0008-0008-000000000008","actor_username":"editor@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:46:29.068361+00 +00000000-0000-0000-0000-000000000000 81031375-449a-4a69-b4dc-2ee999a654b2 {"action":"logout","actor_id":"aaaaaaaa-0008-0008-0008-000000000008","actor_username":"editor@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:46:38.212531+00 +00000000-0000-0000-0000-000000000000 5bcaaf60-9f35-4d7b-a54b-b491ac07ed15 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:46:43.300041+00 +00000000-0000-0000-0000-000000000000 6e71806d-9b38-4aec-af46-15f9aa5b5375 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:46:49.885518+00 +00000000-0000-0000-0000-000000000000 ab45715c-877c-4cea-9249-5e02e163d77a {"action":"user_signedup","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"team","traits":{"provider":"email"}} 2026-03-26 19:47:30.494975+00 +00000000-0000-0000-0000-000000000000 98634ad1-21f0-448b-81a4-6686bbfc9932 {"action":"login","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:47:30.515066+00 +00000000-0000-0000-0000-000000000000 e0613923-7e54-45b6-9861-14fd4a03f8e7 {"action":"logout","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:48:35.667525+00 +00000000-0000-0000-0000-000000000000 11139b73-efc6-43e4-b228-2ea2c4da475b {"action":"login","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:48:44.25225+00 +00000000-0000-0000-0000-000000000000 cf97f01d-8b2d-45a2-a571-d5ae4adce236 {"action":"logout","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:48:53.19869+00 +00000000-0000-0000-0000-000000000000 51c6d2fc-9a32-4449-91b0-707a74077e92 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:51:35.028402+00 +00000000-0000-0000-0000-000000000000 6f152cff-1e9a-4f0d-b2aa-d556ed20ad19 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:53:14.391225+00 +00000000-0000-0000-0000-000000000000 4dbde0d4-78e6-408f-84dc-ab6c246168b0 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:53:17.60162+00 +00000000-0000-0000-0000-000000000000 719ef7ce-f477-4de3-85d4-cb39b346bbf1 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:56:14.350054+00 +00000000-0000-0000-0000-000000000000 cd55882a-9e54-4730-b063-c9b4887c0e0a {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:56:18.624252+00 +00000000-0000-0000-0000-000000000000 232c3b43-f160-40a1-be37-2da29fc997bd {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 20:57:32.788753+00 +00000000-0000-0000-0000-000000000000 8d6852ac-b3b5-4543-bfed-e2369a73f211 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 20:57:32.794287+00 +00000000-0000-0000-0000-000000000000 f860fa47-b12d-4463-ab9a-46cbf022837e {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:19:25.905866+00 +00000000-0000-0000-0000-000000000000 449ddd1b-11e8-4b1e-bdc0-abc398dca77d {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:19:52.487097+00 +00000000-0000-0000-0000-000000000000 61bf81aa-1454-4723-af1a-fb00b2203c72 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:19:54.580581+00 +00000000-0000-0000-0000-000000000000 74a985d1-14b7-40c7-92db-444d0ba62ffb {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:31:18.590523+00 +00000000-0000-0000-0000-000000000000 17d9c260-731d-4fa7-9d69-9c1241c30bd9 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:31:20.209708+00 +00000000-0000-0000-0000-000000000000 6ec569b6-f0a1-4a0c-8c81-54fc82f6be93 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:31:39.762447+00 +00000000-0000-0000-0000-000000000000 75c4095e-994d-4ce9-9d77-59c514d88653 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:32:10.888074+00 +00000000-0000-0000-0000-000000000000 8ec7110e-415d-4627-935f-f0327b1dd58e {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:32:12.867178+00 +00000000-0000-0000-0000-000000000000 8350236f-d596-4d1e-8925-ae917e599ad4 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:33:05.320834+00 +00000000-0000-0000-0000-000000000000 25a7c28b-8ee2-4b94-bf6f-d046387fcde2 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:33:25.418988+00 +00000000-0000-0000-0000-000000000000 ce951d2d-488f-4757-92d0-3460f5bde396 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:40:53.407118+00 +00000000-0000-0000-0000-000000000000 39056ca1-fbd1-4e85-88ab-355c01a969ab {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:40:55.222474+00 +00000000-0000-0000-0000-000000000000 9591ecbc-aad0-4d5a-9076-413ed1e585ff {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:42:37.889869+00 +00000000-0000-0000-0000-000000000000 5777adf8-b362-496b-899f-5b05c0d93721 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:42:53.50928+00 +00000000-0000-0000-0000-000000000000 6a1e5a17-c7ea-453c-8626-d5022145ddf7 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:46:43.464106+00 +00000000-0000-0000-0000-000000000000 d28f02fc-a6ff-4aac-b49a-4fda2583bf17 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:46:46.362488+00 +00000000-0000-0000-0000-000000000000 b0e12d07-d349-4e14-8a3a-6ad42e747b21 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:52:49.203802+00 +00000000-0000-0000-0000-000000000000 c95a6365-3083-4f4f-a910-48f1405b272a {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:52:50.894594+00 +00000000-0000-0000-0000-000000000000 a8da7e38-f276-4aac-9a34-704af25a8716 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:53:20.228765+00 +00000000-0000-0000-0000-000000000000 1630012a-3347-479e-ab2b-76996c192c36 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:53:40.261757+00 +00000000-0000-0000-0000-000000000000 a8db2936-54c5-4614-94c5-4a13e6c83654 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:59:17.880911+00 +00000000-0000-0000-0000-000000000000 3d4fb0b1-d8d1-4beb-8c3a-53a22607cddd {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:59:25.603132+00 +00000000-0000-0000-0000-000000000000 10ef60d6-f55d-4370-b320-78c7b6561104 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:59:34.821814+00 +00000000-0000-0000-0000-000000000000 efc9e3f6-c034-4a04-977e-b81d445763ed {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:59:55.339526+00 +00000000-0000-0000-0000-000000000000 3b3a02dc-067d-42cc-917c-7811c4fd6347 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:02:44.995107+00 +00000000-0000-0000-0000-000000000000 c7cb137e-33a1-4fb2-9960-4bb015db752c {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:02:46.891055+00 +00000000-0000-0000-0000-000000000000 9bd389c2-f3ed-4197-a22b-bab553eb4e4f {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:12:46.756181+00 +00000000-0000-0000-0000-000000000000 bab2f893-dc20-436c-8363-d33a4aee8d06 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:13:14.788124+00 +00000000-0000-0000-0000-000000000000 651c44ae-e597-4840-ab53-1e6732543227 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:21:43.325253+00 +00000000-0000-0000-0000-000000000000 b6c79412-3e78-40e3-b079-555f9240682f {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:21:56.512281+00 +00000000-0000-0000-0000-000000000000 8a4f0ef0-c0ef-4278-852c-ca0ca0393fb9 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:22:05.26663+00 +00000000-0000-0000-0000-000000000000 1811dd26-c005-4aa5-b35d-f1d3a6668be7 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:22:21.864228+00 +00000000-0000-0000-0000-000000000000 5b46125b-ec57-4907-90bd-0f797cf56bfc {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:48:00.736884+00 +00000000-0000-0000-0000-000000000000 45fd6936-faf4-4df8-aac0-263d13f756c8 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:48:53.242588+00 +00000000-0000-0000-0000-000000000000 2a2f7b53-e0c5-4cfc-9b38-cea9d67ad2ad {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 23:32:17.947743+00 +00000000-0000-0000-0000-000000000000 8e3139b2-1c40-47ad-a247-3ccaa94e08ad {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 23:32:44.158626+00 +00000000-0000-0000-0000-000000000000 c0f1f8c1-5da2-4171-acf4-f15f3fef837b {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 09:06:02.093059+00 +00000000-0000-0000-0000-000000000000 0f53bba3-4d36-4bd9-bc78-4e5ce659129c {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 09:06:02.096051+00 +00000000-0000-0000-0000-000000000000 e2ea64a5-54a5-4b0a-95b4-cc26be5dc85e {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:27:06.674863+00 +00000000-0000-0000-0000-000000000000 f008faaf-004d-4d13-ba4d-cec5a113cfe8 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:39:56.661836+00 +00000000-0000-0000-0000-000000000000 5d36ee39-6793-42c6-a157-31037e7ddcbe {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"asd","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:52:45.795272+00 +00000000-0000-0000-0000-000000000000 98a8a1e1-8a81-4837-ba3c-c149d8d0be77 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leoardno","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:57:12.890495+00 +00000000-0000-0000-0000-000000000000 7a5d36c5-48b9-4a8c-b5af-af0fee6b61e2 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leoardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:57:34.211215+00 +00000000-0000-0000-0000-000000000000 4f5a2ed6-3898-4274-a852-519d71b71b21 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:58:48.279047+00 +00000000-0000-0000-0000-000000000000 5404eb4e-93f4-41a5-a86d-4250e2301115 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 10:05:13.289504+00 +00000000-0000-0000-0000-000000000000 e1e7b9e4-86c0-4d43-b108-ce91cfa7305f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 10:05:13.291502+00 +00000000-0000-0000-0000-000000000000 51d32ba8-701c-4bcc-ab72-74c02b06f37e {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 10:05:38.42979+00 +00000000-0000-0000-0000-000000000000 04b8022c-7a17-466e-baab-8355226f82f6 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 10:13:27.443108+00 +00000000-0000-0000-0000-000000000000 c59f34c3-6d7c-43b6-8335-5605d7914abb {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 10:52:34.48919+00 +00000000-0000-0000-0000-000000000000 94735b50-0e6e-4c02-8e0a-7ed9fe148337 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 10:52:36.530338+00 +00000000-0000-0000-0000-000000000000 e2b764c8-79d0-4e51-95f0-b980ccf2d799 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:03:42.792326+00 +00000000-0000-0000-0000-000000000000 3e674f0a-99ab-4c1e-81cf-467b04d50628 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:04:08.619676+00 +00000000-0000-0000-0000-000000000000 b2e669ff-00d5-4de8-a7a1-212c058b997e {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:04:18.215073+00 +00000000-0000-0000-0000-000000000000 efd87e91-95e7-4d43-906c-8700b9763e5d {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:04:41.642564+00 +00000000-0000-0000-0000-000000000000 0e3b31f4-ac2c-427b-9d35-3e9888bea099 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:09:33.113539+00 +00000000-0000-0000-0000-000000000000 c66e88bb-5e43-4d64-9941-bf7b87df1f57 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:09:42.050633+00 +00000000-0000-0000-0000-000000000000 f0d83a39-f0b6-411b-99cd-522f82a7c614 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:27:39.615562+00 +00000000-0000-0000-0000-000000000000 df8b1dd9-47ac-48a7-a1b0-49d7fb81e65c {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:30:42.202869+00 +00000000-0000-0000-0000-000000000000 825d4eb3-8690-4d89-89ee-36235b27fd2b {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 12:24:04.81973+00 +00000000-0000-0000-0000-000000000000 5449ebbf-358c-4daf-941a-071a5aa38352 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 12:24:06.324772+00 +00000000-0000-0000-0000-000000000000 5241335c-6b1e-42cd-b5f0-cf43ae4eac42 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 12:36:58.667642+00 +00000000-0000-0000-0000-000000000000 f77cb484-a8c6-4f12-b203-e08788747c89 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 12:37:01.302944+00 +00000000-0000-0000-0000-000000000000 7f91383b-30cf-4e95-ba47-b901f59133e5 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 12:37:09.603409+00 +00000000-0000-0000-0000-000000000000 de46c96d-1bbd-42c1-82d6-d28eb0cefd38 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 12:37:21.704034+00 +00000000-0000-0000-0000-000000000000 107aa0d2-82b7-4bd7-8672-96a43308cce4 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 13:05:48.781671+00 +00000000-0000-0000-0000-000000000000 cafae40c-1851-459a-8015-bf579bf00dfb {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 13:06:26.132815+00 +00000000-0000-0000-0000-000000000000 a24e3b39-ec62-480e-a8ab-1abe7cf5b6d1 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 14:06:35.696602+00 +00000000-0000-0000-0000-000000000000 a3ec0876-8151-446b-8027-f98fd9caf51b {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 14:06:35.697965+00 +00000000-0000-0000-0000-000000000000 c4557809-e407-495e-80d8-2de06b0c3cf5 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 14:15:40.80509+00 +00000000-0000-0000-0000-000000000000 c12e09c1-2140-4214-b3a6-b3f865941b29 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 14:15:43.617516+00 +00000000-0000-0000-0000-000000000000 fc7e22db-e27e-422e-ac30-93d657d23e78 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 14:16:38.012591+00 +00000000-0000-0000-0000-000000000000 c4b30fe1-40d5-4c74-aa66-0736bb7a3fb8 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 14:17:04.239061+00 +00000000-0000-0000-0000-000000000000 1608005f-42b8-4860-b87b-0232d72312a6 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 15:19:35.676083+00 +00000000-0000-0000-0000-000000000000 8cf0e884-4e73-47b2-8ae6-6b782ef8ad10 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 15:19:35.678353+00 +00000000-0000-0000-0000-000000000000 f9dac421-76ac-45f4-86ef-8d3e6d5f98fb {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 15:59:56.791407+00 +00000000-0000-0000-0000-000000000000 33eed8d8-66e0-4b32-9911-9a51a021903b {"action":"login","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 16:00:02.074346+00 +00000000-0000-0000-0000-000000000000 78d20dc8-f2fc-49b7-b0d2-ab87d74f7c29 {"action":"logout","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 16:00:24.342213+00 +00000000-0000-0000-0000-000000000000 d0bf8715-a43d-43ac-88d9-e3258869de64 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 16:00:29.89578+00 +\. + + +-- +-- Data for Name: flow_state; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.flow_state (id, user_id, auth_code, code_challenge_method, code_challenge, provider_type, provider_access_token, provider_refresh_token, created_at, updated_at, authentication_method, auth_code_issued_at, invite_token, referrer, oauth_client_state_id, linking_target_id, email_optional) FROM stdin; +\. + + +-- +-- Data for Name: identities; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.identities (provider_id, user_id, identity_data, provider, last_sign_in_at, created_at, updated_at, id) FROM stdin; +paciente@agenciapsi.com.br aaaaaaaa-0001-0001-0001-000000000001 {"sub": "aaaaaaaa-0001-0001-0001-000000000001", "email": "paciente@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 977a296d-b7ac-4151-8017-3099808e1f7f +terapeuta@agenciapsi.com.br aaaaaaaa-0002-0002-0002-000000000002 {"sub": "aaaaaaaa-0002-0002-0002-000000000002", "email": "terapeuta@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 44ccd811-6c51-48e9-81a8-77247022c693 +clinica1@agenciapsi.com.br aaaaaaaa-0003-0003-0003-000000000003 {"sub": "aaaaaaaa-0003-0003-0003-000000000003", "email": "clinica1@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 433175a2-4065-47ab-96dd-32508c9a7389 +clinica2@agenciapsi.com.br aaaaaaaa-0004-0004-0004-000000000004 {"sub": "aaaaaaaa-0004-0004-0004-000000000004", "email": "clinica2@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 390305dc-1f61-44e8-9c95-bd9de9591e5a +clinica3@agenciapsi.com.br aaaaaaaa-0005-0005-0005-000000000005 {"sub": "aaaaaaaa-0005-0005-0005-000000000005", "email": "clinica3@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 f669ef52-181e-44d5-8649-833d6b133313 +saas@agenciapsi.com.br aaaaaaaa-0006-0006-0006-000000000006 {"sub": "aaaaaaaa-0006-0006-0006-000000000006", "email": "saas@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 e6af6a5e-55ce-4872-a745-40d688d08982 +supervisor@agenciapsi.com.br aaaaaaaa-0007-0007-0007-000000000007 {"sub": "aaaaaaaa-0007-0007-0007-000000000007", "email": "supervisor@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 8dff38d2-1b09-4b88-9038-28b747358645 +editor@agenciapsi.com.br aaaaaaaa-0008-0008-0008-000000000008 {"sub": "aaaaaaaa-0008-0008-0008-000000000008", "email": "editor@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 a7856eb7-e96f-4752-84a3-b676503b74ae +therapist2@agenciapsi.com.br aaaaaaaa-0009-0009-0009-000000000009 {"sub": "aaaaaaaa-0009-0009-0009-000000000009", "email": "therapist2@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 8129cc38-db24-4f84-b48e-bf0b46235811 +therapist3@agenciapsi.com.br aaaaaaaa-0010-0010-0010-000000000010 {"sub": "aaaaaaaa-0010-0010-0010-000000000010", "email": "therapist3@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 754b015d-3c88-4104-b246-eede6fffabba +secretary@agenciapsi.com.br aaaaaaaa-0011-0011-0011-000000000011 {"sub": "aaaaaaaa-0011-0011-0011-000000000011", "email": "secretary@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 a7aa272b-659f-4bc7-8667-10cf49200410 +384a69d8-b7cd-40ac-9d3c-764c93532b66 384a69d8-b7cd-40ac-9d3c-764c93532b66 {"sub": "384a69d8-b7cd-40ac-9d3c-764c93532b66", "email": "terapeuta2@agenciapsi.com.br", "email_verified": false, "phone_verified": false} email 2026-03-26 19:47:30.489663+00 2026-03-26 19:47:30.489705+00 2026-03-26 19:47:30.489705+00 7e35a3f9-29d8-4f8d-bdfa-5bfe1328293f +\. + + +-- +-- Data for Name: instances; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.instances (id, uuid, raw_base_config, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: mfa_amr_claims; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.mfa_amr_claims (session_id, created_at, updated_at, authentication_method, id) FROM stdin; +2dc73205-6913-432b-8eb7-e8b609e5f0f4 2026-03-27 16:00:29.920845+00 2026-03-27 16:00:29.920845+00 password eb1238d8-fd33-4b72-b879-46e82e0abe4c +\. + + +-- +-- Data for Name: mfa_challenges; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.mfa_challenges (id, factor_id, created_at, verified_at, ip_address, otp_code, web_authn_session_data) FROM stdin; +\. + + +-- +-- Data for Name: mfa_factors; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.mfa_factors (id, user_id, friendly_name, factor_type, status, created_at, updated_at, secret, phone, last_challenged_at, web_authn_credential, web_authn_aaguid, last_webauthn_challenge_data) FROM stdin; +\. + + +-- +-- Data for Name: oauth_authorizations; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_authorizations (id, authorization_id, client_id, user_id, redirect_uri, scope, state, resource, code_challenge, code_challenge_method, response_type, status, authorization_code, created_at, expires_at, approved_at, nonce) FROM stdin; +\. + + +-- +-- Data for Name: oauth_client_states; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_client_states (id, provider_type, code_verifier, created_at) FROM stdin; +\. + + +-- +-- Data for Name: oauth_clients; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_clients (id, client_secret_hash, registration_type, redirect_uris, grant_types, client_name, client_uri, logo_uri, created_at, updated_at, deleted_at, client_type, token_endpoint_auth_method) FROM stdin; +\. + + +-- +-- Data for Name: oauth_consents; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_consents (id, user_id, client_id, scopes, granted_at, revoked_at) FROM stdin; +\. + + +-- +-- Data for Name: one_time_tokens; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.one_time_tokens (id, user_id, token_type, token_hash, relates_to, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: refresh_tokens; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.refresh_tokens (instance_id, id, token, user_id, revoked, created_at, updated_at, parent, session_id) FROM stdin; +00000000-0000-0000-0000-000000000000 137 ow4odpbr72tv aaaaaaaa-0002-0002-0002-000000000002 f 2026-03-27 16:00:29.902122+00 2026-03-27 16:00:29.902122+00 \N 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +\. + + +-- +-- Data for Name: saml_providers; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.saml_providers (id, sso_provider_id, entity_id, metadata_xml, metadata_url, attribute_mapping, created_at, updated_at, name_id_format) FROM stdin; +\. + + +-- +-- Data for Name: saml_relay_states; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.saml_relay_states (id, sso_provider_id, request_id, for_email, redirect_to, created_at, updated_at, flow_state_id) FROM stdin; +\. + + +-- +-- Data for Name: schema_migrations; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.schema_migrations (version) FROM stdin; +20171026211738 +20171026211808 +20171026211834 +20180103212743 +20180108183307 +20180119214651 +20180125194653 +00 +20210710035447 +20210722035447 +20210730183235 +20210909172000 +20210927181326 +20211122151130 +20211124214934 +20211202183645 +20220114185221 +20220114185340 +20220224000811 +20220323170000 +20220429102000 +20220531120530 +20220614074223 +20220811173540 +20221003041349 +20221003041400 +20221011041400 +20221020193600 +20221021073300 +20221021082433 +20221027105023 +20221114143122 +20221114143410 +20221125140132 +20221208132122 +20221215195500 +20221215195800 +20221215195900 +20230116124310 +20230116124412 +20230131181311 +20230322519590 +20230402418590 +20230411005111 +20230508135423 +20230523124323 +20230818113222 +20230914180801 +20231027141322 +20231114161723 +20231117164230 +20240115144230 +20240214120130 +20240306115329 +20240314092811 +20240427152123 +20240612123726 +20240729123726 +20240802193726 +20240806073726 +20241009103726 +20250717082212 +20250731150234 +20250804100000 +20250901200500 +20250903112500 +20250904133000 +20250925093508 +20251007112900 +20251104100000 +20251111201300 +20251201000000 +20260115000000 +20260121000000 +\. + + +-- +-- Data for Name: sessions; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.sessions (id, user_id, created_at, updated_at, factor_id, aal, not_after, refreshed_at, user_agent, ip, tag, oauth_client_id, refresh_token_hmac_key, refresh_token_counter, scopes) FROM stdin; +2dc73205-6913-432b-8eb7-e8b609e5f0f4 aaaaaaaa-0002-0002-0002-000000000002 2026-03-27 16:00:29.897964+00 2026-03-27 16:00:29.897964+00 \N aal1 \N \N Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 172.19.0.1 \N \N \N \N \N +\. + + +-- +-- Data for Name: sso_domains; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.sso_domains (id, sso_provider_id, domain, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: sso_providers; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.sso_providers (id, resource_id, created_at, updated_at, disabled) FROM stdin; +\. + + +-- +-- Data for Name: users; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.users (instance_id, id, aud, role, email, encrypted_password, email_confirmed_at, invited_at, confirmation_token, confirmation_sent_at, recovery_token, recovery_sent_at, email_change_token_new, email_change, email_change_sent_at, last_sign_in_at, raw_app_meta_data, raw_user_meta_data, is_super_admin, created_at, updated_at, phone, phone_confirmed_at, phone_change, phone_change_token, phone_change_sent_at, email_change_token_current, email_change_confirm_status, banned_until, reauthentication_token, reauthentication_sent_at, is_sso_user, deleted_at, is_anonymous) FROM stdin; +00000000-0000-0000-0000-000000000000 aaaaaaaa-0001-0001-0001-000000000001 authenticated authenticated paciente@agenciapsi.com.br $2a$06$ipEc7puaVhQnpusOGhAYgOcrVHq4RnqZeDooS8FaehzHhueScf9S. 2026-03-23 10:46:29.876072+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Ana Paciente"} \N 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0005-0005-0005-000000000005 authenticated authenticated clinica3@agenciapsi.com.br $2a$06$L3aFykCSdduzTHEKsEQ3q.GdHTb5EJBvbIit4k7ZgnbRd5BCGuTxu 2026-03-23 10:46:29.876072+00 \N \N \N \N 2026-03-27 16:00:02.080329+00 {"provider": "email", "providers": ["email"]} {"name": "Clínica Bem Estar"} \N 2026-03-23 10:46:29.876072+00 2026-03-27 16:00:02.084768+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0011-0011-0011-000000000011 authenticated authenticated secretary@agenciapsi.com.br $2a$06$O7HeygRYgJViriMFCImLZu7DD.3A9wZWb9y3c5G2PIURgJ65UnqT. 2026-03-23 14:18:06.087973+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Gabriela Secretária"} \N 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0002-0002-0002-000000000002 authenticated authenticated terapeuta@agenciapsi.com.br $2a$06$CztXijQkaPZa6pUwXmMHWuzSF19GiVtRBdMLp.k4iWf7ftGWNBIg6 2026-03-23 10:46:29.876072+00 \N \N \N \N 2026-03-27 16:00:29.897844+00 {"provider": "email", "providers": ["email"]} {"name": "Bruno Terapeuta", "full_name": "Leonardo Nohama", "avatar_url": "http://127.0.0.1:54321/storage/v1/object/public/avatars/aaaaaaaa-0002-0002-0002-000000000002/avatar.jpg"} \N 2026-03-23 10:46:29.876072+00 2026-03-27 16:00:29.919654+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 384a69d8-b7cd-40ac-9d3c-764c93532b66 authenticated authenticated terapeuta2@agenciapsi.com.br $2a$10$MBE/uZcT1lpKira6nsTY6OzrUabKwtOrm.QvJzdy.IU95tiX3M2ia 2026-03-26 19:47:30.496218+00 \N \N \N \N 2026-03-26 19:48:44.253788+00 {"provider": "email", "providers": ["email"]} {"sub": "384a69d8-b7cd-40ac-9d3c-764c93532b66", "email": "terapeuta2@agenciapsi.com.br", "email_verified": true, "phone_verified": false} \N 2026-03-26 19:47:30.478057+00 2026-03-26 19:48:44.258995+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0007-0007-0007-000000000007 authenticated authenticated supervisor@agenciapsi.com.br $2a$06$.kF47/tagPNwSpgGM4ryZOu01L0ykU2IXakM8trZ.Hon1TTUDeqYK 2026-03-23 14:18:05.215881+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Carlos Supervisor"} \N 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0009-0009-0009-000000000009 authenticated authenticated therapist2@agenciapsi.com.br $2a$06$16hf/nUbN0lElm9l8vQI4ek8vIM2T8ymiJTQ8CHXXw/jD1gMuDFJS 2026-03-23 14:18:06.087973+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Eva Terapeuta"} \N 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0010-0010-0010-000000000010 authenticated authenticated therapist3@agenciapsi.com.br $2a$06$sBJPPHRI/MsrCTEeCK5/vOhASc/SNLeO.B/QEE2MZNWEP8FamyCXW 2026-03-23 14:18:06.087973+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Felipe Terapeuta"} \N 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0003-0003-0003-000000000003 authenticated authenticated clinica1@agenciapsi.com.br $2a$06$cxZ2uXWIOS9MgzoyzSla8Oocid6wKtEBPA4k9QyC8DvwzmOsI0co2 2026-03-23 10:46:29.876072+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Clínica Espaço Psi"} \N 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0004-0004-0004-000000000004 authenticated authenticated clinica2@agenciapsi.com.br $2a$06$ZSW6FPPCmhO8EkfSM4/QLu/J32HRe/87zoNLvPtCbqTdNBbanaLPi 2026-03-23 10:46:29.876072+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Clínica Mente Sã"} \N 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0008-0008-0008-000000000008 authenticated authenticated editor@agenciapsi.com.br $2a$06$lcF3sQOKaQOMwo5OTPPpcODcMtjDoUpHw3rOBhJMYow15LoJFLvH6 2026-03-23 14:18:05.215881+00 \N \N \N \N 2026-03-26 19:46:29.070205+00 {"provider": "email", "providers": ["email"]} {"name": "Diana Editora"} \N 2026-03-23 14:18:05.215881+00 2026-03-26 19:46:29.07828+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0006-0006-0006-000000000006 authenticated authenticated saas@agenciapsi.com.br $2a$06$QsvWGUd7HQTv6kSQDbRsiOkNcLM4O2BnQflXPbx3MK9E4RPGz5FvS 2026-03-23 10:46:29.876072+00 \N \N \N \N 2026-03-26 19:46:43.303588+00 {"provider": "email", "providers": ["email"]} {"name": "Admin Plataforma"} \N 2026-03-23 10:46:29.876072+00 2026-03-26 19:46:43.308732+00 \N \N \N 0 \N \N f \N f +\. + + +-- +-- Data for Name: job; Type: TABLE DATA; Schema: cron; Owner: - +-- + +COPY cron.job (jobid, schedule, command, nodename, nodeport, database, username, active, jobname) FROM stdin; +\. + + +-- +-- Data for Name: job_run_details; Type: TABLE DATA; Schema: cron; Owner: - +-- + +COPY cron.job_run_details (jobid, runid, job_pid, database, username, command, status, return_message, start_time, end_time) FROM stdin; +\. + + +-- +-- Data for Name: _db_migrations; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public._db_migrations (id, filename, hash, category, applied_at) FROM stdin; +1 seed_001_fixed.sql 87fc24517f6446f7 seed 2026-03-23 14:15:02.14603+00 +2 seed_002.sql b05d565b35c97300 seed 2026-03-23 14:15:02.45035+00 +3 seed_003.sql 257ef8bba4e319a2 seed 2026-03-23 14:15:02.755322+00 +4 seed_010_plans.sql 0de612f2301e27d3 seed 2026-03-23 14:15:02.974622+00 +5 seed_011_features.sql e7326ac0e33e4fee seed 2026-03-23 14:15:03.261589+00 +6 seed_012_plan_features.sql f0e1b4ab684383f7 seed 2026-03-23 14:15:03.553899+00 +7 seed_013_subscriptions.sql b61e4af59262f3ac seed 2026-03-23 14:15:03.816997+00 +8 seed_014_global_data.sql a7bc086bc6f052ee seed 2026-03-23 14:15:04.080095+00 +9 fix_addon_credits_fk.sql aaff13facb98e4d8 fix 2026-03-23 14:15:04.372331+00 +10 fix_addon_rls_saas_admin.sql 84d85284eb441afc fix 2026-03-23 14:15:04.630692+00 +11 fix_missing_subscriptions.sql f5740f6eef9e5405 fix 2026-03-23 14:15:04.916745+00 +12 fix_notification_templates_rls_admin.sql ede371cbce54e13e fix 2026-03-23 14:15:05.15764+00 +13 fix_seed_patient_groups.sql e9b870ba0ad5f359 fix 2026-03-23 14:15:05.485322+00 +14 fix_subscriptions_validate_scope.sql c814a90d768d339c fix 2026-03-23 14:15:06.461275+00 +15 fix_template_keys_match_populate.sql e0fdd2a420abaeb8 fix 2026-03-23 14:15:06.977464+00 +\. + + +-- +-- Data for Name: addon_credits; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.addon_credits (id, tenant_id, owner_id, addon_type, balance, total_purchased, total_consumed, low_balance_threshold, low_balance_notified, daily_limit, hourly_limit, daily_used, hourly_used, daily_reset_at, hourly_reset_at, from_number_override, expires_at, is_active, created_at, updated_at) FROM stdin; +0f2ed178-d2d1-4bf0-a58c-206be0183c1c bbbbbbbb-0002-0002-0002-000000000002 \N sms 320 320 0 10 f \N \N 0 0 \N \N \N \N t 2026-03-25 00:15:28.741153+00 2026-03-25 00:15:37.596479+00 +\. + + +-- +-- Data for Name: addon_products; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.addon_products (id, slug, name, description, addon_type, icon, credits_amount, price_cents, currency, is_active, is_visible, sort_order, metadata, created_at, updated_at, deleted_at) FROM stdin; +a6cfa47f-e26d-4835-9c14-4629e8afa331 sms_basico SMS Básico 100 créditos SMS. Ideal para quem está começando ou tem poucos pacientes. sms pi pi-comment 100 2500 BRL t t 10 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +56d95e40-9e32-491d-969d-8970f51c05ed sms_essencial SMS Essencial 220 créditos SMS. Para consultórios em crescimento com envio regular de lembretes. sms pi pi-comments 220 5000 BRL t t 20 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +7589b9fc-2ccf-43f0-aea4-abc3edf05473 sms_profissional SMS Profissional 350 créditos SMS. Para quem envia lembretes, confirmações e avisos de cobrança. sms pi pi-send 350 7500 BRL t t 30 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +2dff4229-32a8-4090-8f19-6c3ab836b9c1 sms_premium SMS Premium 500 créditos SMS. Melhor custo-benefício. Clínicas e agenda cheia. sms pi pi-star 500 10000 BRL t t 40 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +\. + + +-- +-- Data for Name: addon_transactions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.addon_transactions (id, tenant_id, owner_id, addon_type, type, amount, balance_before, balance_after, product_id, queue_id, description, admin_user_id, payment_method, payment_reference, price_cents, currency, created_at, metadata) FROM stdin; +46181c84-9f01-4fec-baaf-c10aa935f240 bbbbbbbb-0002-0002-0002-000000000002 \N sms purchase 100 0 100 a6cfa47f-e26d-4835-9c14-4629e8afa331 \N SMS Básico aaaaaaaa-0006-0006-0006-000000000006 manual \N 2500 BRL 2026-03-25 00:15:28.741153+00 {} +a07a56b5-1b6d-46a1-88fd-15623c77f07f bbbbbbbb-0002-0002-0002-000000000002 \N sms purchase 220 100 320 56d95e40-9e32-491d-969d-8970f51c05ed \N SMS Essencial aaaaaaaa-0006-0006-0006-000000000006 manual \N 5000 BRL 2026-03-25 00:15:37.596479+00 {} +\. + + +-- +-- Data for Name: agenda_bloqueios; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_bloqueios (id, owner_id, tenant_id, tipo, titulo, data_inicio, data_fim, hora_inicio, hora_fim, recorrente, dia_semana, observacao, origem, created_at) FROM stdin; +f809fcb6-0369-42fd-985a-5d9976798e88 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 bloqueio Feriado: Sexta-feira Santa 2026-04-03 2026-04-03 \N \N f \N \N agenda_feriado 2026-03-23 11:32:18.225845+00 +\. + + +-- +-- Data for Name: agenda_configuracoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_configuracoes (owner_id, duracao_padrao_minutos, intervalo_padrao_minutos, timezone, usar_horario_admin_custom, admin_inicio_visualizacao, admin_fim_visualizacao, admin_slot_visual_minutos, online_ativo, online_min_antecedencia_horas, online_max_dias_futuro, online_cancelar_ate_horas, online_reagendar_ate_horas, online_limite_agendamentos_futuros, online_modo, online_buffer_antes_min, online_buffer_depois_min, online_modalidade, created_at, updated_at, usar_granularidade_custom, granularidade_min, setup_concluido, setup_concluido_em, agenda_view_mode, agenda_custom_start, agenda_custom_end, session_duration_min, session_break_min, pausas_semanais, setup_clinica_concluido, setup_clinica_concluido_em, tenant_id, jornada_igual_todos, slot_mode, atendimento_mode) FROM stdin; +aaaaaaaa-0005-0005-0005-000000000005 50 0 America/Sao_Paulo f \N \N 30 f 24 60 12 12 1 automatico 0 0 ambos 2026-03-25 18:35:26.138564+00 2026-03-26 17:19:10.818241+00 f \N t 2026-03-26 17:19:10.804+00 full_24h \N \N 40 10 [{"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 0}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 1}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 2}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 3}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 4}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 5}] f \N bbbbbbbb-0005-0005-0005-000000000005 t fixed ambos +aaaaaaaa-0002-0002-0002-000000000002 50 0 America/Sao_Paulo f \N \N 30 f 24 60 12 12 1 automatico 0 0 ambos 2026-03-23 10:58:31.422764+00 2026-03-27 14:18:31.232768+00 f \N t 2026-03-27 14:18:31.221+00 full_24h \N \N 50 10 [{"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 0}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 1}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 2}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 3}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 4}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 5}] f \N bbbbbbbb-0002-0002-0002-000000000002 t fixed ambos +\. + + +-- +-- Data for Name: agenda_eventos; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_eventos (id, owner_id, tipo, status, titulo, observacoes, inicio_em, fim_em, created_at, updated_at, terapeuta_id, tenant_id, visibility_scope, mirror_of_event_id, mirror_source, patient_id, determined_commitment_id, link_online, titulo_custom, extra_fields, recurrence_id, recurrence_date, modalidade, price, billing_contract_id, billed, services_customized, insurance_plan_id, insurance_guide_number, insurance_value, insurance_plan_service_id) FROM stdin; +dbf6b44e-24f1-4cd2-86f5-a594fa39f9d3 aaaaaaaa-0002-0002-0002-000000000002 sessao agendado Otto Rank [Sess??o] \N 2026-03-23 13:00:00+00 2026-03-23 13:50:00+00 2026-03-23 11:33:08.0471+00 2026-03-23 11:33:08.0471+00 \N bbbbbbbb-0002-0002-0002-000000000002 public \N \N 6449e64b-050b-419f-8845-029b6f10a17d 42a8c681-8b32-4608-870f-b617acbe249e \N \N \N \N \N presencial 0.00 \N f f \N \N \N \N +\. + + +-- +-- Data for Name: agenda_excecoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_excecoes (id, owner_id, data, hora_inicio, hora_fim, tipo, motivo, created_at, updated_at, status, fonte, aplicavel_online, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agenda_online_slots; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_online_slots (id, owner_id, weekday, "time", enabled, created_at, updated_at, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agenda_regras_semanais; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_regras_semanais (id, owner_id, dia_semana, hora_inicio, hora_fim, modalidade, ativo, created_at, updated_at, tenant_id) FROM stdin; +a3212bcd-fc80-40c3-b3ea-92d31d1d051a aaaaaaaa-0002-0002-0002-000000000002 0 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +cbeae6bd-1ebd-494d-b5c0-2f3a8bf09e14 aaaaaaaa-0002-0002-0002-000000000002 1 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +dd9ecded-d82c-4d87-a2df-85b89c331001 aaaaaaaa-0002-0002-0002-000000000002 2 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +ce5054c2-f382-4135-8365-06b3dab7ea1c aaaaaaaa-0002-0002-0002-000000000002 3 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +e8e22a8d-4deb-4eeb-a9e8-e1f906030499 aaaaaaaa-0002-0002-0002-000000000002 4 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +265ff3ab-645f-4dd6-8a91-4a3a4d085ff0 aaaaaaaa-0002-0002-0002-000000000002 5 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +cf21b2ed-fbf6-4899-95b6-bee502e7b139 aaaaaaaa-0005-0005-0005-000000000005 0 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +a6fc9552-59dd-4717-9282-129d3297b5e0 aaaaaaaa-0005-0005-0005-000000000005 1 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +c028ea38-75a0-4e5f-a6c5-705264f43826 aaaaaaaa-0005-0005-0005-000000000005 2 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +c674e740-90b9-4ae1-8b9b-56ee6342235e aaaaaaaa-0005-0005-0005-000000000005 3 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +a272ac19-32d2-4c3c-b1a1-fbdedfffc6d3 aaaaaaaa-0005-0005-0005-000000000005 4 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +1a98514b-8508-4539-9e96-6af89b91417d aaaaaaaa-0005-0005-0005-000000000005 5 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +\. + + +-- +-- Data for Name: agenda_slots_bloqueados_semanais; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_slots_bloqueados_semanais (id, owner_id, dia_semana, hora_inicio, motivo, ativo, created_at, updated_at, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agenda_slots_regras; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_slots_regras (id, owner_id, dia_semana, passo_minutos, offset_minutos, buffer_antes_min, buffer_depois_min, min_antecedencia_horas, ativo, created_at, updated_at, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agendador_configuracoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agendador_configuracoes (owner_id, tenant_id, ativo, link_slug, imagem_fundo_url, imagem_header_url, logomarca_url, cor_primaria, nome_exibicao, endereco, botao_como_chegar_ativo, maps_url, modo_aprovacao, modalidade, tipos_habilitados, duracao_sessao_min, antecedencia_minima_horas, prazo_resposta_horas, reserva_horas, pagamento_obrigatorio, pix_chave, pix_countdown_minutos, triagem_motivo, triagem_como_conheceu, verificacao_email, exigir_aceite_lgpd, mensagem_boas_vindas, texto_como_se_preparar, texto_termos_lgpd, created_at, updated_at, pagamento_modo, pagamento_metodos_visiveis) FROM stdin; +\. + + +-- +-- Data for Name: agendador_solicitacoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agendador_solicitacoes (id, owner_id, tenant_id, paciente_nome, paciente_sobrenome, paciente_email, paciente_celular, paciente_cpf, tipo, modalidade, data_solicitada, hora_solicitada, reservado_ate, motivo, como_conheceu, pix_status, pix_pago_em, status, recusado_motivo, autorizado_em, autorizado_por, user_id, patient_id, evento_id, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: billing_contracts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.billing_contracts (id, owner_id, tenant_id, patient_id, type, total_sessions, sessions_used, package_price, amount, billing_interval, active_from, active_to, status, created_at) FROM stdin; +\. + + +-- +-- Data for Name: commitment_services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.commitment_services (id, commitment_id, service_id, quantity, unit_price, discount_pct, discount_flat, final_price, created_at) FROM stdin; +\. + + +-- +-- Data for Name: commitment_time_logs; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.commitment_time_logs (id, tenant_id, commitment_id, calendar_event_id, source, started_at, ended_at, minutes, created_by, created_at) FROM stdin; +\. + + +-- +-- Data for Name: company_profiles; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.company_profiles (id, tenant_id, nome_fantasia, razao_social, tipo_empresa, cnpj, ie, im, cep, logradouro, numero, complemento, bairro, cidade, estado, email, telefone, site, logo_url, redes_sociais, created_at, updated_at) FROM stdin; +9c2039d4-0058-46d0-b60f-29e16459bb85 bbbbbbbb-0002-0002-0002-000000000002 teste \N consultorio \N \N \N 13561-260 Avenida Tancredo de Almeida Neves 457 complemento Parque Santa Mônica São Carlos SP comercial@gmail.com (11) 11111-1111 site http://127.0.0.1:54321/storage/v1/object/public/logos/bbbbbbbb-0002-0002-0002-000000000002/logo.png [{"url": "@perfil", "name": "Instagram"}] 2026-03-27 12:35:48.759848+00 2026-03-27 14:18:20.352667+00 +\. + + +-- +-- Data for Name: determined_commitment_fields; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.determined_commitment_fields (id, tenant_id, commitment_id, key, label, field_type, required, sort_order, created_at, updated_at) FROM stdin; +3f45731e-f025-4a95-b37d-35becf557474 bbbbbbbb-0002-0002-0002-000000000002 5944e5d9-622d-4db1-b73d-0083a6a4a9a1 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +7cb99c51-3de2-4216-be29-1067d1d2a6e8 bbbbbbbb-0002-0002-0002-000000000002 5944e5d9-622d-4db1-b73d-0083a6a4a9a1 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +29a48e4d-b4b9-41e6-8a3f-23e862b11f71 bbbbbbbb-0002-0002-0002-000000000002 5944e5d9-622d-4db1-b73d-0083a6a4a9a1 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +de1bf25c-2c6e-4de1-9be2-9976eb26e9e6 bbbbbbbb-0002-0002-0002-000000000002 717e552c-99cc-4375-b7f2-a1b29f3581e3 supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +0a2f4c96-be82-49a1-b0bc-65d08bf29078 bbbbbbbb-0002-0002-0002-000000000002 717e552c-99cc-4375-b7f2-a1b29f3581e3 topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +ea018b37-ac9a-49a5-93dd-ebc4f2bcc069 bbbbbbbb-0002-0002-0002-000000000002 717e552c-99cc-4375-b7f2-a1b29f3581e3 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4f4fff30-8b01-46cc-a6c9-25e0c623b03c bbbbbbbb-0002-0002-0002-000000000002 b3bf589d-4a72-429f-b9bd-ba426c3f42f1 theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +3ddb7185-eea6-43e4-bab7-686aa996f4e6 bbbbbbbb-0002-0002-0002-000000000002 b3bf589d-4a72-429f-b9bd-ba426c3f42f1 group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +20c208a1-eae7-4021-916f-a3a45f7d9e84 bbbbbbbb-0002-0002-0002-000000000002 b3bf589d-4a72-429f-b9bd-ba426c3f42f1 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +82c2b726-8de7-4bdd-a7ab-0fe519200f2c bbbbbbbb-0002-0002-0002-000000000002 e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +88bb598e-44f1-4090-a09a-21a11e628ab0 bbbbbbbb-0002-0002-0002-000000000002 e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +f0aa7c4c-e26d-42c2-8f90-5e4a87308fe9 bbbbbbbb-0002-0002-0002-000000000002 e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +fa8083ef-7e1f-4e00-84b6-6dd197a31e11 bbbbbbbb-0003-0003-0003-000000000003 cd1076b1-0f3d-460e-842f-1585c0ae2a64 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +2fea846d-3d7f-4946-8b8a-921756d1375d bbbbbbbb-0003-0003-0003-000000000003 cd1076b1-0f3d-460e-842f-1585c0ae2a64 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +7f71d073-7337-40c3-9f2b-80d359ee6825 bbbbbbbb-0003-0003-0003-000000000003 cd1076b1-0f3d-460e-842f-1585c0ae2a64 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +9ea25942-9529-4ffb-99b1-17c8f48fa1b3 bbbbbbbb-0003-0003-0003-000000000003 9ded91f1-1974-4e56-afc6-12b2e8f9456c supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +642ee453-276b-4636-808b-0deed8827416 bbbbbbbb-0003-0003-0003-000000000003 9ded91f1-1974-4e56-afc6-12b2e8f9456c topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +1107588b-dafc-49f0-adf1-1d478410fbab bbbbbbbb-0003-0003-0003-000000000003 9ded91f1-1974-4e56-afc6-12b2e8f9456c notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +2d00519b-2d66-4b66-9471-4edfd7f03152 bbbbbbbb-0003-0003-0003-000000000003 b82819d4-02a1-40c5-bf57-6e2e0425e62d theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +6fb9b8f8-0a50-4267-93ff-a154487d0ee2 bbbbbbbb-0003-0003-0003-000000000003 b82819d4-02a1-40c5-bf57-6e2e0425e62d group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +99d0df7d-20f7-49df-acd8-c359b195348d bbbbbbbb-0003-0003-0003-000000000003 b82819d4-02a1-40c5-bf57-6e2e0425e62d notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +241c3b0f-78c1-4485-9f11-c8fc5fdf6650 bbbbbbbb-0003-0003-0003-000000000003 b5c43dc2-a3a1-40ca-8b75-02ec649fd914 analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +08440a39-027f-4879-8091-7a9b2cf522c1 bbbbbbbb-0003-0003-0003-000000000003 b5c43dc2-a3a1-40ca-8b75-02ec649fd914 focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +d11f4029-c46e-42bf-b7aa-bc3b1cc9874a bbbbbbbb-0003-0003-0003-000000000003 b5c43dc2-a3a1-40ca-8b75-02ec649fd914 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +d2d11b9f-5dcf-4355-bc33-c977ac7591a9 bbbbbbbb-0004-0004-0004-000000000004 2e5f4151-e259-4261-8954-e3d196a28383 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +2d3e3c6e-6ca0-4170-b9ed-e45e470a8738 bbbbbbbb-0004-0004-0004-000000000004 2e5f4151-e259-4261-8954-e3d196a28383 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +a7003c31-46b1-44a2-876e-034f3c61ce8c bbbbbbbb-0004-0004-0004-000000000004 2e5f4151-e259-4261-8954-e3d196a28383 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +29f2cb01-22c2-45c3-ba02-55b908b5ceac bbbbbbbb-0004-0004-0004-000000000004 0d6cb161-8d89-4b18-9323-5631e8cddd8f supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +e0b8f4dd-740c-4162-b988-62fc8216de8c bbbbbbbb-0004-0004-0004-000000000004 0d6cb161-8d89-4b18-9323-5631e8cddd8f topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +187902e7-4497-4ab6-81f7-7dbe177189e9 bbbbbbbb-0004-0004-0004-000000000004 0d6cb161-8d89-4b18-9323-5631e8cddd8f notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4ef4ac8e-adf4-4034-851f-3767ef37a8e4 bbbbbbbb-0004-0004-0004-000000000004 bec193c2-172b-40ef-a5fe-91b7c44c841e theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4972b76d-c080-4989-a4f8-3d3c57a9b96e bbbbbbbb-0004-0004-0004-000000000004 bec193c2-172b-40ef-a5fe-91b7c44c841e group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +fc589056-f8b8-4d96-94f0-7ac49ab53059 bbbbbbbb-0004-0004-0004-000000000004 bec193c2-172b-40ef-a5fe-91b7c44c841e notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4300058b-c0b4-4b86-8f6e-eda6d99553fc bbbbbbbb-0004-0004-0004-000000000004 d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +20add80c-2807-42c9-8c42-40291a49e5df bbbbbbbb-0004-0004-0004-000000000004 d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4d01680a-733e-4c0c-a26a-e19b962fe9fa bbbbbbbb-0004-0004-0004-000000000004 d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +e56ad576-c831-444a-bb21-0bcd74630d48 bbbbbbbb-0005-0005-0005-000000000005 793f5b1d-b218-40a3-aa73-3e1440d9ea66 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +cc36d270-7961-481f-8a8b-99e0b9ef2e64 bbbbbbbb-0005-0005-0005-000000000005 793f5b1d-b218-40a3-aa73-3e1440d9ea66 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +8eb622a4-8ee9-4c34-af09-9dd5c74923fa bbbbbbbb-0005-0005-0005-000000000005 793f5b1d-b218-40a3-aa73-3e1440d9ea66 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +24d0c58f-3382-4ea8-81e7-3dc32f122b90 bbbbbbbb-0005-0005-0005-000000000005 83666a58-2e53-49a6-8b13-73eeb203e5ba supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4993e870-d101-4754-b2bc-18205255d643 bbbbbbbb-0005-0005-0005-000000000005 83666a58-2e53-49a6-8b13-73eeb203e5ba topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +b6e403de-e93a-42e0-832a-e6103a935421 bbbbbbbb-0005-0005-0005-000000000005 83666a58-2e53-49a6-8b13-73eeb203e5ba notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +a552127c-1b16-464c-a8f3-d7bdf054b0a9 bbbbbbbb-0005-0005-0005-000000000005 8c1a77a1-b66f-462e-940d-2f60d080149c theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +be9ecf92-2dc8-4f62-94f3-223b0165c0af bbbbbbbb-0005-0005-0005-000000000005 8c1a77a1-b66f-462e-940d-2f60d080149c group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +7f7a9b35-60f1-4192-bf6c-d91ca42813a1 bbbbbbbb-0005-0005-0005-000000000005 8c1a77a1-b66f-462e-940d-2f60d080149c notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +528d426f-23d4-422f-8624-5b4178ce3a8b bbbbbbbb-0005-0005-0005-000000000005 a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +11a02a30-7a0a-4baf-acdd-2525c2f2cf84 bbbbbbbb-0005-0005-0005-000000000005 a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +f456679b-05d7-4ccc-ae3b-c5f673f8f6f8 bbbbbbbb-0005-0005-0005-000000000005 a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +07ba5c18-9ebb-4b38-bd2f-591b8e4f7f87 bbbbbbbb-0009-0009-0009-000000000009 fc6da673-fd7c-4101-b980-54eb3842804c book Livro text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +92eec7d8-20f3-46e4-8700-c80e9bd90bcb bbbbbbbb-0009-0009-0009-000000000009 fc6da673-fd7c-4101-b980-54eb3842804c author Autor text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +fdf6b850-b627-494e-9cb0-d1a044409b3c bbbbbbbb-0009-0009-0009-000000000009 fc6da673-fd7c-4101-b980-54eb3842804c notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +ef9a9605-9b0f-4671-b056-835c56844cf9 bbbbbbbb-0009-0009-0009-000000000009 3dbb8c2f-6fab-478f-b094-5bc13606a504 supervisor Supervisor text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e19f0ad4-fe52-4595-84aa-cf11828a7ecd bbbbbbbb-0009-0009-0009-000000000009 3dbb8c2f-6fab-478f-b094-5bc13606a504 topic Assunto text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +41a085ac-6849-4e29-8728-79e1a47c1ac5 bbbbbbbb-0009-0009-0009-000000000009 3dbb8c2f-6fab-478f-b094-5bc13606a504 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +8fdaad8b-f31c-4510-9b72-7346026103b9 bbbbbbbb-0009-0009-0009-000000000009 4c4a8ced-d109-443b-bc83-817c858f9bb6 theme Tema text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +9e53f6f5-6770-4f3e-8b0e-cb18c9df54b7 bbbbbbbb-0009-0009-0009-000000000009 4c4a8ced-d109-443b-bc83-817c858f9bb6 group Turma text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +222021b7-6917-4977-a08f-e19532aa2a0e bbbbbbbb-0009-0009-0009-000000000009 4c4a8ced-d109-443b-bc83-817c858f9bb6 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +329eebff-55c4-49e2-87ee-b3d0f418234b bbbbbbbb-0009-0009-0009-000000000009 b180f6e4-39ad-464c-842e-d42dc60cdf13 analyst Analista text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e87c45d8-8f5f-476d-811d-5b5b4a764e07 bbbbbbbb-0009-0009-0009-000000000009 b180f6e4-39ad-464c-842e-d42dc60cdf13 focus Foco text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +2a8c3eff-4eed-4b2d-98e7-50267ebb910e bbbbbbbb-0009-0009-0009-000000000009 b180f6e4-39ad-464c-842e-d42dc60cdf13 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e6109ef3-7409-4268-8ecf-eaff64fa9498 bbbbbbbb-0010-0010-0010-000000000010 fe52a6b2-e86e-47e0-9dc7-9b7e63599cff book Livro text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +2069cb4c-3c49-4a82-ada1-63082d14bba3 bbbbbbbb-0010-0010-0010-000000000010 fe52a6b2-e86e-47e0-9dc7-9b7e63599cff author Autor text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +d8428a90-f534-4794-99a7-1c752d42e0d2 bbbbbbbb-0010-0010-0010-000000000010 fe52a6b2-e86e-47e0-9dc7-9b7e63599cff notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +69435311-e703-4fbb-84b8-d634fd0148fe bbbbbbbb-0010-0010-0010-000000000010 fb2724d6-3938-4b3f-b8ec-aeb4417a4057 supervisor Supervisor text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +2c71883c-dee5-420e-9c7a-d1b1ade908f5 bbbbbbbb-0010-0010-0010-000000000010 fb2724d6-3938-4b3f-b8ec-aeb4417a4057 topic Assunto text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +1cf750d0-449d-4780-8828-bffce53cc651 bbbbbbbb-0010-0010-0010-000000000010 fb2724d6-3938-4b3f-b8ec-aeb4417a4057 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e38226ca-3af4-41c3-a901-9edfc0814e46 bbbbbbbb-0010-0010-0010-000000000010 53193b8a-8af1-4824-a7c6-22cbb7459c45 theme Tema text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +6100b0b2-db5c-4aeb-98e3-3c6f6a20f058 bbbbbbbb-0010-0010-0010-000000000010 53193b8a-8af1-4824-a7c6-22cbb7459c45 group Turma text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +ef948cc1-4e2b-4c7a-9043-956a6341085a bbbbbbbb-0010-0010-0010-000000000010 53193b8a-8af1-4824-a7c6-22cbb7459c45 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +9405625b-f781-4b5d-836c-19371fccefab bbbbbbbb-0010-0010-0010-000000000010 29b5f51c-0833-4902-8c03-ee8bb7836a33 analyst Analista text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +cc414a63-c433-4269-be6c-caad9f83408d bbbbbbbb-0010-0010-0010-000000000010 29b5f51c-0833-4902-8c03-ee8bb7836a33 focus Foco text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +c5c4f4c9-2e74-4f65-b5d9-ee1121005cbc bbbbbbbb-0010-0010-0010-000000000010 29b5f51c-0833-4902-8c03-ee8bb7836a33 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +155f3805-15e9-48fe-9f36-454e589ca2b2 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b567bfb6-7a58-4893-b072-eb23c72948a2 book Livro text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +95283550-683a-4c3d-a7d7-ac66f3b1f3e5 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b567bfb6-7a58-4893-b072-eb23c72948a2 author Autor text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +f293f578-4b69-46e9-b652-9eb535ae904b a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b567bfb6-7a58-4893-b072-eb23c72948a2 notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +9fa8bb78-eed2-4d10-9cab-e527c19137d8 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 supervisor Supervisor text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +972ac699-e2aa-4312-a0c9-1faa8853e38d a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 topic Assunto text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +f70a4cbb-b55a-4661-94de-d322f80f78a7 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +bc4036a8-32c2-4e13-bfde-0a85e21016f8 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 6a3194d1-475a-4131-9d5f-d8a3bb4ad84e theme Tema text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +4a5acde2-4d27-4377-8cf7-f94c25b2b13a a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 6a3194d1-475a-4131-9d5f-d8a3bb4ad84e group Turma text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +fe89c629-de7b-4dfe-b5b4-00954cf5ef4e a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 6a3194d1-475a-4131-9d5f-d8a3bb4ad84e notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +60192c88-c9ef-4650-8591-104e02f22460 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 fcd58365-0042-4df3-b93d-26cbae67d14a analyst Analista text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +dd49a6ca-755e-4eb2-b4e8-9c17393c2e77 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 fcd58365-0042-4df3-b93d-26cbae67d14a focus Foco text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +d2e9d95a-be44-4bde-a853-0e516345421b a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 fcd58365-0042-4df3-b93d-26cbae67d14a notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +c0206c42-b9c0-4fc4-afc1-22a6178750e2 1e98ca49-a46c-4701-847b-145a14d53d19 b5bcbc9e-abf0-4ff3-8960-133c3592de88 book Livro text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +23f30e08-728e-460d-9410-cb1bb24548ce 1e98ca49-a46c-4701-847b-145a14d53d19 b5bcbc9e-abf0-4ff3-8960-133c3592de88 author Autor text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +5406a62c-430a-4959-b0c5-cca804b03f53 1e98ca49-a46c-4701-847b-145a14d53d19 b5bcbc9e-abf0-4ff3-8960-133c3592de88 notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +1248cce1-2f5e-4e16-a193-503d374408f0 1e98ca49-a46c-4701-847b-145a14d53d19 8b0f4d71-f1d2-4c97-bd75-241aae919cfe supervisor Supervisor text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +cb9c6678-be1b-474a-b5a8-f4aaaff51213 1e98ca49-a46c-4701-847b-145a14d53d19 8b0f4d71-f1d2-4c97-bd75-241aae919cfe topic Assunto text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +b1ade16e-28f0-4b3e-b25e-7dcc6c338f7f 1e98ca49-a46c-4701-847b-145a14d53d19 8b0f4d71-f1d2-4c97-bd75-241aae919cfe notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +fd21821a-58db-40a6-8d2c-54e8bf8dfe45 1e98ca49-a46c-4701-847b-145a14d53d19 e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 theme Tema text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +757a5af1-e35e-4f70-a81c-89295d85979e 1e98ca49-a46c-4701-847b-145a14d53d19 e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 group Turma text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +198896b3-8181-428b-9022-938587ac9db7 1e98ca49-a46c-4701-847b-145a14d53d19 e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +ca55ceb1-a1d4-48ee-b82d-d767dac745c6 1e98ca49-a46c-4701-847b-145a14d53d19 871f7d97-2d2c-485b-a631-6a4884e1c1e9 analyst Analista text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +21832e62-718c-405a-b602-1e725330ccb3 1e98ca49-a46c-4701-847b-145a14d53d19 871f7d97-2d2c-485b-a631-6a4884e1c1e9 focus Foco text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +cad4c61b-482c-4d8c-adf1-9bf08d04f90a 1e98ca49-a46c-4701-847b-145a14d53d19 871f7d97-2d2c-485b-a631-6a4884e1c1e9 notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +\. + + +-- +-- Data for Name: determined_commitments; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.determined_commitments (id, tenant_id, created_by, is_native, native_key, is_locked, active, name, description, created_at, updated_at, bg_color, text_color) FROM stdin; +5944e5d9-622d-4db1-b73d-0083a6a4a9a1 bbbbbbbb-0002-0002-0002-000000000002 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +cd1076b1-0f3d-460e-842f-1585c0ae2a64 bbbbbbbb-0003-0003-0003-000000000003 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +b82819d4-02a1-40c5-bf57-6e2e0425e62d bbbbbbbb-0003-0003-0003-000000000003 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +2e5f4151-e259-4261-8954-e3d196a28383 bbbbbbbb-0004-0004-0004-000000000004 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +bec193c2-172b-40ef-a5fe-91b7c44c841e bbbbbbbb-0004-0004-0004-000000000004 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +b3bf589d-4a72-429f-b9bd-ba426c3f42f1 bbbbbbbb-0002-0002-0002-000000000002 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 16:44:21.537071+00 \N \N +793f5b1d-b218-40a3-aa73-3e1440d9ea66 bbbbbbbb-0005-0005-0005-000000000005 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +717e552c-99cc-4375-b7f2-a1b29f3581e3 bbbbbbbb-0002-0002-0002-000000000002 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 16:46:11.099434+00 \N \N +8c1a77a1-b66f-462e-940d-2f60d080149c bbbbbbbb-0005-0005-0005-000000000005 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +fc6da673-fd7c-4101-b980-54eb3842804c bbbbbbbb-0009-0009-0009-000000000009 \N t reading f t Leitura Praticar leitura 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +4c4a8ced-d109-443b-bc83-817c858f9bb6 bbbbbbbb-0009-0009-0009-000000000009 \N t class f f Aula Dar aula 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +fe52a6b2-e86e-47e0-9dc7-9b7e63599cff bbbbbbbb-0010-0010-0010-000000000010 \N t reading f t Leitura Praticar leitura 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +53193b8a-8af1-4824-a7c6-22cbb7459c45 bbbbbbbb-0010-0010-0010-000000000010 \N t class f f Aula Dar aula 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 bbbbbbbb-0002-0002-0002-000000000002 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 20:57:27.372517+00 \N \N +b567bfb6-7a58-4893-b072-eb23c72948a2 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t reading f t Leitura Praticar leitura 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +19072432-4eaf-411e-940f-c505639ec78a bbbbbbbb-0002-0002-0002-000000000002 \N f \N f t test teste 2026-03-24 19:42:19.588131+00 2026-03-24 19:42:19.588131+00 f97316 #ffffff +a4dcb5bf-86ab-4daa-80ca-357b88d7a6ba bbbbbbbb-0002-0002-0002-000000000002 \N f \N f t teste teste 2026-03-24 22:56:02.422706+00 2026-03-24 22:56:02.422706+00 6366f1 #ffffff +be219d8b-b2fb-4cfc-8910-64171cd7692f a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t session t t Sess??o Sess??o com paciente 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t supervision f t Supervis??o Supervis??o 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +6a3194d1-475a-4131-9d5f-d8a3bb4ad84e a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t class f f Aula Dar aula 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +fcd58365-0042-4df3-b93d-26cbae67d14a a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t analysis f t An??lise Pessoal Minha an??lise pessoal 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +65f1a136-ce11-46a0-b452-80f00132319d 1e98ca49-a46c-4701-847b-145a14d53d19 \N t session t t Sess??o Sess??o com paciente 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +b5bcbc9e-abf0-4ff3-8960-133c3592de88 1e98ca49-a46c-4701-847b-145a14d53d19 \N t reading f t Leitura Praticar leitura 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +8b0f4d71-f1d2-4c97-bd75-241aae919cfe 1e98ca49-a46c-4701-847b-145a14d53d19 \N t supervision f t Supervis??o Supervis??o 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 1e98ca49-a46c-4701-847b-145a14d53d19 \N t class f f Aula Dar aula 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +871f7d97-2d2c-485b-a631-6a4884e1c1e9 1e98ca49-a46c-4701-847b-145a14d53d19 \N t analysis f t An??lise Pessoal Minha an??lise pessoal 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +d2ea6a5e-e563-434b-950e-2d27bbf445ec bbbbbbbb-0003-0003-0003-000000000003 \N t session t t Sessão Sess??o com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +21f39315-5171-42ce-848c-129c1955206b bbbbbbbb-0004-0004-0004-000000000004 \N t session t t Sessão Sess??o com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +3276b032-6990-4bfc-942d-9155943c241e bbbbbbbb-0005-0005-0005-000000000005 \N t session t t Sessão Sess??o com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +fb194a3e-06ee-4704-a8cb-fe302dd2528e bbbbbbbb-0009-0009-0009-000000000009 \N t session t t Sessão Sess??o com paciente 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +1936c4af-5757-4c10-ad8f-8a5988287acc bbbbbbbb-0010-0010-0010-000000000010 \N t session t t Sessão Sess??o com paciente 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +42a8c681-8b32-4608-870f-b617acbe249e bbbbbbbb-0002-0002-0002-000000000002 \N t session t t Sessão Sessão com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 6366f1 #ffffff +9ded91f1-1974-4e56-afc6-12b2e8f9456c bbbbbbbb-0003-0003-0003-000000000003 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +0d6cb161-8d89-4b18-9323-5631e8cddd8f bbbbbbbb-0004-0004-0004-000000000004 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +83666a58-2e53-49a6-8b13-73eeb203e5ba bbbbbbbb-0005-0005-0005-000000000005 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +3dbb8c2f-6fab-478f-b094-5bc13606a504 bbbbbbbb-0009-0009-0009-000000000009 \N t supervision f t Supervisão Supervis??o 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +fb2724d6-3938-4b3f-b8ec-aeb4417a4057 bbbbbbbb-0010-0010-0010-000000000010 \N t supervision f t Supervisão Supervis??o 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +b5c43dc2-a3a1-40ca-8b75-02ec649fd914 bbbbbbbb-0003-0003-0003-000000000003 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 bbbbbbbb-0004-0004-0004-000000000004 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad bbbbbbbb-0005-0005-0005-000000000005 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +b180f6e4-39ad-464c-842e-d42dc60cdf13 bbbbbbbb-0009-0009-0009-000000000009 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +29b5f51c-0833-4902-8c03-ee8bb7836a33 bbbbbbbb-0010-0010-0010-000000000010 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +\. + + +-- +-- Data for Name: dev_user_credentials; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.dev_user_credentials (id, user_id, email, password_dev, kind, note, created_at) FROM stdin; +\. + + +-- +-- Data for Name: email_layout_config; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.email_layout_config (id, tenant_id, header_config, footer_config, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: email_templates_global; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.email_templates_global (id, key, domain, channel, subject, body_html, body_text, version, is_active, variables, created_at, updated_at) FROM stdin; +be1e52ec-0c1d-4cbe-97da-9c47ce052631 session.reminder.email session email Lembrete: sua sessão amanhã às {{session_time}} \n

Olá, {{patient_name}}!

\n

Este é um lembrete da sua sessão agendada para {{session_date}} às {{session_time}}.

\n

Modalidade: {{session_modality}}

\n {{#if session_link}}\n

Clique aqui para entrar na sessão online

\n {{/if}}\n

Em caso de necessidade de cancelamento, entre em contato com antecedência.

\n

Até logo,
{{therapist_name}}

\n Olá, {{patient_name}}! Lembrete da sua sessão: {{session_date}} às {{session_time}} ({{session_modality}}). 2 t {"patient_name": "Nome completo do paciente", "session_date": "Data da sessão (ex: 20/03/2026)", "session_link": "Link da videochamada (apenas online)", "session_time": "Horário da sessão (ex: 14:00)", "therapist_name": "Nome do terapeuta", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +bf956245-c79a-4ed0-86f1-e0be57072cc8 session.confirmation.email session email Sessão confirmada — {{session_date}} às {{session_time}} \n

Olá, {{patient_name}}!

\n

Sua sessão foi confirmada com sucesso.

\n
    \n
  • Data: {{session_date}}
  • \n
  • Horário: {{session_time}}
  • \n
  • Modalidade: {{session_modality}}
  • \n {{#if session_address}}
  • Local: {{session_address}}
  • {{/if}}\n
\n

Até lá,
{{therapist_name}}

\n Sessão confirmada: {{session_date}} às {{session_time}} ({{session_modality}}). 1 t {"patient_name": "Nome do paciente", "session_date": "Data da sessão", "session_time": "Horário da sessão", "therapist_name": "Nome do terapeuta", "session_address": "Endereço (apenas presencial)", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +9039f35e-e31f-4bad-a6a5-2b865f1152b3 session.cancellation.email session email Sessão cancelada — {{session_date}} \n

Olá, {{patient_name}}!

\n

Informamos que sua sessão do dia {{session_date}} às {{session_time}} foi cancelada.

\n {{#if cancellation_reason}}

Motivo: {{cancellation_reason}}

{{/if}}\n

Entre em contato para reagendar.

\n

{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Data cancelada", "session_time": "Horário cancelado", "therapist_name": "Nome do terapeuta", "cancellation_reason": "Motivo do cancelamento (opcional)"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +ff36dccc-6fe1-4391-9eac-bd03927b38f5 session.rescheduled.email session email Sessão reagendada — {{session_date}} às {{session_time}} \n

Olá, {{patient_name}}!

\n

Sua sessão foi reagendada para {{session_date}} às {{session_time}}.

\n

Modalidade: {{session_modality}}

\n

Até lá,
{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Nova data", "session_time": "Novo horário", "therapist_name": "Nome do terapeuta", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +f17b10f8-d811-4f83-bf5e-3ed636262f28 intake.received.email intake email Recebemos seu cadastro — {{clinic_name}} \n

Olá, {{patient_name}}!

\n

Recebemos seu cadastro com sucesso. Nossa equipe entrará em contato em breve para dar continuidade ao processo.

\n

Obrigado pela confiança,
{{clinic_name}}

\n \N 1 t {"clinic_name": "Nome da clínica ou terapeuta", "patient_name": "Nome do solicitante"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +5b4a9a8b-bc92-4030-92ec-acbb6f9e7ee0 intake.approved.email intake email Cadastro aprovado — bem-vindo(a)! \n

Olá, {{patient_name}}!

\n

Seu cadastro foi aprovado. Você já pode acessar o portal e agendar sua primeira sessão.

\n

Acessar portal →

\n

Qualquer dúvida, estamos à disposição.
{{therapist_name}}

\n \N 1 t {"portal_link": "Link do portal do paciente", "patient_name": "Nome do paciente", "therapist_name": "Nome do terapeuta"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +04da6880-708a-4018-9b1f-0f398d152e0c intake.rejected.email intake email Atualização sobre seu cadastro — {{clinic_name}} \n

Olá, {{patient_name}}!

\n

Agradecemos seu interesse. Infelizmente não será possível dar continuidade ao seu cadastro no momento.

\n {{#if rejection_reason}}

{{rejection_reason}}

{{/if}}\n

{{clinic_name}}

\n \N 1 t {"clinic_name": "Nome da clínica", "patient_name": "Nome do paciente", "rejection_reason": "Motivo (opcional)"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +3e9ae66a-598c-4763-ab61-f02bb71323a4 scheduler.request_accepted.email session email Sua solicitação foi aceita — {{session_date}} às {{session_time}} \n

Olá, {{patient_name}}!

\n

Sua solicitação de agendamento foi aceita.

\n
    \n
  • Data: {{session_date}}
  • \n
  • Horário: {{session_time}}
  • \n
  • Tipo: {{session_type}}
  • \n
  • Modalidade: {{session_modality}}
  • \n
\n

Até logo,
{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Data confirmada", "session_time": "Horário confirmado", "session_type": "Primeira consulta / Retorno", "therapist_name": "Nome do terapeuta", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +7c7f736d-1ddb-46ba-ab41-f66d2267b730 scheduler.request_rejected.email session email Atualização sobre sua solicitação de agendamento \n

Olá, {{patient_name}}!

\n

Infelizmente não foi possível confirmar sua solicitação de agendamento para {{session_date}}.

\n {{#if rejection_reason}}

Motivo: {{rejection_reason}}

{{/if}}\n

Entre em contato para verificar outros horários disponíveis.

\n

{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Data solicitada", "therapist_name": "Nome do terapeuta", "rejection_reason": "Motivo (opcional)"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +78a33f29-3428-482f-9b0e-362c69ff20a8 system.welcome.email system email Bem-vindo(a) ao {{clinic_name}}! \n

Olá, {{patient_name}}!

\n

Seja bem-vindo(a)! Sua conta foi criada com sucesso.

\n

Acessar minha área →

\n \N 1 t {"clinic_name": "Nome da clínica", "portal_link": "Link do portal", "patient_name": "Nome do paciente"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +21a3fab3-01af-4b16-bd63-50929be5001d system.password_reset.email system email Redefinição de senha \n

Olá, {{patient_name}}!

\n

Recebemos uma solicitação para redefinir sua senha.

\n

Clique aqui para redefinir sua senha →

\n

Se você não solicitou a redefinição, ignore este e-mail.

\n \N 1 t {"reset_link": "Link de redefinição de senha", "patient_name": "Nome do usuário"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +\. + + +-- +-- Data for Name: email_templates_tenant; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.email_templates_tenant (id, tenant_id, owner_id, template_key, subject, body_html, body_text, enabled, synced_version, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: entitlements_invalidation; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.entitlements_invalidation (owner_id, changed_at) FROM stdin; +aaaaaaaa-0002-0002-0002-000000000002 2026-03-27 09:41:23.50964+00 +\. + + +-- +-- Data for Name: features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.features (id, key, description, created_at, descricao, name) FROM stdin; +5e539124-630f-4c2a-a9de-7999317085e6 agenda.view Visualização da agenda 2026-02-21 02:36:01.562728+00 Visualizar agenda Agenda - Visualizar +a74fef14-c9d9-4884-ba45-f81c60e0783a agenda.manage Gerenciamento completo da agenda 2026-02-21 02:35:50.629667+00 Adicionar Compromissos na agenda Agenda - Gerenciar +a56482a1-0787-49da-90a7-e1857488734a patients Módulo de pacientes 2026-03-02 12:35:19.955748+00 Pacientes Pacientes +57f631a1-9ebe-480b-a2cb-144ad32ff5f0 patients.view Visualização de pacientes 2026-02-28 11:15:14.275572+00 Visualizar pacientes Pacientes - Visualizar +4e5bc50b-e339-42fe-9d91-61e8555f83e7 patients.manage Gerenciamento completo de pacientes 2026-02-28 11:15:14.275572+00 Gerenciar pacientes Pacientes - Gerenciar +53a48c3b-0617-4618-adf8-f3a255c51ee4 online_scheduling Sistema de agendamento online 2026-03-01 09:59:15.432733+00 Agendamento online Agendamento Online +5739aa27-b089-4b15-b149-31b13d768825 online_scheduling.manage Gerenciamento do agendamento online 2026-02-15 21:50:02.056357+00 Gerenciar agendamento online (admin) Agendamento Online - Gerenciar +0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 online_scheduling.public Página pública do agendador 2026-02-15 21:50:02.056357+00 Página pública de agendamento Agendamento Online - Público +f5d66212-fd73-4472-a306-07928e5deaec reminders Sistema de lembretes automáticos 2026-03-01 09:59:15.432733+00 Lembretes Lembretes +b3efa25d-60a4-4974-8153-6ec098b3d477 reports_basic Relatórios básicos 2026-03-01 09:59:15.432733+00 Relatórios básicos Relatórios Básicos +bf133ad1-da8e-4ea9-bd66-21901cb50075 reports_advanced Relatórios avançados com exportação 2026-03-01 09:59:15.432733+00 Relatórios avançados Relatórios Avançados +336aeeba-b18e-4e68-8303-d42ba09f4b20 secretary Funcionalidade de secretária 2026-03-01 09:59:15.432733+00 Secretaria Secretária +30c9cdd5-7c8c-44d9-8c0b-614165bb9496 shared_reception Recepção compartilhada entre terapeutas 2026-03-02 12:35:19.955748+00 Recepção / Secretária Recepção Compartilhada +74fc1321-4d17-49c3-b72e-db3a7f4be451 rooms Gerenciamento de salas 2026-03-02 12:35:19.955748+00 Salas / Coworking Salas +c109ad27-0edf-4774-91a7-94dac4faab49 intake_public Formulário de intake público 2026-03-02 12:35:19.955748+00 Link externo de cadastro Intake Público +90e92108-8124-40ee-88a0-f0ecafb57d76 intakes_pro Funcionalidades avançadas de intake 2026-02-15 23:29:55.845638+00 Formulários PRO Intakes PRO +f393178c-284d-422f-b096-8793f85428d5 custom_branding Personalização de marca 2026-03-01 09:59:15.432733+00 Personalização de marca Branding Personalizado +d6f54674-ea8b-484b-af0e-99127a510da2 api_access Acesso via API 2026-03-01 09:59:15.432733+00 Integrações/API Acesso API +a5593d96-dd95-46bb-bef0-bd379b56ad50 audit_log Log de auditoria completo 2026-03-01 09:59:15.432733+00 Auditoria Log de Auditoria +8cc81988-d02a-4542-9cb2-ce2ed7c18d60 sms_reminder Lembretes via SMS 2026-02-15 23:29:55.845638+00 Lembretes por SMS Lembrete SMS +9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 clinic_calendar Visão consolidada do calendário 2026-03-01 09:59:15.432733+00 Agenda da clínica Calendário da Clínica +a830e45b-3bb4-4b17-812d-fe83777a2377 advanced_reports Relatórios avançados da clínica 2026-02-15 23:29:55.845638+00 Relatórios avançados Relatórios Avançados (Clínica) +9ab8bdbb-838b-4946-aa5d-fd9cfdd257b3 supervisor.access Acesso ao módulo de supervisão 2026-03-05 00:58:17.218326+00 Acesso básico ao espaço de supervisão (sala, lista de supervisionados). Supervisor - Acesso +1167b54a-0e93-43a2-94d7-c12e64eb56de supervisor.invite Convidar supervisionados 2026-03-05 00:58:17.218326+00 Permite convidar terapeutas para participar da sala de supervisão. Supervisor - Convidar +761e4495-b46a-4791-9519-86ffe48dc47f supervisor.sessions Gerenciar sessões de supervisão 2026-03-05 00:58:17.218326+00 Agendamento e registro de sessões de supervisão. Supervisor - Sessões +7e82ee01-44f6-4b3f-9861-840c58e13f58 supervisor.reports Relatórios de supervisão 2026-03-05 00:58:17.218326+00 Relatórios avançados de progresso e evolução dos supervisionados. Supervisor - Relatórios +\. + + +-- +-- Data for Name: feriados; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.feriados (id, tenant_id, owner_id, tipo, nome, data, cidade, estado, observacao, bloqueia_sessoes, criado_em) FROM stdin; +\. + + +-- +-- Data for Name: financial_categories; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.financial_categories (id, user_id, name, type, color, icon, sort_order, created_at) FROM stdin; +\. + + +-- +-- Data for Name: financial_exceptions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.financial_exceptions (id, owner_id, tenant_id, exception_type, charge_mode, charge_value, charge_pct, min_hours_notice, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: financial_records; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.financial_records (id, owner_id, tenant_id, type, amount, description, category, payment_method, paid_at, due_date, installments, installment_number, installment_group, agenda_evento_id, patient_id, clinic_fee_pct, clinic_fee_amount, insurance_plan_id, notes, tags, created_at, updated_at, deleted_at, discount_amount, final_amount, status, category_id) FROM stdin; +d51c8380-812a-45a7-8154-c799f4f95723 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 receita 1.00 \N \N \N \N 2026-03-25 1 1 \N \N 76cec0bb-1e63-45bf-9b03-feaaa2a5d18d 0.00 0.00 \N \N \N 2026-03-24 22:57:09.509577+00 2026-03-24 22:57:09.509577+00 \N 0.00 1.00 pending \N +\. + + +-- +-- Data for Name: global_notices; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.global_notices (id, title, message, variant, roles, contexts, starts_at, ends_at, is_active, priority, dismissible, persist_dismiss, dismiss_scope, show_once, max_views, cooldown_minutes, version, action_type, action_label, action_url, action_route, views_count, clicks_count, created_at, updated_at, created_by, content_align, link_target) FROM stdin; +\. + + +-- +-- Data for Name: insurance_plan_services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.insurance_plan_services (id, insurance_plan_id, name, value, active, created_at, updated_at) FROM stdin; +b24c1a29-a5b3-4676-94b0-8effa89c672a ec7aa65f-cbd2-48a5-9ec0-368a788438a7 procedimento 1111.00 t 2026-03-24 12:21:29.140157+00 2026-03-24 12:21:29.140157+00 +\. + + +-- +-- Data for Name: insurance_plans; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.insurance_plans (id, owner_id, tenant_id, name, notes, default_value, active, created_at, updated_at) FROM stdin; +ec7aa65f-cbd2-48a5-9ec0-368a788438a7 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste asd \N t 2026-03-24 12:21:20.501533+00 2026-03-24 12:21:20.501533+00 +\. + + +-- +-- Data for Name: login_carousel_slides; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.login_carousel_slides (id, title, body, icon, ordem, ativo, created_at, updated_at) FROM stdin; +0af63b3a-1819-4384-bf94-b29cbe84aca3 Gestão clínica simplificada Agendamentos, prontuários e sessões em um único painel. Foco no que importa: seus pacientes. pi-calendar-clock 0 t 2026-03-23 14:18:08.414378+00 2026-03-23 14:18:08.414378+00 +c02309d3-85cf-452f-bb85-363889aea9f3 Gestão clínica simplificada Gerencie agenda, pacientes e financeiro em um só lugar. Simples, rápido e seguro. pi-users 1 t 2026-03-23 14:18:08.414378+00 2026-03-23 14:18:08.414378+00 +763a600e-987c-4e42-8f62-29b5dea59c39 Múltiplos profissionais, uma só plataforma Ideal para clínicas com vários terapeutas. Cada profissional com sua agenda e seus pacientes. pi-shield 2 t 2026-03-23 14:18:08.414378+00 2026-03-23 14:18:08.414378+00 +\. + + +-- +-- Data for Name: module_features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.module_features (module_id, feature_id, enabled, limits, created_at) FROM stdin; +\. + + +-- +-- Data for Name: modules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.modules (id, key, name, description, is_active, created_at) FROM stdin; +\. + + +-- +-- Data for Name: notice_dismissals; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notice_dismissals (id, notice_id, user_id, version, dismissed_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_channels; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_channels (id, tenant_id, owner_id, channel, provider, is_active, display_name, sender_address, credentials, connection_status, last_health_check, metadata, created_at, updated_at, deleted_at, twilio_subaccount_sid, twilio_phone_number, twilio_phone_sid, webhook_url, cost_per_message_usd, price_per_message_brl, provisioned_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_logs; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_logs (id, tenant_id, owner_id, queue_id, agenda_evento_id, patient_id, channel, template_key, schedule_key, recipient_address, resolved_message, resolved_vars, status, provider, provider_message_id, provider_status, provider_response, sent_at, delivered_at, read_at, failed_at, failure_reason, estimated_cost_brl, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_preferences; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_preferences (id, tenant_id, owner_id, patient_id, whatsapp_opt_in, email_opt_in, sms_opt_in, preferred_time_start, preferred_time_end, lgpd_consent_given, lgpd_consent_date, lgpd_consent_version, lgpd_consent_ip, lgpd_opt_out_date, lgpd_opt_out_reason, created_at, updated_at, deleted_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_queue; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_queue (id, tenant_id, owner_id, agenda_evento_id, patient_id, channel, template_key, schedule_key, resolved_vars, recipient_address, status, scheduled_at, sent_at, next_retry_at, attempts, max_attempts, last_error, idempotency_key, provider_message_id, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_schedules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_schedules (id, tenant_id, owner_id, schedule_key, event_type, trigger_type, offset_minutes, whatsapp_enabled, email_enabled, sms_enabled, allowed_time_start, allowed_time_end, skip_weekends, skip_holidays, is_active, sort_order, created_at, updated_at, deleted_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_templates; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_templates (id, tenant_id, owner_id, key, domain, channel, event_type, body_text, meta_template_name, meta_template_namespace, meta_components, meta_status, variables, version, is_active, is_default, created_at, updated_at, deleted_at) FROM stdin; +37311b1a-2919-4f38-8dbc-7ccf81942062 \N \N session.reminder.whatsapp session whatsapp lembrete_sessao Olá {{nome_paciente}}! 👋\n\nLembrete: você tem sessão agendada para *{{data_sessao}}* às *{{hora_sessao}}* com {{nome_terapeuta}}.\n\n📋 {{modalidade}}\n\nPara confirmar, responda ✅\nPara cancelar, responda ❌\n\nSe precisar remarcar, entre em contato.\n\n_Responda SAIR para não receber mais lembretes._ \N \N \N draft ["nome_paciente", "data_sessao", "hora_sessao", "nome_terapeuta", "modalidade"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +4e61a64d-b2f9-49de-9d27-6496ddba8aef \N \N session.confirmation.whatsapp session whatsapp confirmacao_sessao ✅ Sessão confirmada!\n\nOlá {{nome_paciente}}, sua sessão com {{nome_terapeuta}} foi confirmada:\n\n📅 {{data_sessao}} às {{hora_sessao}}\n📋 {{modalidade}}\n\nTe esperamos! \N \N \N draft ["nome_paciente", "data_sessao", "hora_sessao", "nome_terapeuta", "modalidade"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +262e9dba-0324-478f-9d76-3faba555ec3c \N \N session.cancellation.whatsapp session whatsapp cancelamento_sessao Olá {{nome_paciente}},\n\nSua sessão de {{data_sessao}} às {{hora_sessao}} com {{nome_terapeuta}} foi *cancelada*.\n\nSe desejar reagendar, entre em contato.\n\nAtenciosamente,\n{{nome_terapeuta}} \N \N \N draft ["nome_paciente", "data_sessao", "hora_sessao", "nome_terapeuta"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +2c5331f9-6728-4cea-b4cc-e9b2659f5362 \N \N session.reminder.sms session sms lembrete_sessao Lembrete: sessao em {{data_sessao}} as {{hora_sessao}} com {{nome_terapeuta}}. Confirme respondendo OK. \N \N \N draft ["data_sessao", "hora_sessao", "nome_terapeuta"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +3e4386ba-2790-41e8-9b92-3b6beab549d8 \N \N session.lembrete.whatsapp session whatsapp lembrete_sessao Olá, {{nome_paciente}}! 👋\n\nLembrete da sua sessão com {{nome_terapeuta}}.\n\n📅 Data: {{data_sessao}}\n🕐 Hora: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nQualquer dúvida, entre em contato. Até lá! 💚 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +eb218e05-4ea0-4884-acd2-a766d3986d04 \N \N session.lembrete_2h.whatsapp session whatsapp lembrete_sessao Olá, {{nome_paciente}}! Sua sessão com {{nome_terapeuta}} começa em 2 horas.\n\n🕐 Hora: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nAté já! 😊 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +4574eb97-aeda-4cbb-a7e9-243c8f8c0b17 \N \N session.confirmacao.whatsapp session whatsapp confirmacao_sessao Olá, {{nome_paciente}}! ✅\n\nSua sessão foi confirmada!\n\n📅 Data: {{data_sessao}}\n🕐 Hora: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nAté lá! 💚 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade", "link_confirmacao"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +b321696c-96e2-4d16-8f8d-aa46d98945b8 \N \N session.cancelamento.whatsapp session whatsapp cancelamento_sessao Olá, {{nome_paciente}}. Infelizmente a sessão do dia {{data_sessao}} às {{hora_sessao}} foi cancelada.\n\nEntre em contato para reagendar. 🙏 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +216c6404-6e6c-4cb6-b5bd-486c8abc3c48 \N \N session.reagendamento.whatsapp session whatsapp reagendamento Olá, {{nome_paciente}}! Sua sessão foi reagendada.\n\n📅 Nova data: {{data_sessao}}\n🕐 Novo horário: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nAté lá! 😊 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +48a93a15-9675-49a7-a2d7-3b9b589e14f5 \N \N cobranca.pendente.whatsapp billing whatsapp cobranca_pendente Olá, {{nome_paciente}}. Identificamos uma cobrança pendente no valor de {{valor_sessao}} referente à sua sessão do dia {{data_sessao}}.\n\nPor favor, entre em contato para regularizar. 🙏 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade", "valor_sessao"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +26edef88-2d45-4ee2-8496-30f76f35cb77 \N \N sistema.boas_vindas.whatsapp system whatsapp boas_vindas_paciente Olá, {{nome_paciente}}! 🎉\n\nSeja muito bem-vindo(a)! Estamos felizes em ter você aqui.\n\nEm caso de dúvidas, estamos à disposição. Até a nossa primeira sessão! 💚 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +e66ef5f5-ab3a-46c1-8446-25b55487a017 \N \N session.confirmation.sms session sms confirmacao_sessao Sessão confirmada! {{session_date}} às {{session_time}} ({{session_modality}}) com {{therapist_name}}. \N \N \N draft ["patient_name", "session_date", "session_time", "session_modality", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +aae09094-82cb-406f-8015-c42cd24afded \N \N session.cancellation.sms session sms cancelamento_sessao Sua sessão de {{session_date}} às {{session_time}} foi cancelada. Entre em contato para reagendar. — {{therapist_name}} \N \N \N draft ["patient_name", "session_date", "session_time", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +c8521ea4-31c0-4015-8923-7ef57b3f8b47 \N \N session.rescheduled.sms session sms reagendamento Sua sessão foi reagendada para {{session_date}} às {{session_time}} ({{session_modality}}). — {{therapist_name}} \N \N \N draft ["patient_name", "session_date", "session_time", "session_modality", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +ba32ee4e-6548-48de-bf83-b0cb11a00761 \N \N intake.received.sms intake sms intake_recebido Olá {{patient_name}}, recebemos seu cadastro. Em breve entraremos em contato. — {{clinic_name}} \N \N \N draft ["patient_name", "clinic_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +7ccd6a04-8b89-4238-bb97-d78b0d56312f \N \N intake.approved.sms intake sms intake_aprovado Olá {{patient_name}}, seu cadastro foi aprovado! Acesse o portal para agendar sua sessão. — {{therapist_name}} \N \N \N draft ["patient_name", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +\. + + +-- +-- Data for Name: notifications; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notifications (id, owner_id, tenant_id, type, ref_id, ref_table, payload, read_at, archived, created_at) FROM stdin; +de9453b5-688c-411a-9ea1-c03ec3afea56 aaaaaaaa-0002-0002-0002-000000000002 \N new_patient f94e4a39-c92b-4f9b-82b4-1a2956f2140d patient_intake_requests {"title": "Novo cadastro externo", "detail": "Yasmin Gomes Ferreira", "deeplink": "/therapist/patients/cadastro/recebidos", "avatar_initials": "YA"} \N f 2026-03-23 23:25:39.670692+00 +efb5e6fd-ab89-46d9-b9d5-3534199b10e1 aaaaaaaa-0002-0002-0002-000000000002 \N new_patient 208f1883-a9d8-4056-831d-a22aec679cfb patient_intake_requests {"title": "Novo cadastro externo", "detail": "Carla Lima Almeida", "deeplink": "/therapist/patients/cadastro/recebidos", "avatar_initials": "CA"} 2026-03-23 23:34:19.18+00 f 2026-03-23 23:25:46.914188+00 +\. + + +-- +-- Data for Name: owner_users; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.owner_users (owner_id, user_id, role, created_at) FROM stdin; +\. + + +-- +-- Data for Name: patient_discounts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_discounts (id, owner_id, tenant_id, patient_id, discount_pct, discount_flat, reason, active, active_from, active_to, created_at) FROM stdin; +\. + + +-- +-- Data for Name: patient_group_patient; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_group_patient (patient_group_id, patient_id, created_at, tenant_id) FROM stdin; +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 2026-03-25 20:47:18.396593+00 bbbbbbbb-0002-0002-0002-000000000002 +a5b1e98b-e951-4f8a-b8ab-8221bf74342f 4fdd639d-5f32-453f-9092-74b183c8bbfe 2026-03-25 20:47:43.917435+00 bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 2aaa34f7-c770-4d78-a2a2-c77bd4771c6a 2026-03-25 20:58:12.198254+00 bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 c193905f-e70c-4935-aec3-b9c161c6044c 2026-03-25 20:58:37.212766+00 bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: patient_groups; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_groups (id, nome, descricao, cor, is_active, is_system, owner_id, created_at, updated_at, therapist_id, tenant_id) FROM stdin; +a5b1e98b-e951-4f8a-b8ab-8221bf74342f teste \N #22c55e t f aaaaaaaa-0002-0002-0002-000000000002 2026-03-23 15:35:37.961391+00 2026-03-23 17:13:24.546376+00 \N bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 teste 2 \N \N t f aaaaaaaa-0002-0002-0002-000000000002 2026-03-24 22:43:26.378387+00 2026-03-24 22:43:26.378387+00 \N bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: patient_intake_requests; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_intake_requests (id, owner_id, token, consent, status, created_at, converted_patient_id, rejected_reason, updated_at, cpf, rg, cep, nome_completo, email_principal, telefone, pais, cidade, estado, endereco, numero, bairro, complemento, data_nascimento, naturalidade, genero, estado_civil, onde_nos_conheceu, encaminhado_por, observacoes, notas_internas, email_alternativo, telefone_alternativo, profissao, escolaridade, nacionalidade, avatar_url, tenant_id) FROM stdin; +208f1883-a9d8-4056-831d-a22aec679cfb aaaaaaaa-0002-0002-0002-000000000002 9c0780f2-bf0a-442e-9aa5-787c2f585f5c t converted 2026-03-23 23:25:46.914188+00 f816b137-fb45-471c-81d2-bda8504b4f00 \N 2026-03-23 23:29:46.973175+00 68948962086 98.292.802-5 13561260 Carla Lima Almeida carla.lima.almeida.336@email.com 57973277841 Brasil São Carlos SP Avenida Tancredo de Almeida Neves 7363 Parque Santa Mônica Apto 122 1966-10-10 Campinas other single Outro \N Tenho disponibilidade no período da noite. \N \N \N Enfermeira Superior completo \N \N \N +f94e4a39-c92b-4f9b-82b4-1a2956f2140d aaaaaaaa-0002-0002-0002-000000000002 9c0780f2-bf0a-442e-9aa5-787c2f585f5c t converted 2026-03-23 23:25:39.670692+00 4214ea1c-c387-47ba-922d-5a492aca2ee7 \N 2026-03-23 23:30:08.162018+00 16487081612 43.076.886-9 13561260 Yasmin Gomes Ferreira yasmin.gomes.ferreira.69@email.com 12947152592 Brasil São Carlos SP Avenida Tancredo de Almeida Neves 8346 Parque Santa Mônica \N 1992-03-02 Bauru na single Google \N Cadastro realizado via link externo. \N \N \N Professora Pós-graduação \N \N \N +\. + + +-- +-- Data for Name: patient_invites; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_invites (id, owner_id, token, active, expires_at, max_uses, uses, created_at, tenant_id) FROM stdin; +d04d07d7-736c-4a28-8102-347cd2987bdd aaaaaaaa-0002-0002-0002-000000000002 9c0780f2-bf0a-442e-9aa5-787c2f585f5c t \N \N 0 2026-03-23 16:52:59.16431+00 \N +\. + + +-- +-- Data for Name: patient_patient_tag; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_patient_tag (owner_id, patient_id, tag_id, created_at, tenant_id) FROM stdin; +aaaaaaaa-0002-0002-0002-000000000002 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-25 20:47:18.644356+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-25 20:47:18.644356+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 4fdd639d-5f32-453f-9092-74b183c8bbfe 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-25 20:47:44.202242+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 2aaa34f7-c770-4d78-a2a2-c77bd4771c6a d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-25 20:58:12.518944+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 c193905f-e70c-4935-aec3-b9c161c6044c 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-25 20:58:37.551427+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 c193905f-e70c-4935-aec3-b9c161c6044c d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-25 20:58:37.551427+00 bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: patient_tags; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_tags (id, owner_id, nome, cor, is_padrao, created_at, updated_at, tenant_id) FROM stdin; +69826bd2-005f-414f-a7fe-74b873e88012 aaaaaaaa-0002-0002-0002-000000000002 teste #b53e3e f 2026-03-23 16:56:08.12277+00 2026-03-23 22:54:26.043013+00 bbbbbbbb-0002-0002-0002-000000000002 +d39ec5c3-ae75-411c-9f2c-2d19f017b53b aaaaaaaa-0002-0002-0002-000000000002 teste2 #ef4444 f 2026-03-23 23:34:11.517479+00 \N bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: patients; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patients (id, nome_completo, email_principal, telefone, created_at, owner_id, avatar_url, status, last_attended_at, is_native, naturalidade, data_nascimento, rg, cpf, identification_color, genero, estado_civil, email_alternativo, pais, cep, cidade, estado, endereco, numero, bairro, complemento, escolaridade, profissao, nome_parente, grau_parentesco, telefone_alternativo, onde_nos_conheceu, encaminhado_por, nome_responsavel, telefone_responsavel, cpf_responsavel, observacao_responsavel, cobranca_no_responsavel, observacoes, notas_internas, updated_at, telefone_parente, tenant_id, responsible_member_id, user_id, patient_scope, therapist_member_id) FROM stdin; +6449e64b-050b-419f-8845-029b6f10a17d Otto Rank otto.rank.437@example.com 86363331874 2026-03-23 11:33:02.010795+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-23 11:33:02.010795+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +76cec0bb-1e63-45bf-9b03-feaaa2a5d18d Peter Fonagy peter.fonagy.466@example.com 88283303064 2026-03-24 10:20:35.019712+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-24 10:20:35.019712+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 Carla Lima Souza carla.lima.souza.882@email.com 55327597657 2026-03-25 20:47:18.217202+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Bauru 1980-10-09 498989206 41727305582 \N Feminino Casado(a) alt.709@email.com Brasil 73591-841 Santos SP Av. Brasil 5688 Jardim Paulista Apto 637 Superior completo Estudante Henrique Costa Irmão 55216555751 Site Ana Ribeiro Yasmin Araújo Lima 55209246525 85070073257 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:47:18.217202+00 55979769772 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +d8a170f0-c67e-4e6d-8dc1-b51423d20889 Anna Freud anna.freud.323@example.com 96307749614 2026-03-25 20:47:35.342234+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-25 20:47:35.342234+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +4fdd639d-5f32-453f-9092-74b183c8bbfe Daniel Oliveira Silva daniel.oliveira.silva.779@email.com 55629968595 2026-03-25 20:47:43.705237+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Ribeirão Preto 2018-06-14 644776179 07710834329 \N Feminino Solteiro(a) alt.72@email.com Brasil 43519-831 Ribeirão Preto RS Rua XV de Novembro 5937 Vila Prado Apto 78 Superior incompleto Autônomo Felipe Lima Irmã 55619689156 Outro Vanessa Martins Vanessa Carvalho Barbosa 55209874519 24378696207 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:47:43.705237+00 55738955502 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +8e33a4dc-4bfb-4cce-aec0-fe5a5c91300f John Bowlby john.bowlby.398@example.com 84123228043 2026-03-25 20:48:39.752714+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-25 20:48:39.752714+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +2aaa34f7-c770-4d78-a2a2-c77bd4771c6a Yasmin Martins Lima yasmin.martins.lima.810@email.com 55979361027 2026-03-25 20:58:11.897509+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Santos 1950-10-11 25599572 65091040676 \N Prefere não informar Divorciado(a) alt.364@email.com Brasil 55632-609 Santos RJ Av. Brasil 1268 Jardim Paulista Apto 117 Superior incompleto Autônomo Bruno Martins Pai 55819227599 Google Marcos Martins Ana Oliveira Martins 55129232266 29325344165 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:58:11.897509+00 55529945515 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +c193905f-e70c-4935-aec3-b9c161c6044c Otávio Souza Ferreira888 otavio.souza.ferreira.212@email.com 55519100542 2026-03-25 20:58:37.007581+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Campinas 2018-03-10 249907937 85207459899 \N Feminino Solteiro(a) alt.523@email.com Brasil 80355-723 Araraquara RS Rua XV de Novembro 2199 Vila Prado Apto 249 Ensino Médio Professora Felipe Carvalho Pai 55759833223 Threads Yasmin Carvalho Sabrina Martins Santos 55749688637 89130651778 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:58:37.007581+00 55225172394 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N +\. + + +-- +-- Data for Name: payment_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.payment_settings (id, owner_id, tenant_id, pix_ativo, pix_tipo, pix_chave, pix_nome_titular, deposito_ativo, deposito_banco, deposito_agencia, deposito_conta, deposito_tipo_conta, deposito_titular, deposito_cpf_cnpj, dinheiro_ativo, cartao_ativo, cartao_instrucao, convenio_ativo, convenio_lista, observacoes_pagamento, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: plan_features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_features (plan_id, feature_id, enabled, limits, created_at) FROM stdin; +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 t {"max_patients": 9999, "max_therapists": 999} 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a56482a1-0787-49da-90a7-e1857488734a t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 9999} 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 bf133ad1-da8e-4ea9-bd66-21901cb50075 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a830e45b-3bb4-4b17-812d-fe83777a2377 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 9999} 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 336aeeba-b18e-4e68-8303-d42ba09f4b20 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 30c9cdd5-7c8c-44d9-8c0b-614165bb9496 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 74fc1321-4d17-49c3-b72e-db3a7f4be451 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 90e92108-8124-40ee-88a0-f0ecafb57d76 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 f393178c-284d-422f-b096-8793f85428d5 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 d6f54674-ea8b-484b-af0e-99127a510da2 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a5593d96-dd95-46bb-bef0-bd379b56ad50 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 t {"max_patients": 30, "max_therapists": 5} 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a56482a1-0787-49da-90a7-e1857488734a t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 40} 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 bf133ad1-da8e-4ea9-bd66-21901cb50075 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a830e45b-3bb4-4b17-812d-fe83777a2377 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 50} 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 90e92108-8124-40ee-88a0-f0ecafb57d76 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 336aeeba-b18e-4e68-8303-d42ba09f4b20 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 30c9cdd5-7c8c-44d9-8c0b-614165bb9496 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 74fc1321-4d17-49c3-b72e-db3a7f4be451 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 f393178c-284d-422f-b096-8793f85428d5 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 d6f54674-ea8b-484b-af0e-99127a510da2 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a5593d96-dd95-46bb-bef0-bd379b56ad50 f \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 9999} 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 bf133ad1-da8e-4ea9-bd66-21901cb50075 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 a830e45b-3bb4-4b17-812d-fe83777a2377 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 9999} 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 336aeeba-b18e-4e68-8303-d42ba09f4b20 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 90e92108-8124-40ee-88a0-f0ecafb57d76 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 f393178c-284d-422f-b096-8793f85428d5 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 d6f54674-ea8b-484b-af0e-99127a510da2 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 a5593d96-dd95-46bb-bef0-bd379b56ad50 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 40} 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a bf133ad1-da8e-4ea9-bd66-21901cb50075 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 50} 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 90e92108-8124-40ee-88a0-f0ecafb57d76 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a f393178c-284d-422f-b096-8793f85428d5 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a d6f54674-ea8b-484b-af0e-99127a510da2 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a a5593d96-dd95-46bb-bef0-bd379b56ad50 f \N 2026-03-23 14:18:07.731209+00 +8c4895a3-e12d-48de-a078-efb8a4ea2eb2 9ab8bdbb-838b-4946-aa5d-fd9cfdd257b3 t \N 2026-03-23 14:18:07.731209+00 +8c4895a3-e12d-48de-a078-efb8a4ea2eb2 1167b54a-0e93-43a2-94d7-c12e64eb56de t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 9ab8bdbb-838b-4946-aa5d-fd9cfdd257b3 t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 1167b54a-0e93-43a2-94d7-c12e64eb56de t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 761e4495-b46a-4791-9519-86ffe48dc47f t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 7e82ee01-44f6-4b3f-9861-840c58e13f58 t \N 2026-03-23 14:18:07.731209+00 +\. + + +-- +-- Data for Name: plan_prices; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_prices (id, plan_id, currency, "interval", amount_cents, is_active, active_from, active_to, source, provider, provider_price_id, created_at) FROM stdin; +37510504-4617-4421-9979-4249778bd5ae 82067ba7-16f0-4803-b36f-4c4e8919d4b4 BRL month 4900 t 2026-03-01 09:25:03.878498+00 \N manual \N \N 2026-03-01 09:25:03.878498+00 +225afd5a-9f30-46bc-a0df-5eb8f91660cb 82067ba7-16f0-4803-b36f-4c4e8919d4b4 BRL year 49000 t 2026-03-01 09:25:03.878498+00 \N manual \N \N 2026-03-01 09:25:03.878498+00 +124779b4-362d-4890-9631-747021ecc1c0 a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 BRL month 14900 t 2026-03-01 09:30:06.50975+00 \N manual \N \N 2026-03-01 09:30:06.50975+00 +73908784-6299-45c8-b547-e1556b45c292 a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 BRL year 149000 t 2026-03-01 09:30:06.50975+00 \N manual \N \N 2026-03-01 09:30:06.50975+00 +\. + + +-- +-- Data for Name: plan_public; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_public (plan_id, public_name, public_description, badge, is_featured, is_visible, sort_order, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: plan_public_bullets; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_public_bullets (id, plan_id, text, sort_order, highlight, created_at) FROM stdin; +\. + + +-- +-- Data for Name: plans; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plans (id, key, name, description, is_active, created_at, price_cents, currency, billing_interval, target, max_supervisees) FROM stdin; +984c1f29-a975-4208-93ac-2118ed1039b7 patient_free Paciente Free Plano gratuito para pacientes t 2026-03-03 22:40:11.413107+00 0 BRL month patient \N +c56fe2a8-2c17-4048-adc7-ff7fbd89461a therapist_free THERAPIST FREE Plano gratuito para terapeutas. t 2026-03-01 09:40:48.439668+00 0 BRL month therapist \N +82067ba7-16f0-4803-b36f-4c4e8919d4b4 therapist_pro THERAPIST PRO Plano completo para terapeutas. t 2026-03-01 09:25:03.878498+00 4900 BRL month therapist \N +01a5867f-0705-4714-ac97-a23470949157 clinic_free CLINIC FREE Plano gratuito para clínicas iniciarem. t 2026-03-01 09:25:03.878498+00 0 BRL month clinic \N +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 clinic_pro CLINIC PRO Plano completo para clínicas. t 2026-03-01 09:30:06.50975+00 14900 BRL month clinic \N +8c4895a3-e12d-48de-a078-efb8a4ea2eb2 supervisor_free Supervisor Free Plano gratuito de supervisão. Até 3 terapeutas supervisionados. t 2026-03-05 00:58:17.218326+00 0 BRL month supervisor 3 +ca28e46c-0687-45d5-9406-0a0f56a5b625 supervisor_pro Supervisor PRO Plano profissional de supervisão. Até 20 terapeutas supervisionados. t 2026-03-05 00:58:17.218326+00 0 BRL month supervisor 20 +\. + + +-- +-- Data for Name: professional_pricing; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.professional_pricing (id, owner_id, tenant_id, determined_commitment_id, price, notes, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: profiles; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.profiles (id, role, full_name, created_at, updated_at, avatar_url, phone, bio, language, timezone, notify_system_email, notify_reminders, notify_news, account_type, platform_roles, nickname, work_description, work_description_other, site_url, social_instagram, social_youtube, social_facebook, social_x, social_custom) FROM stdin; +aaaaaaaa-0001-0001-0001-000000000001 portal_user Ana Paciente 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N pt-BR America/Sao_Paulo t t f patient {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0002-0002-0002-000000000002 tenant_member Leonardo Nohama223 2026-03-23 10:46:29.876072+00 2026-03-27 14:18:26.567019+00 http://127.0.0.1:54321/storage/v1/object/public/avatars/aaaaaaaa-0002-0002-0002-000000000002/avatar.webp (11) 11111-1111 Bio 2asd pt-BR America/Sao_Paulo t t t therapist {} Léo psicologo_clinico \N \N \N \N \N \N [] +aaaaaaaa-0006-0006-0006-000000000006 saas_admin Admin Plataforma 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N pt-BR America/Sao_Paulo t t f free {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0007-0007-0007-000000000007 tenant_member Carlos Supervisor 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0009-0009-0009-000000000009 tenant_member Eva Terapeuta 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0010-0010-0010-000000000010 tenant_member Felipe Terapeuta 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0011-0011-0011-000000000011 tenant_member Gabriela Secretária 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0003-0003-0003-000000000003 tenant_member Clínica Espaço Psi 2026-03-23 10:46:29.876072+00 2026-03-23 14:18:43.066684+00 \N \N \N pt-BR America/Sao_Paulo t t f clinic {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0004-0004-0004-000000000004 tenant_member Clínica Mente Sã 2026-03-23 10:46:29.876072+00 2026-03-23 14:18:59.312611+00 \N \N \N pt-BR America/Sao_Paulo t t f clinic {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0005-0005-0005-000000000005 tenant_member Leonardo Nohama 2026-03-23 10:46:29.876072+00 2026-03-26 17:19:07.27483+00 \N (16) 98828-0038 bio curta pt-BR America/Sao_Paulo t t f clinic {} Léo psicanalista \N \N \N \N \N \N [] +aaaaaaaa-0008-0008-0008-000000000008 tenant_member Diana Editora 2026-03-23 14:18:05.215881+00 2026-03-26 19:46:29.161046+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {editor} \N \N \N \N \N \N \N \N [] +384a69d8-b7cd-40ac-9d3c-764c93532b66 portal_user \N 2026-03-26 19:47:30.477551+00 2026-03-26 19:47:30.68134+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +\. + + +-- +-- Data for Name: recurrence_exceptions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.recurrence_exceptions (id, recurrence_id, tenant_id, original_date, type, new_date, new_start_time, new_end_time, modalidade, observacoes, titulo_custom, extra_fields, reason, agenda_evento_id, created_at) FROM stdin; +\. + + +-- +-- Data for Name: recurrence_rule_services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.recurrence_rule_services (id, rule_id, service_id, quantity, unit_price, discount_pct, discount_flat, final_price, created_at) FROM stdin; +\. + + +-- +-- Data for Name: recurrence_rules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.recurrence_rules (id, tenant_id, owner_id, therapist_id, patient_id, determined_commitment_id, type, "interval", weekdays, start_time, end_time, timezone, duration_min, start_date, end_date, max_occurrences, open_ended, modalidade, titulo_custom, observacoes, extra_fields, status, created_at, updated_at, price, insurance_plan_id, insurance_guide_number, insurance_value, insurance_plan_service_id) FROM stdin; +\. + + +-- +-- Data for Name: saas_admins; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_admins (user_id, created_at) FROM stdin; +aaaaaaaa-0006-0006-0006-000000000006 2026-03-23 10:46:29.876072+00 +\. + + +-- +-- Data for Name: saas_doc_votos; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_doc_votos (id, doc_id, user_id, util, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: saas_docs; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_docs (id, titulo, conteudo, medias, tipo_acesso, pagina_path, docs_relacionados, ativo, ordem, created_at, updated_at, categoria, exibir_no_faq, votos_util, votos_nao_util) FROM stdin; +\. + + +-- +-- Data for Name: saas_faq; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_faq (id, pergunta, categoria, publico, votos, titulo, conteudo, tipo_acesso, pagina_path, pagina_label, medias, faqs_relacionados, ativo, ordem, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: saas_faq_itens; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_faq_itens (id, doc_id, pergunta, resposta, ordem, ativo, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.services (id, owner_id, tenant_id, name, description, price, duration_min, active, created_at, updated_at) FROM stdin; +2f2d31a7-abef-4efc-89fc-794a458682cb aaaaaaaa-0005-0005-0005-000000000005 bbbbbbbb-0005-0005-0005-000000000005 Atendimento padrão \N 0.00 50 t 2026-03-26 16:50:09.010706+00 2026-03-26 16:50:09.010706+00 +0166ac88-669d-4f0c-8769-17eb0029c8a9 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 Atendimento padrão \N 40.00 50 t 2026-03-26 21:48:12.84259+00 2026-03-26 21:50:44.411125+00 +045843e1-815a-437b-b11a-ea9136e10cdf aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste \N 1.00 50 t 2026-03-27 11:40:12.054774+00 2026-03-27 11:40:12.054774+00 +36cc58d9-7beb-451b-b630-e81271d45de4 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste \N 2.00 50 t 2026-03-27 13:03:35.570064+00 2026-03-27 13:03:35.570064+00 +\. + + +-- +-- Data for Name: subscription_events; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_events (id, subscription_id, owner_id, event_type, old_plan_id, new_plan_id, created_at, created_by, source, reason, metadata, owner_type, owner_ref) FROM stdin; +b9180edb-76a7-4f0b-be24-92f056091f14 6864f71c-c4d4-4d7e-9897-89a644002b6d aaaaaaaa-0002-0002-0002-000000000002 plan_changed c56fe2a8-2c17-4048-adc7-ff7fbd89461a 82067ba7-16f0-4803-b36f-4c4e8919d4b4 2026-03-27 09:41:23.50964+00 aaaaaaaa-0002-0002-0002-000000000002 dev_menu Plan change via DEV menu {"new_plan": "82067ba7-16f0-4803-b36f-4c4e8919d4b4", "new_plan_key": "therapist_pro", "previous_plan": "c56fe2a8-2c17-4048-adc7-ff7fbd89461a", "new_plan_target": "therapist"} therapist aaaaaaaa-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: subscription_intents_legacy; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_intents_legacy (id, user_id, email, plan_key, "interval", amount_cents, currency, status, source, notes, created_at, paid_at, tenant_id, created_by_user_id) FROM stdin; +\. + + +-- +-- Data for Name: subscription_intents_personal; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_intents_personal (id, user_id, created_by_user_id, email, plan_id, plan_key, "interval", amount_cents, currency, status, source, notes, created_at, paid_at, subscription_id) FROM stdin; +\. + + +-- +-- Data for Name: subscription_intents_tenant; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_intents_tenant (id, user_id, created_by_user_id, email, plan_id, plan_key, "interval", amount_cents, currency, status, source, notes, created_at, paid_at, tenant_id, subscription_id) FROM stdin; +\. + + +-- +-- Data for Name: subscriptions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscriptions (id, user_id, plan_id, status, current_period_start, current_period_end, cancel_at_period_end, provider, provider_customer_id, provider_subscription_id, created_at, updated_at, tenant_id, plan_key, "interval", source, started_at, canceled_at, activated_at, past_due_since, suspended_at, suspended_reason, cancelled_at, cancel_reason, expired_at) FROM stdin; +61f81f06-c718-4d52-8063-67c38c1c1df9 aaaaaaaa-0001-0001-0001-000000000001 984c1f29-a975-4208-93ac-2118ed1039b7 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N patient_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +8716fb6d-372f-4560-98a5-68c40aec96dc aaaaaaaa-0007-0007-0007-000000000007 8c4895a3-e12d-48de-a078-efb8a4ea2eb2 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N supervisor_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +3fc090ba-3e9a-47ef-9711-edf9a3fd1795 aaaaaaaa-0008-0008-0008-000000000008 c56fe2a8-2c17-4048-adc7-ff7fbd89461a active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N therapist_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +cc4c4eb4-c9e5-4ce7-a705-9f5dc415fcb4 aaaaaaaa-0009-0009-0009-000000000009 c56fe2a8-2c17-4048-adc7-ff7fbd89461a active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N therapist_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +d2516bea-712d-453a-a5ed-9e40456f9aca aaaaaaaa-0010-0010-0010-000000000010 c56fe2a8-2c17-4048-adc7-ff7fbd89461a active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N therapist_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +bd6dd2c0-9c50-4939-8fc9-41fd67423a3e \N 01a5867f-0705-4714-ac97-a23470949157 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 bbbbbbbb-0003-0003-0003-000000000003 clinic_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +505d05fa-a6db-46b7-8cbf-bb6b12fb8e2b \N 01a5867f-0705-4714-ac97-a23470949157 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 bbbbbbbb-0004-0004-0004-000000000004 clinic_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +27eef221-7db2-442a-99f3-989f910cdcb4 \N 01a5867f-0705-4714-ac97-a23470949157 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 bbbbbbbb-0005-0005-0005-000000000005 clinic_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +6864f71c-c4d4-4d7e-9897-89a644002b6d aaaaaaaa-0002-0002-0002-000000000002 82067ba7-16f0-4803-b36f-4c4e8919d4b4 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-27 09:41:23.50964+00 \N therapist_pro month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +\. + + +-- +-- Data for Name: support_sessions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.support_sessions (id, tenant_id, admin_id, token, expires_at, created_at) FROM stdin; +\. + + +-- +-- Data for Name: tenant_feature_exceptions_log; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_feature_exceptions_log (id, tenant_id, feature_key, enabled, reason, created_by, created_at) FROM stdin; +\. + + +-- +-- Data for Name: tenant_features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_features (tenant_id, feature_key, enabled, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: tenant_invites; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_invites (id, tenant_id, email, role, token, invited_by, created_at, expires_at, accepted_at, accepted_by, revoked_at, revoked_by) FROM stdin; +\. + + +-- +-- Data for Name: tenant_members; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_members (id, tenant_id, user_id, role, status, created_at) FROM stdin; +72be63ef-bc2c-4c8e-8522-0c6d64746bbc bbbbbbbb-0002-0002-0002-000000000002 aaaaaaaa-0002-0002-0002-000000000002 tenant_admin active 2026-03-23 10:46:29.876072+00 +07974953-677e-4941-b90f-1ae15ffbbbf6 bbbbbbbb-0003-0003-0003-000000000003 aaaaaaaa-0003-0003-0003-000000000003 tenant_admin active 2026-03-23 10:46:29.876072+00 +11417585-46af-4faa-a34a-dc23a36c5704 bbbbbbbb-0004-0004-0004-000000000004 aaaaaaaa-0004-0004-0004-000000000004 tenant_admin active 2026-03-23 10:46:29.876072+00 +cd22a662-2b16-4cfe-a9e6-15ccb8e7d67e bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0005-0005-0005-000000000005 tenant_admin active 2026-03-23 10:46:29.876072+00 +e40559cd-43af-4547-b447-2cc7bbff5ad7 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0002-0002-0002-000000000002 therapist active 2026-03-23 10:46:29.876072+00 +0ee00481-77f2-4bc0-ab09-3bc441c03b1b bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0007-0007-0007-000000000007 supervisor active 2026-03-23 14:18:05.215881+00 +344a6ae3-379a-4776-9441-8ac9f8733c99 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0008-0008-0008-000000000008 therapist active 2026-03-23 14:18:05.215881+00 +aac8e805-da4f-4089-b2e1-0b32b679d0dd bbbbbbbb-0009-0009-0009-000000000009 aaaaaaaa-0009-0009-0009-000000000009 tenant_admin active 2026-03-23 14:18:06.087973+00 +2633ed47-76e8-4a30-8e3c-07bec4f7c9cd bbbbbbbb-0010-0010-0010-000000000010 aaaaaaaa-0010-0010-0010-000000000010 tenant_admin active 2026-03-23 14:18:06.087973+00 +a72fa56d-04e4-4e23-853c-fd4926b263c3 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0009-0009-0009-000000000009 therapist active 2026-03-23 14:18:06.087973+00 +677f130c-06bd-44d7-b283-844351d65c45 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0010-0010-0010-000000000010 therapist active 2026-03-23 14:18:06.087973+00 +978ce15a-8dc5-4540-8430-f9e1e19d4952 bbbbbbbb-0004-0004-0004-000000000004 aaaaaaaa-0011-0011-0011-000000000011 clinic_admin active 2026-03-23 14:18:06.087973+00 +da8fd413-1710-40f2-85a0-5af9cf2c78d8 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 aaaaaaaa-0008-0008-0008-000000000008 tenant_admin active 2026-03-26 19:46:29.161046+00 +fb7a3517-0479-42ac-8c37-ec6ffc03c5be 1e98ca49-a46c-4701-847b-145a14d53d19 384a69d8-b7cd-40ac-9d3c-764c93532b66 tenant_admin active 2026-03-26 19:47:30.68134+00 +\. + + +-- +-- Data for Name: tenant_modules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_modules (id, owner_id, module_id, status, settings, provider, provider_item_id, installed_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: tenants; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenants (id, name, created_at, kind) FROM stdin; +bbbbbbbb-0009-0009-0009-000000000009 Eva Terapeuta 2026-03-23 10:47:12.826179+00 therapist +bbbbbbbb-0010-0010-0010-000000000010 Felipe Terapeuta 2026-03-23 10:47:12.826179+00 therapist +bbbbbbbb-0003-0003-0003-000000000003 Clínica Espaço Psi 2026-03-23 10:46:29.876072+00 clinic_coworking +bbbbbbbb-0004-0004-0004-000000000004 Clínica Mente Sã 2026-03-23 10:46:29.876072+00 clinic_reception +bbbbbbbb-0005-0005-0005-000000000005 Clínica Bem Estar 2026-03-23 10:46:29.876072+00 clinic_full +a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 Diana Editora 2026-03-26 19:46:29.161046+00 therapist +1e98ca49-a46c-4701-847b-145a14d53d19 terapeuta2 2026-03-26 19:47:30.68134+00 therapist +bbbbbbbb-0002-0002-0002-000000000002 Bruno Terapeuta 2026-03-23 10:46:29.876072+00 therapist +\. + + +-- +-- Data for Name: therapist_payout_records; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.therapist_payout_records (payout_id, financial_record_id) FROM stdin; +\. + + +-- +-- Data for Name: therapist_payouts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.therapist_payouts (id, owner_id, tenant_id, period_start, period_end, total_sessions, gross_amount, clinic_fee_total, net_amount, status, paid_at, notes, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: twilio_subaccount_usage; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.twilio_subaccount_usage (id, tenant_id, channel_id, twilio_subaccount_sid, period_start, period_end, messages_sent, messages_delivered, messages_failed, cost_usd, cost_brl, revenue_brl, usd_brl_rate, synced_at, created_at) FROM stdin; +\. + + +-- +-- Data for Name: user_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.user_settings (user_id, theme_mode, preset, primary_color, surface_color, menu_mode, created_at, updated_at, layout_variant) FROM stdin; +aaaaaaaa-0002-0002-0002-000000000002 dark Lara rose soho static 2026-03-25 18:47:05.458455+00 2026-03-27 14:18:26.578988+00 classic +aaaaaaaa-0005-0005-0005-000000000005 dark Aura orange slate static 2026-03-26 10:14:04.329765+00 2026-03-26 17:19:07.587294+00 classic +\. + + +-- +-- Data for Name: messages_2026_03_24; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.messages_2026_03_24 (topic, extension, payload, event, private, updated_at, inserted_at, id) FROM stdin; +\. + + +-- +-- Data for Name: messages_2026_03_25; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.messages_2026_03_25 (topic, extension, payload, event, private, updated_at, inserted_at, id) FROM stdin; +\. + + +-- +-- Data for Name: messages_2026_03_26; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.messages_2026_03_26 (topic, extension, payload, event, private, updated_at, inserted_at, id) FROM stdin; +\. + + +-- +-- Data for Name: messages_2026_03_27; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.messages_2026_03_27 (topic, extension, payload, event, private, updated_at, inserted_at, id) FROM stdin; +\. + + +-- +-- Data for Name: messages_2026_03_28; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.messages_2026_03_28 (topic, extension, payload, event, private, updated_at, inserted_at, id) FROM stdin; +\. + + +-- +-- Data for Name: messages_2026_03_29; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.messages_2026_03_29 (topic, extension, payload, event, private, updated_at, inserted_at, id) FROM stdin; +\. + + +-- +-- Data for Name: messages_2026_03_30; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.messages_2026_03_30 (topic, extension, payload, event, private, updated_at, inserted_at, id) FROM stdin; +\. + + +-- +-- Data for Name: schema_migrations; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.schema_migrations (version, inserted_at) FROM stdin; +20211116024918 2026-03-23 10:13:17 +20211116045059 2026-03-23 10:13:17 +20211116050929 2026-03-23 10:13:17 +20211116051442 2026-03-23 10:13:17 +20211116212300 2026-03-23 10:13:17 +20211116213355 2026-03-23 10:13:17 +20211116213934 2026-03-23 10:13:17 +20211116214523 2026-03-23 10:13:17 +20211122062447 2026-03-23 10:13:17 +20211124070109 2026-03-23 10:13:17 +20211202204204 2026-03-23 10:13:17 +20211202204605 2026-03-23 10:13:17 +20211210212804 2026-03-23 10:13:17 +20211228014915 2026-03-23 10:13:17 +20220107221237 2026-03-23 10:13:17 +20220228202821 2026-03-23 10:13:17 +20220312004840 2026-03-23 10:13:17 +20220603231003 2026-03-23 10:13:17 +20220603232444 2026-03-23 10:13:17 +20220615214548 2026-03-23 10:13:17 +20220712093339 2026-03-23 10:13:17 +20220908172859 2026-03-23 10:13:17 +20220916233421 2026-03-23 10:13:17 +20230119133233 2026-03-23 10:13:17 +20230128025114 2026-03-23 10:13:17 +20230128025212 2026-03-23 10:13:17 +20230227211149 2026-03-23 10:13:17 +20230228184745 2026-03-23 10:13:17 +20230308225145 2026-03-23 10:13:17 +20230328144023 2026-03-23 10:13:17 +20231018144023 2026-03-23 10:13:17 +20231204144023 2026-03-23 10:13:17 +20231204144024 2026-03-23 10:13:17 +20231204144025 2026-03-23 10:13:17 +20240108234812 2026-03-23 10:13:17 +20240109165339 2026-03-23 10:13:17 +20240227174441 2026-03-23 10:13:17 +20240311171622 2026-03-23 10:13:17 +20240321100241 2026-03-23 10:13:17 +20240401105812 2026-03-23 10:13:17 +20240418121054 2026-03-23 10:13:17 +20240523004032 2026-03-23 10:13:17 +20240618124746 2026-03-23 10:13:18 +20240801235015 2026-03-23 10:13:18 +20240805133720 2026-03-23 10:13:18 +20240827160934 2026-03-23 10:13:18 +20240919163303 2026-03-23 10:13:18 +20240919163305 2026-03-23 10:13:18 +20241019105805 2026-03-23 10:13:18 +20241030150047 2026-03-23 10:13:18 +20241108114728 2026-03-23 10:13:18 +20241121104152 2026-03-23 10:13:18 +20241130184212 2026-03-23 10:13:18 +20241220035512 2026-03-23 10:13:18 +20241220123912 2026-03-23 10:13:18 +20241224161212 2026-03-23 10:13:18 +20250107150512 2026-03-23 10:13:18 +20250110162412 2026-03-23 10:13:18 +20250123174212 2026-03-23 10:13:18 +20250128220012 2026-03-23 10:13:18 +20250506224012 2026-03-23 10:13:18 +20250523164012 2026-03-23 10:13:18 +20250714121412 2026-03-23 10:13:18 +20250905041441 2026-03-23 10:13:18 +20251103001201 2026-03-23 10:13:18 +\. + + +-- +-- Data for Name: subscription; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.subscription (id, subscription_id, entity, filters, claims, created_at) FROM stdin; +907 5681a0cc-29f8-11f1-b093-3e375a9478e1 public.notifications {"(owner_id,eq,aaaaaaaa-0002-0002-0002-000000000002)"} {"aal": "aal1", "amr": [{"method": "password", "timestamp": 1774627229}], "aud": "authenticated", "exp": 1774630829, "iat": 1774627229, "iss": "http://127.0.0.1:54321/auth/v1", "sub": "aaaaaaaa-0002-0002-0002-000000000002", "role": "authenticated", "email": "terapeuta@agenciapsi.com.br", "phone": "", "session_id": "2dc73205-6913-432b-8eb7-e8b609e5f0f4", "app_metadata": {"provider": "email", "providers": ["email"]}, "is_anonymous": false, "user_metadata": {"name": "Bruno Terapeuta", "full_name": "Leonardo Nohama", "avatar_url": "http://127.0.0.1:54321/storage/v1/object/public/avatars/aaaaaaaa-0002-0002-0002-000000000002/avatar.jpg"}} 2026-03-27 16:16:40.516976 +\. + + +-- +-- Data for Name: buckets; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.buckets (id, name, owner, created_at, updated_at, public, avif_autodetection, file_size_limit, allowed_mime_types, owner_id, type) FROM stdin; +avatars avatars \N 2026-03-26 21:49:42.507669+00 2026-03-26 21:49:42.507669+00 t f \N \N \N STANDARD +logos logos \N 2026-03-26 21:49:42.507669+00 2026-03-26 21:49:42.507669+00 t f \N \N \N STANDARD +\. + + +-- +-- Data for Name: buckets_analytics; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.buckets_analytics (name, type, format, created_at, updated_at, id, deleted_at) FROM stdin; +\. + + +-- +-- Data for Name: buckets_vectors; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.buckets_vectors (id, type, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: iceberg_namespaces; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.iceberg_namespaces (id, bucket_name, name, created_at, updated_at, metadata, catalog_id) FROM stdin; +\. + + +-- +-- Data for Name: iceberg_tables; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.iceberg_tables (id, namespace_id, bucket_name, name, location, created_at, updated_at, remote_table_id, shard_key, shard_id, catalog_id) FROM stdin; +\. + + +-- +-- Data for Name: migrations; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.migrations (id, name, hash, executed_at) FROM stdin; +0 create-migrations-table e18db593bcde2aca2a408c4d1100f6abba2195df 2026-03-23 10:13:30.796432 +1 initialmigration 6ab16121fbaa08bbd11b712d05f358f9b555d777 2026-03-23 10:13:30.806948 +2 storage-schema f6a1fa2c93cbcd16d4e487b362e45fca157a8dbd 2026-03-23 10:13:30.810381 +3 pathtoken-column 2cb1b0004b817b29d5b0a971af16bafeede4b70d 2026-03-23 10:13:30.829325 +4 add-migrations-rls 427c5b63fe1c5937495d9c635c263ee7a5905058 2026-03-23 10:13:30.844667 +5 add-size-functions 79e081a1455b63666c1294a440f8ad4b1e6a7f84 2026-03-23 10:13:30.849797 +6 change-column-name-in-get-size ded78e2f1b5d7e616117897e6443a925965b30d2 2026-03-23 10:13:30.853961 +7 add-rls-to-buckets e7e7f86adbc51049f341dfe8d30256c1abca17aa 2026-03-23 10:13:30.857966 +8 add-public-to-buckets fd670db39ed65f9d08b01db09d6202503ca2bab3 2026-03-23 10:13:30.86089 +9 fix-search-function af597a1b590c70519b464a4ab3be54490712796b 2026-03-23 10:13:30.865914 +10 search-files-search-function b595f05e92f7e91211af1bbfe9c6a13bb3391e16 2026-03-23 10:13:30.870596 +11 add-trigger-to-auto-update-updated_at-column 7425bdb14366d1739fa8a18c83100636d74dcaa2 2026-03-23 10:13:30.877495 +12 add-automatic-avif-detection-flag 8e92e1266eb29518b6a4c5313ab8f29dd0d08df9 2026-03-23 10:13:30.887175 +13 add-bucket-custom-limits cce962054138135cd9a8c4bcd531598684b25e7d 2026-03-23 10:13:30.894644 +14 use-bytes-for-max-size 941c41b346f9802b411f06f30e972ad4744dad27 2026-03-23 10:13:30.903415 +15 add-can-insert-object-function 934146bc38ead475f4ef4b555c524ee5d66799e5 2026-03-23 10:13:30.921243 +16 add-version 76debf38d3fd07dcfc747ca49096457d95b1221b 2026-03-23 10:13:30.925814 +17 drop-owner-foreign-key f1cbb288f1b7a4c1eb8c38504b80ae2a0153d101 2026-03-23 10:13:30.930594 +18 add_owner_id_column_deprecate_owner e7a511b379110b08e2f214be852c35414749fe66 2026-03-23 10:13:30.935872 +19 alter-default-value-objects-id 02e5e22a78626187e00d173dc45f58fa66a4f043 2026-03-23 10:13:30.945545 +20 list-objects-with-delimiter cd694ae708e51ba82bf012bba00caf4f3b6393b7 2026-03-23 10:13:30.949803 +21 s3-multipart-uploads 8c804d4a566c40cd1e4cc5b3725a664a9303657f 2026-03-23 10:13:30.956077 +22 s3-multipart-uploads-big-ints 9737dc258d2397953c9953d9b86920b8be0cdb73 2026-03-23 10:13:30.986283 +23 optimize-search-function 9d7e604cddc4b56a5422dc68c9313f4a1b6f132c 2026-03-23 10:13:30.995314 +24 operation-function 8312e37c2bf9e76bbe841aa5fda889206d2bf8aa 2026-03-23 10:13:31.001863 +25 custom-metadata d974c6057c3db1c1f847afa0e291e6165693b990 2026-03-23 10:13:31.005106 +26 objects-prefixes 215cabcb7f78121892a5a2037a09fedf9a1ae322 2026-03-23 10:13:31.010242 +27 search-v2 859ba38092ac96eb3964d83bf53ccc0b141663a6 2026-03-23 10:13:31.021533 +28 object-bucket-name-sorting c73a2b5b5d4041e39705814fd3a1b95502d38ce4 2026-03-23 10:13:31.025092 +29 create-prefixes ad2c1207f76703d11a9f9007f821620017a66c21 2026-03-23 10:13:31.028503 +30 update-object-levels 2be814ff05c8252fdfdc7cfb4b7f5c7e17f0bed6 2026-03-23 10:13:31.031493 +31 objects-level-index b40367c14c3440ec75f19bbce2d71e914ddd3da0 2026-03-23 10:13:31.033332 +32 backward-compatible-index-on-objects e0c37182b0f7aee3efd823298fb3c76f1042c0f7 2026-03-23 10:13:31.035585 +33 backward-compatible-index-on-prefixes b480e99ed951e0900f033ec4eb34b5bdcb4e3d49 2026-03-23 10:13:31.043153 +34 optimize-search-function-v1 ca80a3dc7bfef894df17108785ce29a7fc8ee456 2026-03-23 10:13:31.045135 +35 add-insert-trigger-prefixes 458fe0ffd07ec53f5e3ce9df51bfdf4861929ccc 2026-03-23 10:13:31.047011 +36 optimise-existing-functions 6ae5fca6af5c55abe95369cd4f93985d1814ca8f 2026-03-23 10:13:31.049187 +37 add-bucket-name-length-trigger 3944135b4e3e8b22d6d4cbb568fe3b0b51df15c1 2026-03-23 10:13:31.052127 +38 iceberg-catalog-flag-on-buckets 02716b81ceec9705aed84aa1501657095b32e5c5 2026-03-23 10:13:31.056547 +39 add-search-v2-sort-support 6706c5f2928846abee18461279799ad12b279b78 2026-03-23 10:13:31.071273 +40 fix-prefix-race-conditions-optimized 7ad69982ae2d372b21f48fc4829ae9752c518f6b 2026-03-23 10:13:31.073508 +41 add-object-level-update-trigger 07fcf1a22165849b7a029deed059ffcde08d1ae0 2026-03-23 10:13:31.075277 +42 rollback-prefix-triggers 771479077764adc09e2ea2043eb627503c034cd4 2026-03-23 10:13:31.07699 +43 fix-object-level 84b35d6caca9d937478ad8a797491f38b8c2979f 2026-03-23 10:13:31.079645 +44 vector-bucket-type 99c20c0ffd52bb1ff1f32fb992f3b351e3ef8fb3 2026-03-23 10:13:31.081463 +45 vector-buckets 049e27196d77a7cb76497a85afae669d8b230953 2026-03-23 10:13:31.084341 +46 buckets-objects-grants fedeb96d60fefd8e02ab3ded9fbde05632f84aed 2026-03-23 10:13:31.090554 +47 iceberg-table-metadata 649df56855c24d8b36dd4cc1aeb8251aa9ad42c2 2026-03-23 10:13:31.09725 +48 iceberg-catalog-ids e0e8b460c609b9999ccd0df9ad14294613eed939 2026-03-23 10:13:31.106653 +49 buckets-objects-grants-postgres 072b1195d0d5a2f888af6b2302a1938dd94b8b3d 2026-03-23 10:13:31.134859 +50 search-v2-optimised 6323ac4f850aa14e7387eb32102869578b5bd478 2026-03-23 10:13:31.141562 +51 index-backward-compatible-search 2ee395d433f76e38bcd3856debaf6e0e5b674011 2026-03-23 10:13:31.236273 +52 drop-not-used-indexes-and-functions bb0cbc7f2206a5a41113363dd22556cc1afd6327 2026-03-23 10:13:31.237617 +53 drop-index-lower-name d0cb18777d9e2a98ebe0bc5cc7a42e57ebe41854 2026-03-23 10:13:31.243999 +54 drop-index-object-level 6289e048b1472da17c31a7eba1ded625a6457e67 2026-03-23 10:13:31.24577 +55 prevent-direct-deletes 262a4798d5e0f2e7c8970232e03ce8be695d5819 2026-03-23 10:13:31.247132 +\. + + +-- +-- Data for Name: objects; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.objects (id, bucket_id, name, owner, created_at, updated_at, last_accessed_at, metadata, version, owner_id, user_metadata) FROM stdin; +075af3e6-3564-45a5-b23f-436703c6c26f avatars aaaaaaaa-0002-0002-0002-000000000002/avatar.png aaaaaaaa-0002-0002-0002-000000000002 2026-03-26 21:50:11.504419+00 2026-03-26 21:50:13.194121+00 2026-03-26 21:50:11.504419+00 {"eTag": "\\"7acd7fe015d57168c9d1f740e6ef9dca\\"", "size": 120039, "mimetype": "image/png", "cacheControl": "max-age=3600", "lastModified": "2026-03-26T21:50:13.181Z", "contentLength": 120039, "httpStatusCode": 200} c6a84a00-860d-44bb-9811-020973cebfb5 aaaaaaaa-0002-0002-0002-000000000002 {} +89cde848-64e4-4977-b80d-451a5d6821f3 logos bbbbbbbb-0002-0002-0002-000000000002/logo.webp aaaaaaaa-0002-0002-0002-000000000002 2026-03-26 21:50:03.179856+00 2026-03-26 22:02:03.167238+00 2026-03-26 21:50:03.179856+00 {"eTag": "\\"af758fda0f2989c0fe7e39c321769aae\\"", "size": 62662, "mimetype": "image/webp", "cacheControl": "max-age=3600", "lastModified": "2026-03-26T22:02:03.153Z", "contentLength": 62662, "httpStatusCode": 200} 3f73d9fd-b955-48f6-9000-9c9b496368a0 aaaaaaaa-0002-0002-0002-000000000002 {} +15bc29b2-3f9d-4e12-a8ca-57d86a86af98 avatars aaaaaaaa-0002-0002-0002-000000000002/avatar.jpg aaaaaaaa-0002-0002-0002-000000000002 2026-03-26 22:33:38.030054+00 2026-03-26 22:38:39.744595+00 2026-03-26 22:33:38.030054+00 {"eTag": "\\"ef32cf7cfbb4f5f02cf6232c16eab2d5\\"", "size": 81994, "mimetype": "image/jpeg", "cacheControl": "max-age=3600", "lastModified": "2026-03-26T22:38:39.735Z", "contentLength": 81994, "httpStatusCode": 200} 3863856f-cab6-426e-b583-c7a5eefd17b9 aaaaaaaa-0002-0002-0002-000000000002 {} +d6b93d8f-349d-4528-aa08-1ec8002a081c logos bbbbbbbb-0002-0002-0002-000000000002/logo.jpg aaaaaaaa-0002-0002-0002-000000000002 2026-03-26 22:12:00.379274+00 2026-03-26 22:12:16.15555+00 2026-03-26 22:12:00.379274+00 {"eTag": "\\"2b730f9f0e7047d098f5143fe2f4cccf\\"", "size": 198972, "mimetype": "image/jpeg", "cacheControl": "max-age=3600", "lastModified": "2026-03-26T22:12:16.145Z", "contentLength": 198972, "httpStatusCode": 200} 1dfcf0cc-9416-4eac-9d76-daed5851d689 aaaaaaaa-0002-0002-0002-000000000002 {} +b4b03b3c-d69f-4243-aec1-f37518ad181f avatars aaaaaaaa-0002-0002-0002-000000000002/avatar.webp aaaaaaaa-0002-0002-0002-000000000002 2026-03-27 14:17:24.227826+00 2026-03-27 14:18:26.525375+00 2026-03-27 14:17:24.227826+00 {"eTag": "\\"7d090e1ccd302488cb8ac03ea09e5354\\"", "size": 94090, "mimetype": "image/webp", "cacheControl": "max-age=3600", "lastModified": "2026-03-27T14:18:26.500Z", "contentLength": 94090, "httpStatusCode": 200} 1bc2430d-1ab7-43eb-af6f-5f96fea36361 aaaaaaaa-0002-0002-0002-000000000002 {} +7e19b5ee-127c-43a2-b14d-b1760780d18d logos bbbbbbbb-0002-0002-0002-000000000002/logo.png aaaaaaaa-0002-0002-0002-000000000002 2026-03-27 11:02:41.945678+00 2026-03-27 14:18:20.310831+00 2026-03-27 11:02:41.945678+00 {"eTag": "\\"60d363c60025fd47681d9b9d50939a8d\\"", "size": 158427, "mimetype": "image/png", "cacheControl": "max-age=3600", "lastModified": "2026-03-27T14:18:20.304Z", "contentLength": 158427, "httpStatusCode": 200} 9c580c92-3cc2-4237-b42c-4bd5e5a9a7f0 aaaaaaaa-0002-0002-0002-000000000002 {} +\. + + +-- +-- Data for Name: s3_multipart_uploads; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.s3_multipart_uploads (id, in_progress_size, upload_signature, bucket_id, key, version, owner_id, created_at, user_metadata) FROM stdin; +\. + + +-- +-- Data for Name: s3_multipart_uploads_parts; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.s3_multipart_uploads_parts (id, upload_id, size, part_number, bucket_id, key, etag, owner_id, version, created_at) FROM stdin; +\. + + +-- +-- Data for Name: vector_indexes; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.vector_indexes (id, name, bucket_id, data_type, dimension, distance_metric, metadata_configuration, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: hooks; Type: TABLE DATA; Schema: supabase_functions; Owner: - +-- + +COPY supabase_functions.hooks (id, hook_table_id, hook_name, created_at, request_id) FROM stdin; +\. + + +-- +-- Data for Name: migrations; Type: TABLE DATA; Schema: supabase_functions; Owner: - +-- + +COPY supabase_functions.migrations (version, inserted_at) FROM stdin; +initial 2026-03-23 10:13:12.298935+00 +20210809183423_update_grants 2026-03-23 10:13:12.298935+00 +\. + + +-- +-- Data for Name: secrets; Type: TABLE DATA; Schema: vault; Owner: - +-- + +COPY vault.secrets (id, name, description, secret, key_id, nonce, created_at, updated_at) FROM stdin; +\. + + +-- +-- Name: refresh_tokens_id_seq; Type: SEQUENCE SET; Schema: auth; Owner: - +-- + +SELECT pg_catalog.setval('auth.refresh_tokens_id_seq', 137, true); + + +-- +-- Name: jobid_seq; Type: SEQUENCE SET; Schema: cron; Owner: - +-- + +SELECT pg_catalog.setval('cron.jobid_seq', 1, false); + + +-- +-- Name: runid_seq; Type: SEQUENCE SET; Schema: cron; Owner: - +-- + +SELECT pg_catalog.setval('cron.runid_seq', 1, false); + + +-- +-- Name: _db_migrations_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public._db_migrations_id_seq', 15, true); + + +-- +-- Name: agenda_online_slots_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.agenda_online_slots_id_seq', 1, false); + + +-- +-- Name: subscription_id_seq; Type: SEQUENCE SET; Schema: realtime; Owner: - +-- + +SELECT pg_catalog.setval('realtime.subscription_id_seq', 907, true); + + +-- +-- Name: hooks_id_seq; Type: SEQUENCE SET; Schema: supabase_functions; Owner: - +-- + +SELECT pg_catalog.setval('supabase_functions.hooks_id_seq', 1, false); + + +-- +-- Name: extensions extensions_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.extensions + ADD CONSTRAINT extensions_pkey PRIMARY KEY (id); + + +-- +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.schema_migrations + ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.tenants + ADD CONSTRAINT tenants_pkey PRIMARY KEY (id); + + +-- +-- Name: mfa_amr_claims amr_id_pk; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_amr_claims + ADD CONSTRAINT amr_id_pk PRIMARY KEY (id); + + +-- +-- Name: audit_log_entries audit_log_entries_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.audit_log_entries + ADD CONSTRAINT audit_log_entries_pkey PRIMARY KEY (id); + + +-- +-- Name: flow_state flow_state_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.flow_state + ADD CONSTRAINT flow_state_pkey PRIMARY KEY (id); + + +-- +-- Name: identities identities_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.identities + ADD CONSTRAINT identities_pkey PRIMARY KEY (id); + + +-- +-- Name: identities identities_provider_id_provider_unique; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.identities + ADD CONSTRAINT identities_provider_id_provider_unique UNIQUE (provider_id, provider); + + +-- +-- Name: instances instances_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.instances + ADD CONSTRAINT instances_pkey PRIMARY KEY (id); + + +-- +-- Name: mfa_amr_claims mfa_amr_claims_session_id_authentication_method_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_amr_claims + ADD CONSTRAINT mfa_amr_claims_session_id_authentication_method_pkey UNIQUE (session_id, authentication_method); + + +-- +-- Name: mfa_challenges mfa_challenges_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_challenges + ADD CONSTRAINT mfa_challenges_pkey PRIMARY KEY (id); + + +-- +-- Name: mfa_factors mfa_factors_last_challenged_at_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_factors + ADD CONSTRAINT mfa_factors_last_challenged_at_key UNIQUE (last_challenged_at); + + +-- +-- Name: mfa_factors mfa_factors_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_factors + ADD CONSTRAINT mfa_factors_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_authorizations oauth_authorizations_authorization_code_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_authorization_code_key UNIQUE (authorization_code); + + +-- +-- Name: oauth_authorizations oauth_authorizations_authorization_id_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_authorization_id_key UNIQUE (authorization_id); + + +-- +-- Name: oauth_authorizations oauth_authorizations_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_client_states oauth_client_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_client_states + ADD CONSTRAINT oauth_client_states_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_clients oauth_clients_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_clients + ADD CONSTRAINT oauth_clients_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_consents oauth_consents_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_consents oauth_consents_user_client_unique; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_user_client_unique UNIQUE (user_id, client_id); + + +-- +-- Name: one_time_tokens one_time_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.one_time_tokens + ADD CONSTRAINT one_time_tokens_pkey PRIMARY KEY (id); + + +-- +-- Name: refresh_tokens refresh_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens + ADD CONSTRAINT refresh_tokens_pkey PRIMARY KEY (id); + + +-- +-- Name: refresh_tokens refresh_tokens_token_unique; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens + ADD CONSTRAINT refresh_tokens_token_unique UNIQUE (token); + + +-- +-- Name: saml_providers saml_providers_entity_id_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_providers + ADD CONSTRAINT saml_providers_entity_id_key UNIQUE (entity_id); + + +-- +-- Name: saml_providers saml_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_providers + ADD CONSTRAINT saml_providers_pkey PRIMARY KEY (id); + + +-- +-- Name: saml_relay_states saml_relay_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_relay_states + ADD CONSTRAINT saml_relay_states_pkey PRIMARY KEY (id); + + +-- +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.schema_migrations + ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: sessions sessions_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sessions + ADD CONSTRAINT sessions_pkey PRIMARY KEY (id); + + +-- +-- Name: sso_domains sso_domains_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sso_domains + ADD CONSTRAINT sso_domains_pkey PRIMARY KEY (id); + + +-- +-- Name: sso_providers sso_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sso_providers + ADD CONSTRAINT sso_providers_pkey PRIMARY KEY (id); + + +-- +-- Name: users users_phone_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.users + ADD CONSTRAINT users_phone_key UNIQUE (phone); + + +-- +-- Name: users users_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.users + ADD CONSTRAINT users_pkey PRIMARY KEY (id); + + +-- +-- Name: _db_migrations _db_migrations_filename_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._db_migrations + ADD CONSTRAINT _db_migrations_filename_key UNIQUE (filename); + + +-- +-- Name: _db_migrations _db_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._db_migrations + ADD CONSTRAINT _db_migrations_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_credits addon_credits_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT addon_credits_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_products addon_products_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_products + ADD CONSTRAINT addon_products_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_products addon_products_slug_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_products + ADD CONSTRAINT addon_products_slug_key UNIQUE (slug); + + +-- +-- Name: addon_transactions addon_transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_bloqueios + ADD CONSTRAINT agenda_bloqueios_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_configuracoes + ADD CONSTRAINT agenda_configuracoes_pkey PRIMARY KEY (owner_id); + + +-- +-- Name: agenda_eventos agenda_eventos_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_eventos agenda_eventos_sem_sobreposicao; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_sem_sobreposicao EXCLUDE USING gist (owner_id WITH =, tstzrange(inicio_em, fim_em, '[)'::text) WITH &&); + + +-- +-- Name: agenda_excecoes agenda_excecoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_excecoes + ADD CONSTRAINT agenda_excecoes_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_online_slots agenda_online_slots_owner_id_weekday_time_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_owner_id_weekday_time_key UNIQUE (owner_id, weekday, "time"); + + +-- +-- Name: agenda_online_slots agenda_online_slots_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_regras_semanais + ADD CONSTRAINT agenda_regras_semanais_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_regras_semanais + ADD CONSTRAINT agenda_regras_semanais_unique UNIQUE (owner_id, dia_semana, hora_inicio, hora_fim, modalidade); + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_seman_owner_id_dia_semana_hora_inic_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais + ADD CONSTRAINT agenda_slots_bloqueados_seman_owner_id_dia_semana_hora_inic_key UNIQUE (owner_id, dia_semana, hora_inicio); + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais + ADD CONSTRAINT agenda_slots_bloqueados_semanais_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_owner_id_dia_semana_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_regras + ADD CONSTRAINT agenda_slots_regras_owner_id_dia_semana_key UNIQUE (owner_id, dia_semana); + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_regras + ADD CONSTRAINT agenda_slots_regras_pkey PRIMARY KEY (id); + + +-- +-- Name: agendador_configuracoes agendador_configuracoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_configuracoes + ADD CONSTRAINT agendador_configuracoes_pkey PRIMARY KEY (owner_id); + + +-- +-- Name: agendador_solicitacoes agendador_solicitacoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_solicitacoes + ADD CONSTRAINT agendador_solicitacoes_pkey PRIMARY KEY (id); + + +-- +-- Name: billing_contracts billing_contracts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.billing_contracts + ADD CONSTRAINT billing_contracts_pkey PRIMARY KEY (id); + + +-- +-- Name: commitment_services commitment_services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_services + ADD CONSTRAINT commitment_services_pkey PRIMARY KEY (id); + + +-- +-- Name: commitment_time_logs commitment_time_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_pkey PRIMARY KEY (id); + + +-- +-- Name: company_profiles company_profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.company_profiles + ADD CONSTRAINT company_profiles_pkey PRIMARY KEY (id); + + +-- +-- Name: company_profiles company_profiles_tenant_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.company_profiles + ADD CONSTRAINT company_profiles_tenant_id_key UNIQUE (tenant_id); + + +-- +-- Name: determined_commitment_fields determined_commitment_fields_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitment_fields + ADD CONSTRAINT determined_commitment_fields_pkey PRIMARY KEY (id); + + +-- +-- Name: determined_commitments determined_commitments_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitments + ADD CONSTRAINT determined_commitments_pkey PRIMARY KEY (id); + + +-- +-- Name: determined_commitments determined_commitments_tenant_native_key_uq; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitments + ADD CONSTRAINT determined_commitments_tenant_native_key_uq UNIQUE (tenant_id, native_key); + + +-- +-- Name: dev_user_credentials dev_user_credentials_email_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.dev_user_credentials + ADD CONSTRAINT dev_user_credentials_email_key UNIQUE (email); + + +-- +-- Name: dev_user_credentials dev_user_credentials_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.dev_user_credentials + ADD CONSTRAINT dev_user_credentials_pkey PRIMARY KEY (id); + + +-- +-- Name: email_layout_config email_layout_config_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_layout_config + ADD CONSTRAINT email_layout_config_pkey PRIMARY KEY (id); + + +-- +-- Name: email_layout_config email_layout_config_tenant_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_layout_config + ADD CONSTRAINT email_layout_config_tenant_id_key UNIQUE (tenant_id); + + +-- +-- Name: email_templates_global email_templates_global_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_global + ADD CONSTRAINT email_templates_global_key_key UNIQUE (key); + + +-- +-- Name: email_templates_global email_templates_global_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_global + ADD CONSTRAINT email_templates_global_pkey PRIMARY KEY (id); + + +-- +-- Name: email_templates_tenant email_templates_tenant_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_pkey PRIMARY KEY (id); + + +-- +-- Name: email_templates_tenant email_templates_tenant_tenant_id_owner_id_template_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_tenant_id_owner_id_template_key_key UNIQUE (tenant_id, owner_id, template_key); + + +-- +-- Name: entitlements_invalidation entitlements_invalidation_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entitlements_invalidation + ADD CONSTRAINT entitlements_invalidation_pkey PRIMARY KEY (owner_id); + + +-- +-- Name: features features_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.features + ADD CONSTRAINT features_key_key UNIQUE (key); + + +-- +-- Name: features features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.features + ADD CONSTRAINT features_pkey PRIMARY KEY (id); + + +-- +-- Name: feriados feriados_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_pkey PRIMARY KEY (id); + + +-- +-- Name: feriados feriados_tenant_id_data_nome_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_tenant_id_data_nome_key UNIQUE (tenant_id, data, nome); + + +-- +-- Name: financial_categories financial_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_categories + ADD CONSTRAINT financial_categories_pkey PRIMARY KEY (id); + + +-- +-- Name: financial_exceptions financial_exceptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_exceptions + ADD CONSTRAINT financial_exceptions_pkey PRIMARY KEY (id); + + +-- +-- Name: financial_records financial_records_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_pkey PRIMARY KEY (id); + + +-- +-- Name: global_notices global_notices_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.global_notices + ADD CONSTRAINT global_notices_pkey PRIMARY KEY (id); + + +-- +-- Name: insurance_plan_services insurance_plan_services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plan_services + ADD CONSTRAINT insurance_plan_services_pkey PRIMARY KEY (id); + + +-- +-- Name: insurance_plans insurance_plans_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plans + ADD CONSTRAINT insurance_plans_pkey PRIMARY KEY (id); + + +-- +-- Name: login_carousel_slides login_carousel_slides_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.login_carousel_slides + ADD CONSTRAINT login_carousel_slides_pkey PRIMARY KEY (id); + + +-- +-- Name: module_features module_features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.module_features + ADD CONSTRAINT module_features_pkey PRIMARY KEY (module_id, feature_id); + + +-- +-- Name: modules modules_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.modules + ADD CONSTRAINT modules_key_key UNIQUE (key); + + +-- +-- Name: modules modules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.modules + ADD CONSTRAINT modules_pkey PRIMARY KEY (id); + + +-- +-- Name: notice_dismissals notice_dismissals_notice_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_notice_id_user_id_key UNIQUE (notice_id, user_id); + + +-- +-- Name: notice_dismissals notice_dismissals_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_channels notification_channels_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_channels + ADD CONSTRAINT notification_channels_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_logs notification_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_logs + ADD CONSTRAINT notification_logs_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_preferences notification_preferences_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_preferences + ADD CONSTRAINT notification_preferences_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_queue notification_queue_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_queue + ADD CONSTRAINT notification_queue_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_schedules notification_schedules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_schedules + ADD CONSTRAINT notification_schedules_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_templates notification_templates_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_templates + ADD CONSTRAINT notification_templates_pkey PRIMARY KEY (id); + + +-- +-- Name: notifications notifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT notifications_pkey PRIMARY KEY (id); + + +-- +-- Name: owner_users owner_users_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.owner_users + ADD CONSTRAINT owner_users_pkey PRIMARY KEY (owner_id, user_id); + + +-- +-- Name: patient_discounts patient_discounts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_discounts + ADD CONSTRAINT patient_discounts_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_group_patient patient_group_patient_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_group_patient + ADD CONSTRAINT patient_group_patient_pkey PRIMARY KEY (patient_group_id, patient_id); + + +-- +-- Name: patient_groups patient_groups_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_groups + ADD CONSTRAINT patient_groups_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_intake_requests patient_intake_requests_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_intake_requests + ADD CONSTRAINT patient_intake_requests_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_invites patient_invites_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_invites + ADD CONSTRAINT patient_invites_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_invites patient_invites_token_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_invites + ADD CONSTRAINT patient_invites_token_key UNIQUE (token); + + +-- +-- Name: patient_patient_tag patient_patient_tag_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT patient_patient_tag_pkey PRIMARY KEY (patient_id, tag_id); + + +-- +-- Name: patient_tags patient_tags_owner_name_uniq; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_tags + ADD CONSTRAINT patient_tags_owner_name_uniq UNIQUE (owner_id, nome); + + +-- +-- Name: patient_tags patient_tags_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_tags + ADD CONSTRAINT patient_tags_pkey PRIMARY KEY (id); + + +-- +-- Name: patients patients_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_pkey PRIMARY KEY (id); + + +-- +-- Name: payment_settings payment_settings_owner_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_owner_id_key UNIQUE (owner_id); + + +-- +-- Name: payment_settings payment_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_pkey PRIMARY KEY (id); + + +-- +-- Name: plan_features plan_features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_features + ADD CONSTRAINT plan_features_pkey PRIMARY KEY (plan_id, feature_id); + + +-- +-- Name: plan_prices plan_prices_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_prices + ADD CONSTRAINT plan_prices_pkey PRIMARY KEY (id); + + +-- +-- Name: plan_public_bullets plan_public_bullets_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public_bullets + ADD CONSTRAINT plan_public_bullets_pkey PRIMARY KEY (id); + + +-- +-- Name: plan_public plan_public_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public + ADD CONSTRAINT plan_public_pkey PRIMARY KEY (plan_id); + + +-- +-- Name: plans plans_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plans + ADD CONSTRAINT plans_key_key UNIQUE (key); + + +-- +-- Name: plans plans_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plans + ADD CONSTRAINT plans_pkey PRIMARY KEY (id); + + +-- +-- Name: professional_pricing professional_pricing_owner_commitment_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.professional_pricing + ADD CONSTRAINT professional_pricing_owner_commitment_key UNIQUE (owner_id, determined_commitment_id); + + +-- +-- Name: professional_pricing professional_pricing_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.professional_pricing + ADD CONSTRAINT professional_pricing_pkey PRIMARY KEY (id); + + +-- +-- Name: profiles profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.profiles + ADD CONSTRAINT profiles_pkey PRIMARY KEY (id); + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_pkey PRIMARY KEY (id); + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_unique UNIQUE (recurrence_id, original_date); + + +-- +-- Name: recurrence_rule_services recurrence_rule_services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rule_services + ADD CONSTRAINT recurrence_rule_services_pkey PRIMARY KEY (id); + + +-- +-- Name: recurrence_rules recurrence_rules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rules + ADD CONSTRAINT recurrence_rules_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_admins saas_admins_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_admins + ADD CONSTRAINT saas_admins_pkey PRIMARY KEY (user_id); + + +-- +-- Name: saas_doc_votos saas_doc_votos_doc_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_doc_id_user_id_key UNIQUE (doc_id, user_id); + + +-- +-- Name: saas_doc_votos saas_doc_votos_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_docs saas_docs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_docs + ADD CONSTRAINT saas_docs_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_faq_itens saas_faq_itens_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_faq_itens + ADD CONSTRAINT saas_faq_itens_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_faq saas_faq_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_faq + ADD CONSTRAINT saas_faq_pkey PRIMARY KEY (id); + + +-- +-- Name: services services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.services + ADD CONSTRAINT services_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_events subscription_events_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_events + ADD CONSTRAINT subscription_events_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_intents_personal subscription_intents_personal_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_personal + ADD CONSTRAINT subscription_intents_personal_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_intents_legacy subscription_intents_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_legacy + ADD CONSTRAINT subscription_intents_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_intents_tenant subscription_intents_tenant_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_tenant + ADD CONSTRAINT subscription_intents_tenant_pkey PRIMARY KEY (id); + + +-- +-- Name: subscriptions subscriptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscriptions + ADD CONSTRAINT subscriptions_pkey PRIMARY KEY (id); + + +-- +-- Name: support_sessions support_sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_pkey PRIMARY KEY (id); + + +-- +-- Name: support_sessions support_sessions_token_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_token_unique UNIQUE (token); + + +-- +-- Name: tenant_feature_exceptions_log tenant_feature_exceptions_log_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_feature_exceptions_log + ADD CONSTRAINT tenant_feature_exceptions_log_pkey PRIMARY KEY (id); + + +-- +-- Name: tenant_features tenant_features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_features + ADD CONSTRAINT tenant_features_pkey PRIMARY KEY (tenant_id, feature_key); + + +-- +-- Name: tenant_invites tenant_invites_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_pkey PRIMARY KEY (id); + + +-- +-- Name: tenant_members tenant_members_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_pkey PRIMARY KEY (id); + + +-- +-- Name: tenant_members tenant_members_tenant_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_tenant_id_user_id_key UNIQUE (tenant_id, user_id); + + +-- +-- Name: tenant_modules tenant_modules_owner_id_module_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_owner_id_module_id_key UNIQUE (owner_id, module_id); + + +-- +-- Name: tenant_modules tenant_modules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_pkey PRIMARY KEY (id); + + +-- +-- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenants + ADD CONSTRAINT tenants_pkey PRIMARY KEY (id); + + +-- +-- Name: therapist_payout_records therapist_payout_records_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payout_records + ADD CONSTRAINT therapist_payout_records_pkey PRIMARY KEY (payout_id, financial_record_id); + + +-- +-- Name: therapist_payouts therapist_payouts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payouts + ADD CONSTRAINT therapist_payouts_pkey PRIMARY KEY (id); + + +-- +-- Name: twilio_subaccount_usage twilio_subaccount_usage_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.twilio_subaccount_usage + ADD CONSTRAINT twilio_subaccount_usage_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_credits uq_addon_credits_tenant_type; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT uq_addon_credits_tenant_type UNIQUE (tenant_id, addon_type); + + +-- +-- Name: notification_channels uq_channel_per_owner; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_channels + ADD CONSTRAINT uq_channel_per_owner UNIQUE NULLS NOT DISTINCT (owner_id, channel, deleted_at); + + +-- +-- Name: notification_preferences uq_notif_prefs_patient_owner; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_preferences + ADD CONSTRAINT uq_notif_prefs_patient_owner UNIQUE NULLS NOT DISTINCT (owner_id, patient_id, deleted_at); + + +-- +-- Name: notification_queue uq_notif_queue_idempotency; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_queue + ADD CONSTRAINT uq_notif_queue_idempotency UNIQUE (idempotency_key); + + +-- +-- Name: notification_schedules uq_notif_schedule_owner; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_schedules + ADD CONSTRAINT uq_notif_schedule_owner UNIQUE NULLS NOT DISTINCT (owner_id, schedule_key, deleted_at); + + +-- +-- Name: notification_templates uq_notif_template_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_templates + ADD CONSTRAINT uq_notif_template_key UNIQUE NULLS NOT DISTINCT (tenant_id, owner_id, key, deleted_at); + + +-- +-- Name: user_settings user_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_settings + ADD CONSTRAINT user_settings_pkey PRIMARY KEY (user_id); + + +-- +-- Name: messages messages_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages + ADD CONSTRAINT messages_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_24 messages_2026_03_24_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_24 + ADD CONSTRAINT messages_2026_03_24_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_25 messages_2026_03_25_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_25 + ADD CONSTRAINT messages_2026_03_25_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_26 messages_2026_03_26_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_26 + ADD CONSTRAINT messages_2026_03_26_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_27 messages_2026_03_27_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_27 + ADD CONSTRAINT messages_2026_03_27_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_28 messages_2026_03_28_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_28 + ADD CONSTRAINT messages_2026_03_28_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_29 messages_2026_03_29_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_29 + ADD CONSTRAINT messages_2026_03_29_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_30 messages_2026_03_30_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_30 + ADD CONSTRAINT messages_2026_03_30_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: subscription pk_subscription; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.subscription + ADD CONSTRAINT pk_subscription PRIMARY KEY (id); + + +-- +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.schema_migrations + ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: buckets_analytics buckets_analytics_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.buckets_analytics + ADD CONSTRAINT buckets_analytics_pkey PRIMARY KEY (id); + + +-- +-- Name: buckets buckets_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.buckets + ADD CONSTRAINT buckets_pkey PRIMARY KEY (id); + + +-- +-- Name: buckets_vectors buckets_vectors_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.buckets_vectors + ADD CONSTRAINT buckets_vectors_pkey PRIMARY KEY (id); + + +-- +-- Name: iceberg_namespaces iceberg_namespaces_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_namespaces + ADD CONSTRAINT iceberg_namespaces_pkey PRIMARY KEY (id); + + +-- +-- Name: iceberg_tables iceberg_tables_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_tables + ADD CONSTRAINT iceberg_tables_pkey PRIMARY KEY (id); + + +-- +-- Name: migrations migrations_name_key; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.migrations + ADD CONSTRAINT migrations_name_key UNIQUE (name); + + +-- +-- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.migrations + ADD CONSTRAINT migrations_pkey PRIMARY KEY (id); + + +-- +-- Name: objects objects_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.objects + ADD CONSTRAINT objects_pkey PRIMARY KEY (id); + + +-- +-- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads_parts + ADD CONSTRAINT s3_multipart_uploads_parts_pkey PRIMARY KEY (id); + + +-- +-- Name: s3_multipart_uploads s3_multipart_uploads_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads + ADD CONSTRAINT s3_multipart_uploads_pkey PRIMARY KEY (id); + + +-- +-- Name: vector_indexes vector_indexes_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.vector_indexes + ADD CONSTRAINT vector_indexes_pkey PRIMARY KEY (id); + + +-- +-- Name: hooks hooks_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: - +-- + +ALTER TABLE ONLY supabase_functions.hooks + ADD CONSTRAINT hooks_pkey PRIMARY KEY (id); + + +-- +-- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: - +-- + +ALTER TABLE ONLY supabase_functions.migrations + ADD CONSTRAINT migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: extensions_tenant_external_id_index; Type: INDEX; Schema: _realtime; Owner: - +-- + +CREATE INDEX extensions_tenant_external_id_index ON _realtime.extensions USING btree (tenant_external_id); + + +-- +-- Name: extensions_tenant_external_id_type_index; Type: INDEX; Schema: _realtime; Owner: - +-- + +CREATE UNIQUE INDEX extensions_tenant_external_id_type_index ON _realtime.extensions USING btree (tenant_external_id, type); + + +-- +-- Name: tenants_external_id_index; Type: INDEX; Schema: _realtime; Owner: - +-- + +CREATE UNIQUE INDEX tenants_external_id_index ON _realtime.tenants USING btree (external_id); + + +-- +-- Name: audit_logs_instance_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX audit_logs_instance_id_idx ON auth.audit_log_entries USING btree (instance_id); + + +-- +-- Name: confirmation_token_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX confirmation_token_idx ON auth.users USING btree (confirmation_token) WHERE ((confirmation_token)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: email_change_token_current_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX email_change_token_current_idx ON auth.users USING btree (email_change_token_current) WHERE ((email_change_token_current)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: email_change_token_new_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX email_change_token_new_idx ON auth.users USING btree (email_change_token_new) WHERE ((email_change_token_new)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: factor_id_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX factor_id_created_at_idx ON auth.mfa_factors USING btree (user_id, created_at); + + +-- +-- Name: flow_state_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX flow_state_created_at_idx ON auth.flow_state USING btree (created_at DESC); + + +-- +-- Name: identities_email_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX identities_email_idx ON auth.identities USING btree (email text_pattern_ops); + + +-- +-- Name: INDEX identities_email_idx; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON INDEX auth.identities_email_idx IS 'Auth: Ensures indexed queries on the email column'; + + +-- +-- Name: identities_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX identities_user_id_idx ON auth.identities USING btree (user_id); + + +-- +-- Name: idx_auth_code; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX idx_auth_code ON auth.flow_state USING btree (auth_code); + + +-- +-- Name: idx_oauth_client_states_created_at; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX idx_oauth_client_states_created_at ON auth.oauth_client_states USING btree (created_at); + + +-- +-- Name: idx_user_id_auth_method; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX idx_user_id_auth_method ON auth.flow_state USING btree (user_id, authentication_method); + + +-- +-- Name: mfa_challenge_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX mfa_challenge_created_at_idx ON auth.mfa_challenges USING btree (created_at DESC); + + +-- +-- Name: mfa_factors_user_friendly_name_unique; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX mfa_factors_user_friendly_name_unique ON auth.mfa_factors USING btree (friendly_name, user_id) WHERE (TRIM(BOTH FROM friendly_name) <> ''::text); + + +-- +-- Name: mfa_factors_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX mfa_factors_user_id_idx ON auth.mfa_factors USING btree (user_id); + + +-- +-- Name: oauth_auth_pending_exp_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_auth_pending_exp_idx ON auth.oauth_authorizations USING btree (expires_at) WHERE (status = 'pending'::auth.oauth_authorization_status); + + +-- +-- Name: oauth_clients_deleted_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_clients_deleted_at_idx ON auth.oauth_clients USING btree (deleted_at); + + +-- +-- Name: oauth_consents_active_client_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_consents_active_client_idx ON auth.oauth_consents USING btree (client_id) WHERE (revoked_at IS NULL); + + +-- +-- Name: oauth_consents_active_user_client_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_consents_active_user_client_idx ON auth.oauth_consents USING btree (user_id, client_id) WHERE (revoked_at IS NULL); + + +-- +-- Name: oauth_consents_user_order_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_consents_user_order_idx ON auth.oauth_consents USING btree (user_id, granted_at DESC); + + +-- +-- Name: one_time_tokens_relates_to_hash_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX one_time_tokens_relates_to_hash_idx ON auth.one_time_tokens USING hash (relates_to); + + +-- +-- Name: one_time_tokens_token_hash_hash_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX one_time_tokens_token_hash_hash_idx ON auth.one_time_tokens USING hash (token_hash); + + +-- +-- Name: one_time_tokens_user_id_token_type_key; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX one_time_tokens_user_id_token_type_key ON auth.one_time_tokens USING btree (user_id, token_type); + + +-- +-- Name: reauthentication_token_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX reauthentication_token_idx ON auth.users USING btree (reauthentication_token) WHERE ((reauthentication_token)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: recovery_token_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX recovery_token_idx ON auth.users USING btree (recovery_token) WHERE ((recovery_token)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: refresh_tokens_instance_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_instance_id_idx ON auth.refresh_tokens USING btree (instance_id); + + +-- +-- Name: refresh_tokens_instance_id_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_instance_id_user_id_idx ON auth.refresh_tokens USING btree (instance_id, user_id); + + +-- +-- Name: refresh_tokens_parent_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_parent_idx ON auth.refresh_tokens USING btree (parent); + + +-- +-- Name: refresh_tokens_session_id_revoked_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_session_id_revoked_idx ON auth.refresh_tokens USING btree (session_id, revoked); + + +-- +-- Name: refresh_tokens_updated_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_updated_at_idx ON auth.refresh_tokens USING btree (updated_at DESC); + + +-- +-- Name: saml_providers_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_providers_sso_provider_id_idx ON auth.saml_providers USING btree (sso_provider_id); + + +-- +-- Name: saml_relay_states_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_relay_states_created_at_idx ON auth.saml_relay_states USING btree (created_at DESC); + + +-- +-- Name: saml_relay_states_for_email_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_relay_states_for_email_idx ON auth.saml_relay_states USING btree (for_email); + + +-- +-- Name: saml_relay_states_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_relay_states_sso_provider_id_idx ON auth.saml_relay_states USING btree (sso_provider_id); + + +-- +-- Name: sessions_not_after_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sessions_not_after_idx ON auth.sessions USING btree (not_after DESC); + + +-- +-- Name: sessions_oauth_client_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sessions_oauth_client_id_idx ON auth.sessions USING btree (oauth_client_id); + + +-- +-- Name: sessions_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sessions_user_id_idx ON auth.sessions USING btree (user_id); + + +-- +-- Name: sso_domains_domain_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX sso_domains_domain_idx ON auth.sso_domains USING btree (lower(domain)); + + +-- +-- Name: sso_domains_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sso_domains_sso_provider_id_idx ON auth.sso_domains USING btree (sso_provider_id); + + +-- +-- Name: sso_providers_resource_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX sso_providers_resource_id_idx ON auth.sso_providers USING btree (lower(resource_id)); + + +-- +-- Name: sso_providers_resource_id_pattern_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sso_providers_resource_id_pattern_idx ON auth.sso_providers USING btree (resource_id text_pattern_ops); + + +-- +-- Name: unique_phone_factor_per_user; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX unique_phone_factor_per_user ON auth.mfa_factors USING btree (user_id, phone); + + +-- +-- Name: user_id_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX user_id_created_at_idx ON auth.sessions USING btree (user_id, created_at); + + +-- +-- Name: users_email_partial_key; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX users_email_partial_key ON auth.users USING btree (email) WHERE (is_sso_user = false); + + +-- +-- Name: INDEX users_email_partial_key; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON INDEX auth.users_email_partial_key IS 'Auth: A partial unique index that applies only when is_sso_user is false'; + + +-- +-- Name: users_instance_id_email_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX users_instance_id_email_idx ON auth.users USING btree (instance_id, lower((email)::text)); + + +-- +-- Name: users_instance_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX users_instance_id_idx ON auth.users USING btree (instance_id); + + +-- +-- Name: users_is_anonymous_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX users_is_anonymous_idx ON auth.users USING btree (is_anonymous); + + +-- +-- Name: agenda_bloqueios_owner_data_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_owner_data_idx ON public.agenda_bloqueios USING btree (owner_id, data_inicio, data_fim); + + +-- +-- Name: agenda_bloqueios_owner_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_owner_id_idx ON public.agenda_bloqueios USING btree (owner_id); + + +-- +-- Name: agenda_bloqueios_recorrente_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_recorrente_idx ON public.agenda_bloqueios USING btree (owner_id, dia_semana) WHERE (recorrente = true); + + +-- +-- Name: agenda_bloqueios_tenant_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_tenant_id_idx ON public.agenda_bloqueios USING btree (tenant_id); + + +-- +-- Name: agenda_configuracoes_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_configuracoes_tenant_idx ON public.agenda_configuracoes USING btree (tenant_id); + + +-- +-- Name: agenda_configuracoes_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_configuracoes_tenant_owner_idx ON public.agenda_configuracoes USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_eventos_billing_contract_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_billing_contract_idx ON public.agenda_eventos USING btree (billing_contract_id) WHERE (billing_contract_id IS NOT NULL); + + +-- +-- Name: agenda_eventos_insurance_plan_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_insurance_plan_idx ON public.agenda_eventos USING btree (insurance_plan_id); + + +-- +-- Name: agenda_eventos_owner_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_owner_inicio_idx ON public.agenda_eventos USING btree (owner_id, inicio_em); + + +-- +-- Name: agenda_eventos_owner_terapeuta_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_owner_terapeuta_inicio_idx ON public.agenda_eventos USING btree (owner_id, terapeuta_id, inicio_em); + + +-- +-- Name: agenda_eventos_recurrence_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_recurrence_idx ON public.agenda_eventos USING btree (recurrence_id) WHERE (recurrence_id IS NOT NULL); + + +-- +-- Name: agenda_eventos_tenant_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_tenant_inicio_idx ON public.agenda_eventos USING btree (tenant_id, inicio_em); + + +-- +-- Name: agenda_eventos_tenant_owner_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_tenant_owner_inicio_idx ON public.agenda_eventos USING btree (tenant_id, owner_id, inicio_em); + + +-- +-- Name: agenda_excecoes_owner_data_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_excecoes_owner_data_idx ON public.agenda_excecoes USING btree (owner_id, data); + + +-- +-- Name: agenda_excecoes_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_excecoes_tenant_idx ON public.agenda_excecoes USING btree (tenant_id); + + +-- +-- Name: agenda_excecoes_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_excecoes_tenant_owner_idx ON public.agenda_excecoes USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_online_slots_owner_weekday_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_online_slots_owner_weekday_idx ON public.agenda_online_slots USING btree (owner_id, weekday); + + +-- +-- Name: agenda_online_slots_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_online_slots_tenant_idx ON public.agenda_online_slots USING btree (tenant_id); + + +-- +-- Name: agenda_online_slots_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_online_slots_tenant_owner_idx ON public.agenda_online_slots USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_regras_semanais_owner_dia_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_regras_semanais_owner_dia_idx ON public.agenda_regras_semanais USING btree (owner_id, dia_semana); + + +-- +-- Name: agenda_regras_semanais_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_regras_semanais_tenant_idx ON public.agenda_regras_semanais USING btree (tenant_id); + + +-- +-- Name: agenda_regras_semanais_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_regras_semanais_tenant_owner_idx ON public.agenda_regras_semanais USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_slots_bloqueados_semanais_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_bloqueados_semanais_tenant_idx ON public.agenda_slots_bloqueados_semanais USING btree (tenant_id); + + +-- +-- Name: agenda_slots_bloqueados_semanais_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_bloqueados_semanais_tenant_owner_idx ON public.agenda_slots_bloqueados_semanais USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_slots_regras_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_regras_tenant_idx ON public.agenda_slots_regras USING btree (tenant_id); + + +-- +-- Name: agenda_slots_regras_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_regras_tenant_owner_idx ON public.agenda_slots_regras USING btree (tenant_id, owner_id); + + +-- +-- Name: agendador_cfg_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_cfg_tenant_idx ON public.agendador_configuracoes USING btree (tenant_id); + + +-- +-- Name: agendador_sol_data_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_sol_data_idx ON public.agendador_solicitacoes USING btree (data_solicitada, hora_solicitada); + + +-- +-- Name: agendador_sol_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_sol_owner_idx ON public.agendador_solicitacoes USING btree (owner_id, status); + + +-- +-- Name: agendador_sol_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_sol_tenant_idx ON public.agendador_solicitacoes USING btree (tenant_id); + + +-- +-- Name: billing_contracts_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX billing_contracts_owner_idx ON public.billing_contracts USING btree (owner_id); + + +-- +-- Name: billing_contracts_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX billing_contracts_patient_idx ON public.billing_contracts USING btree (patient_id); + + +-- +-- Name: billing_contracts_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX billing_contracts_tenant_idx ON public.billing_contracts USING btree (tenant_id); + + +-- +-- Name: commitment_services_commitment_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_services_commitment_idx ON public.commitment_services USING btree (commitment_id); + + +-- +-- Name: commitment_services_service_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_services_service_idx ON public.commitment_services USING btree (service_id); + + +-- +-- Name: commitment_time_logs_calendar_event_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_time_logs_calendar_event_idx ON public.commitment_time_logs USING btree (calendar_event_id); + + +-- +-- Name: commitment_time_logs_commitment_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_time_logs_commitment_idx ON public.commitment_time_logs USING btree (commitment_id, created_at DESC); + + +-- +-- Name: commitment_time_logs_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_time_logs_tenant_idx ON public.commitment_time_logs USING btree (tenant_id, created_at DESC); + + +-- +-- Name: determined_commitment_fields_commitment_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitment_fields_commitment_idx ON public.determined_commitment_fields USING btree (commitment_id, sort_order); + + +-- +-- Name: determined_commitment_fields_key_uniq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX determined_commitment_fields_key_uniq ON public.determined_commitment_fields USING btree (commitment_id, key); + + +-- +-- Name: determined_commitment_fields_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitment_fields_tenant_idx ON public.determined_commitment_fields USING btree (tenant_id); + + +-- +-- Name: determined_commitments_active_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitments_active_idx ON public.determined_commitments USING btree (tenant_id, active); + + +-- +-- Name: determined_commitments_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitments_tenant_idx ON public.determined_commitments USING btree (tenant_id); + + +-- +-- Name: determined_commitments_tenant_name_uniq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX determined_commitments_tenant_name_uniq ON public.determined_commitments USING btree (tenant_id, lower(name)); + + +-- +-- Name: feriados_global_unique; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX feriados_global_unique ON public.feriados USING btree (data, nome) WHERE (tenant_id IS NULL); + + +-- +-- Name: financial_exceptions_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX financial_exceptions_owner_idx ON public.financial_exceptions USING btree (owner_id); + + +-- +-- Name: financial_exceptions_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX financial_exceptions_tenant_idx ON public.financial_exceptions USING btree (tenant_id); + + +-- +-- Name: idx_addon_credits_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_credits_tenant ON public.addon_credits USING btree (tenant_id) WHERE (is_active = true); + + +-- +-- Name: idx_addon_credits_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_credits_type ON public.addon_credits USING btree (addon_type) WHERE (is_active = true); + + +-- +-- Name: idx_addon_products_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_products_active ON public.addon_products USING btree (is_active, is_visible) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_addon_products_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_products_type ON public.addon_products USING btree (addon_type) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_addon_tx_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_created ON public.addon_transactions USING btree (created_at DESC); + + +-- +-- Name: idx_addon_tx_queue; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_queue ON public.addon_transactions USING btree (queue_id) WHERE (queue_id IS NOT NULL); + + +-- +-- Name: idx_addon_tx_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_tenant ON public.addon_transactions USING btree (tenant_id, addon_type); + + +-- +-- Name: idx_addon_tx_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_type ON public.addon_transactions USING btree (type); + + +-- +-- Name: idx_agenda_eventos_determined_commitment_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_agenda_eventos_determined_commitment_id ON public.agenda_eventos USING btree (determined_commitment_id); + + +-- +-- Name: idx_agenda_excecoes_owner_data; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_agenda_excecoes_owner_data ON public.agenda_excecoes USING btree (owner_id, data); + + +-- +-- Name: idx_agenda_slots_regras_owner_dia; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_agenda_slots_regras_owner_dia ON public.agenda_slots_regras USING btree (owner_id, dia_semana); + + +-- +-- Name: idx_email_templates_global_domain; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_global_domain ON public.email_templates_global USING btree (domain) WHERE (is_active = true); + + +-- +-- Name: idx_email_templates_global_key; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_global_key ON public.email_templates_global USING btree (key) WHERE (is_active = true); + + +-- +-- Name: idx_email_templates_tenant_lookup; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_tenant_lookup ON public.email_templates_tenant USING btree (tenant_id, template_key) WHERE (enabled = true); + + +-- +-- Name: idx_email_templates_tenant_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_tenant_owner ON public.email_templates_tenant USING btree (owner_id, template_key) WHERE ((enabled = true) AND (owner_id IS NOT NULL)); + + +-- +-- Name: idx_financial_categories_user_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_categories_user_id ON public.financial_categories USING btree (user_id); + + +-- +-- Name: idx_financial_records_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_active ON public.financial_records USING btree (owner_id, paid_at DESC) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_financial_records_agenda_evento_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_agenda_evento_id ON public.financial_records USING btree (agenda_evento_id) WHERE (agenda_evento_id IS NOT NULL); + + +-- +-- Name: idx_financial_records_category_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_category_id ON public.financial_records USING btree (category_id) WHERE (category_id IS NOT NULL); + + +-- +-- Name: idx_financial_records_due_date; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_due_date ON public.financial_records USING btree (due_date); + + +-- +-- Name: idx_financial_records_installment_group; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_installment_group ON public.financial_records USING btree (installment_group) WHERE (installment_group IS NOT NULL); + + +-- +-- Name: idx_financial_records_owner_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_owner_id ON public.financial_records USING btree (owner_id); + + +-- +-- Name: idx_financial_records_paid_at; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_paid_at ON public.financial_records USING btree (paid_at DESC); + + +-- +-- Name: idx_financial_records_patient_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_patient_id ON public.financial_records USING btree (patient_id); + + +-- +-- Name: idx_financial_records_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_status ON public.financial_records USING btree (status) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_financial_records_tenant_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_tenant_active ON public.financial_records USING btree (tenant_id, paid_at DESC) WHERE ((deleted_at IS NULL) AND (tenant_id IS NOT NULL)); + + +-- +-- Name: idx_financial_records_tenant_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_tenant_id ON public.financial_records USING btree (tenant_id); + + +-- +-- Name: idx_financial_records_type_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_type_status ON public.financial_records USING btree (type, status); + + +-- +-- Name: idx_global_notices_active_priority; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_global_notices_active_priority ON public.global_notices USING btree (is_active, priority DESC, starts_at, ends_at); + + +-- +-- Name: idx_intakes_converted_patient_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_converted_patient_id ON public.patient_intake_requests USING btree (converted_patient_id); + + +-- +-- Name: idx_intakes_owner_cpf; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_owner_cpf ON public.patient_intake_requests USING btree (owner_id, cpf); + + +-- +-- Name: idx_intakes_owner_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_owner_created ON public.patient_intake_requests USING btree (owner_id, created_at DESC); + + +-- +-- Name: idx_intakes_owner_status_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_owner_status_created ON public.patient_intake_requests USING btree (owner_id, status, created_at DESC); + + +-- +-- Name: idx_intakes_status_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_status_created ON public.patient_intake_requests USING btree (status, created_at DESC); + + +-- +-- Name: idx_notice_dismissals_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notice_dismissals_user ON public.notice_dismissals USING btree (user_id, notice_id); + + +-- +-- Name: idx_notif_channels_owner_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_channels_owner_active ON public.notification_channels USING btree (owner_id, channel) WHERE ((is_active = true) AND (deleted_at IS NULL)); + + +-- +-- Name: idx_notif_channels_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_channels_tenant ON public.notification_channels USING btree (tenant_id) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_logs_owner_date; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_owner_date ON public.notification_logs USING btree (owner_id, created_at DESC); + + +-- +-- Name: idx_notif_logs_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_patient ON public.notification_logs USING btree (patient_id, created_at DESC); + + +-- +-- Name: idx_notif_logs_provider_msg; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_provider_msg ON public.notification_logs USING btree (provider_message_id) WHERE (provider_message_id IS NOT NULL); + + +-- +-- Name: idx_notif_logs_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_status ON public.notification_logs USING btree (status, created_at DESC); + + +-- +-- Name: idx_notif_logs_tenant_date; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_tenant_date ON public.notification_logs USING btree (tenant_id, created_at DESC); + + +-- +-- Name: idx_notif_prefs_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_prefs_owner ON public.notification_preferences USING btree (owner_id) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_prefs_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_prefs_patient ON public.notification_preferences USING btree (patient_id) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_prefs_whatsapp_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_prefs_whatsapp_active ON public.notification_preferences USING btree (owner_id, patient_id) WHERE ((whatsapp_opt_in = true) AND (deleted_at IS NULL)); + + +-- +-- Name: idx_notif_queue_evento; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_evento ON public.notification_queue USING btree (agenda_evento_id) WHERE (status = ANY (ARRAY['pendente'::text, 'processando'::text])); + + +-- +-- Name: idx_notif_queue_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_patient ON public.notification_queue USING btree (patient_id, channel) WHERE (status = 'pendente'::text); + + +-- +-- Name: idx_notif_queue_pending; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_pending ON public.notification_queue USING btree (scheduled_at) WHERE (status = 'pendente'::text); + + +-- +-- Name: idx_notif_queue_processing; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_processing ON public.notification_queue USING btree (status, updated_at) WHERE (status = 'processando'::text); + + +-- +-- Name: idx_notif_queue_retry; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_retry ON public.notification_queue USING btree (next_retry_at) WHERE ((status = 'pendente'::text) AND (attempts > 0)); + + +-- +-- Name: idx_notif_queue_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_tenant ON public.notification_queue USING btree (tenant_id, created_at DESC); + + +-- +-- Name: idx_notif_schedules_owner_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_schedules_owner_active ON public.notification_schedules USING btree (owner_id, event_type) WHERE ((is_active = true) AND (deleted_at IS NULL)); + + +-- +-- Name: idx_notif_templates_default; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_templates_default ON public.notification_templates USING btree (channel, event_type) WHERE ((is_default = true) AND (deleted_at IS NULL) AND (tenant_id IS NULL)); + + +-- +-- Name: idx_notif_templates_lookup; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_templates_lookup ON public.notification_templates USING btree (channel, event_type, is_active) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_templates_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_templates_tenant ON public.notification_templates USING btree (tenant_id, channel, event_type) WHERE ((deleted_at IS NULL) AND (is_active = true)); + + +-- +-- Name: idx_notification_channels_twilio_subaccount_sid; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notification_channels_twilio_subaccount_sid ON public.notification_channels USING btree (twilio_subaccount_sid) WHERE (twilio_subaccount_sid IS NOT NULL); + + +-- +-- Name: idx_patient_group_patient_group_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_group_patient_group_id ON public.patient_group_patient USING btree (patient_group_id); + + +-- +-- Name: idx_patient_groups_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_groups_owner ON public.patient_groups USING btree (owner_id); + + +-- +-- Name: idx_patient_groups_owner_system_nome; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_groups_owner_system_nome ON public.patient_groups USING btree (owner_id, is_system, nome); + + +-- +-- Name: idx_patient_tags_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_tags_owner ON public.patient_tags USING btree (owner_id); + + +-- +-- Name: idx_patients_created_at; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_created_at ON public.patients USING btree (created_at DESC); + + +-- +-- Name: idx_patients_last_attended; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_last_attended ON public.patients USING btree (last_attended_at DESC); + + +-- +-- Name: idx_patients_owner_email_principal; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_owner_email_principal ON public.patients USING btree (owner_id, email_principal); + + +-- +-- Name: idx_patients_owner_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_owner_id ON public.patients USING btree (owner_id); + + +-- +-- Name: idx_patients_owner_nome; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_owner_nome ON public.patients USING btree (owner_id, nome_completo); + + +-- +-- Name: idx_patients_responsible_member; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_responsible_member ON public.patients USING btree (responsible_member_id); + + +-- +-- Name: idx_patients_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_status ON public.patients USING btree (status); + + +-- +-- Name: idx_patients_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_tenant ON public.patients USING btree (tenant_id); + + +-- +-- Name: idx_patients_tenant_email_norm; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_tenant_email_norm ON public.patients USING btree (tenant_id, lower(TRIM(BOTH FROM email_principal))); + + +-- +-- Name: idx_pgp_group; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pgp_group ON public.patient_group_patient USING btree (patient_group_id); + + +-- +-- Name: idx_pgp_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pgp_patient ON public.patient_group_patient USING btree (patient_id); + + +-- +-- Name: idx_ppt_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_ppt_patient ON public.patient_patient_tag USING btree (patient_id); + + +-- +-- Name: idx_ppt_tag; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_ppt_tag ON public.patient_patient_tag USING btree (tag_id); + + +-- +-- Name: idx_slots_bloq_owner_dia; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_slots_bloq_owner_dia ON public.agenda_slots_bloqueados_semanais USING btree (owner_id, dia_semana); + + +-- +-- Name: idx_subscription_intents_plan_interval; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_subscription_intents_plan_interval ON public.subscription_intents_legacy USING btree (plan_key, "interval"); + + +-- +-- Name: idx_subscription_intents_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_subscription_intents_status ON public.subscription_intents_legacy USING btree (status); + + +-- +-- Name: idx_subscription_intents_user_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_subscription_intents_user_id ON public.subscription_intents_legacy USING btree (user_id); + + +-- +-- Name: idx_tenant_features_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_tenant_features_tenant ON public.tenant_features USING btree (tenant_id); + + +-- +-- Name: idx_tenant_invites_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_tenant_invites_tenant ON public.tenant_invites USING btree (tenant_id); + + +-- +-- Name: idx_tenant_invites_token; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_tenant_invites_token ON public.tenant_invites USING btree (token); + + +-- +-- Name: idx_therapist_payout_records_financial_record_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payout_records_financial_record_id ON public.therapist_payout_records USING btree (financial_record_id); + + +-- +-- Name: idx_therapist_payouts_owner_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_owner_id ON public.therapist_payouts USING btree (owner_id); + + +-- +-- Name: idx_therapist_payouts_period; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_period ON public.therapist_payouts USING btree (period_start, period_end); + + +-- +-- Name: idx_therapist_payouts_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_status ON public.therapist_payouts USING btree (status) WHERE (status = 'pending'::text); + + +-- +-- Name: idx_therapist_payouts_tenant_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_tenant_id ON public.therapist_payouts USING btree (tenant_id); + + +-- +-- Name: idx_twilio_usage_channel; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_twilio_usage_channel ON public.twilio_subaccount_usage USING btree (channel_id, period_start DESC); + + +-- +-- Name: idx_twilio_usage_tenant_period; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_twilio_usage_tenant_period ON public.twilio_subaccount_usage USING btree (tenant_id, period_start DESC); + + +-- +-- Name: idx_twilio_usage_unique_period; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX idx_twilio_usage_unique_period ON public.twilio_subaccount_usage USING btree (channel_id, period_start, period_end); + + +-- +-- Name: insurance_plans_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX insurance_plans_owner_idx ON public.insurance_plans USING btree (owner_id); + + +-- +-- Name: insurance_plans_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX insurance_plans_tenant_idx ON public.insurance_plans USING btree (tenant_id); + + +-- +-- Name: ix_plan_prices_plan; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ix_plan_prices_plan ON public.plan_prices USING btree (plan_id); + + +-- +-- Name: ix_plan_public_bullets_plan; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ix_plan_public_bullets_plan ON public.plan_public_bullets USING btree (plan_id); + + +-- +-- Name: ix_plan_public_sort; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ix_plan_public_sort ON public.plan_public USING btree (sort_order); + + +-- +-- Name: notifications_owner_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX notifications_owner_created ON public.notifications USING btree (owner_id, created_at DESC); + + +-- +-- Name: notifications_owner_unread; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX notifications_owner_unread ON public.notifications USING btree (owner_id, read_at) WHERE (read_at IS NULL); + + +-- +-- Name: patient_discounts_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_discounts_owner_idx ON public.patient_discounts USING btree (owner_id); + + +-- +-- Name: patient_discounts_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_discounts_patient_idx ON public.patient_discounts USING btree (patient_id); + + +-- +-- Name: patient_discounts_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_discounts_tenant_idx ON public.patient_discounts USING btree (tenant_id); + + +-- +-- Name: patient_group_patient_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_group_patient_tenant_idx ON public.patient_group_patient USING btree (tenant_id); + + +-- +-- Name: patient_groups_owner_nome_uniq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX patient_groups_owner_nome_uniq ON public.patient_groups USING btree (owner_id, nome); + + +-- +-- Name: patient_groups_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_groups_tenant_idx ON public.patient_groups USING btree (tenant_id); + + +-- +-- Name: patient_intake_owner_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_owner_id_idx ON public.patient_intake_requests USING btree (owner_id); + + +-- +-- Name: patient_intake_requests_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_requests_tenant_idx ON public.patient_intake_requests USING btree (tenant_id); + + +-- +-- Name: patient_intake_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_status_idx ON public.patient_intake_requests USING btree (status); + + +-- +-- Name: patient_intake_token_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_token_idx ON public.patient_intake_requests USING btree (token); + + +-- +-- Name: patient_invites_one_active_per_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX patient_invites_one_active_per_owner ON public.patient_invites USING btree (owner_id) WHERE (active = true); + + +-- +-- Name: patient_invites_owner_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_invites_owner_id_idx ON public.patient_invites USING btree (owner_id); + + +-- +-- Name: patient_invites_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_invites_tenant_idx ON public.patient_invites USING btree (tenant_id); + + +-- +-- Name: patient_invites_token_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_invites_token_idx ON public.patient_invites USING btree (token); + + +-- +-- Name: patient_patient_tag_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_patient_tag_tenant_idx ON public.patient_patient_tag USING btree (tenant_id); + + +-- +-- Name: patient_tags_owner_name_uq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX patient_tags_owner_name_uq ON public.patient_tags USING btree (owner_id, lower(nome)); + + +-- +-- Name: patient_tags_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_tags_tenant_idx ON public.patient_tags USING btree (tenant_id); + + +-- +-- Name: payment_settings_tenant_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX payment_settings_tenant_id_idx ON public.payment_settings USING btree (tenant_id); + + +-- +-- Name: ppt_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ppt_owner_idx ON public.patient_patient_tag USING btree (owner_id); + + +-- +-- Name: ppt_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ppt_patient_idx ON public.patient_patient_tag USING btree (patient_id); + + +-- +-- Name: ppt_tag_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ppt_tag_idx ON public.patient_patient_tag USING btree (tag_id); + + +-- +-- Name: professional_pricing_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX professional_pricing_tenant_idx ON public.professional_pricing USING btree (tenant_id); + + +-- +-- Name: profiles_work_description_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX profiles_work_description_idx ON public.profiles USING btree (work_description) WHERE (work_description IS NOT NULL); + + +-- +-- Name: recurrence_exceptions_rule_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_exceptions_rule_idx ON public.recurrence_exceptions USING btree (recurrence_id); + + +-- +-- Name: recurrence_exceptions_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_exceptions_tenant_idx ON public.recurrence_exceptions USING btree (tenant_id); + + +-- +-- Name: recurrence_rule_services_rule_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rule_services_rule_idx ON public.recurrence_rule_services USING btree (rule_id); + + +-- +-- Name: recurrence_rule_services_service_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rule_services_service_idx ON public.recurrence_rule_services USING btree (service_id); + + +-- +-- Name: recurrence_rules_active_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_active_idx ON public.recurrence_rules USING btree (owner_id, status) WHERE (status = 'ativo'::text); + + +-- +-- Name: recurrence_rules_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_owner_idx ON public.recurrence_rules USING btree (owner_id); + + +-- +-- Name: recurrence_rules_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_patient_idx ON public.recurrence_rules USING btree (patient_id); + + +-- +-- Name: recurrence_rules_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_tenant_idx ON public.recurrence_rules USING btree (tenant_id); + + +-- +-- Name: saas_doc_votos_doc_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_doc_votos_doc_id_idx ON public.saas_doc_votos USING btree (doc_id); + + +-- +-- Name: saas_doc_votos_user_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_doc_votos_user_id_idx ON public.saas_doc_votos USING btree (user_id); + + +-- +-- Name: saas_docs_categoria_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_docs_categoria_idx ON public.saas_docs USING btree (categoria); + + +-- +-- Name: saas_docs_exibir_no_faq_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_docs_exibir_no_faq_idx ON public.saas_docs USING btree (exibir_no_faq) WHERE (exibir_no_faq = true); + + +-- +-- Name: saas_docs_path_ativo_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_docs_path_ativo_idx ON public.saas_docs USING btree (pagina_path, ativo); + + +-- +-- Name: saas_faq_ativo_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_ativo_idx ON public.saas_faq USING btree (ativo); + + +-- +-- Name: saas_faq_categoria_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_categoria_idx ON public.saas_faq USING btree (categoria); + + +-- +-- Name: saas_faq_fts_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_fts_idx ON public.saas_faq USING gin (to_tsvector('portuguese'::regconfig, ((COALESCE(pergunta, ''::text) || ' '::text) || COALESCE(conteudo, ''::text)))); + + +-- +-- Name: saas_faq_itens_ativo_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_itens_ativo_idx ON public.saas_faq_itens USING btree (ativo); + + +-- +-- Name: saas_faq_itens_doc_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_itens_doc_id_idx ON public.saas_faq_itens USING btree (doc_id); + + +-- +-- Name: saas_faq_pagina_path_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_pagina_path_idx ON public.saas_faq USING btree (pagina_path); + + +-- +-- Name: saas_faq_publico_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_publico_idx ON public.saas_faq USING btree (publico); + + +-- +-- Name: saas_faq_votos_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_votos_idx ON public.saas_faq USING btree (votos DESC); + + +-- +-- Name: services_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX services_owner_idx ON public.services USING btree (owner_id); + + +-- +-- Name: services_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX services_tenant_idx ON public.services USING btree (tenant_id); + + +-- +-- Name: sint_personal_created_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_personal_created_idx ON public.subscription_intents_personal USING btree (created_at DESC); + + +-- +-- Name: sint_personal_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_personal_status_idx ON public.subscription_intents_personal USING btree (status); + + +-- +-- Name: sint_tenant_created_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_tenant_created_idx ON public.subscription_intents_tenant USING btree (created_at DESC); + + +-- +-- Name: sint_tenant_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_tenant_status_idx ON public.subscription_intents_tenant USING btree (status); + + +-- +-- Name: sint_tenant_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_tenant_tenant_idx ON public.subscription_intents_tenant USING btree (tenant_id); + + +-- +-- Name: subscription_events_created_at_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_created_at_idx ON public.subscription_events USING btree (created_at DESC); + + +-- +-- Name: subscription_events_owner_ref_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_owner_ref_idx ON public.subscription_events USING btree (owner_type, owner_ref); + + +-- +-- Name: subscription_events_sub_created_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_sub_created_idx ON public.subscription_events USING btree (subscription_id, created_at DESC); + + +-- +-- Name: subscription_events_subscription_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_subscription_id_idx ON public.subscription_events USING btree (subscription_id); + + +-- +-- Name: subscriptions_one_active_per_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX subscriptions_one_active_per_tenant ON public.subscriptions USING btree (tenant_id) WHERE (status = 'active'::text); + + +-- +-- Name: subscriptions_one_active_per_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX subscriptions_one_active_per_user ON public.subscriptions USING btree (user_id) WHERE (status = 'active'::text); + + +-- +-- Name: subscriptions_one_active_per_user_personal; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX subscriptions_one_active_per_user_personal ON public.subscriptions USING btree (user_id) WHERE ((tenant_id IS NULL) AND (status = 'active'::text)); + + +-- +-- Name: subscriptions_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_owner_idx ON public.subscriptions USING btree (user_id); + + +-- +-- Name: subscriptions_plan_key_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_plan_key_idx ON public.subscriptions USING btree (plan_key); + + +-- +-- Name: subscriptions_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_status_idx ON public.subscriptions USING btree (status); + + +-- +-- Name: subscriptions_tenant_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_tenant_id_idx ON public.subscriptions USING btree (tenant_id); + + +-- +-- Name: subscriptions_tenant_period_end_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_tenant_period_end_idx ON public.subscriptions USING btree (tenant_id, current_period_end); + + +-- +-- Name: subscriptions_tenant_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_tenant_status_idx ON public.subscriptions USING btree (tenant_id, status); + + +-- +-- Name: subscriptions_user_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_user_status_idx ON public.subscriptions USING btree (user_id, status, created_at DESC); + + +-- +-- Name: support_sessions_expires_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX support_sessions_expires_idx ON public.support_sessions USING btree (expires_at); + + +-- +-- Name: support_sessions_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX support_sessions_tenant_idx ON public.support_sessions USING btree (tenant_id); + + +-- +-- Name: support_sessions_token_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX support_sessions_token_idx ON public.support_sessions USING btree (token); + + +-- +-- Name: tenant_members_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX tenant_members_tenant_idx ON public.tenant_members USING btree (tenant_id); + + +-- +-- Name: tenant_members_user_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX tenant_members_user_idx ON public.tenant_members USING btree (user_id); + + +-- +-- Name: tenant_modules_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX tenant_modules_owner_idx ON public.tenant_modules USING btree (owner_id); + + +-- +-- Name: unique_member_per_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX unique_member_per_tenant ON public.tenant_members USING btree (tenant_id, user_id); + + +-- +-- Name: uq_patients_tenant_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_patients_tenant_user ON public.patients USING btree (tenant_id, user_id) WHERE (user_id IS NOT NULL); + + +-- +-- Name: uq_plan_price_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_plan_price_active ON public.plan_prices USING btree (plan_id, "interval", currency) WHERE ((is_active = true) AND (active_to IS NULL)); + + +-- +-- Name: uq_plan_prices_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_plan_prices_active ON public.plan_prices USING btree (plan_id, "interval") WHERE (is_active = true); + + +-- +-- Name: uq_subscriptions_active_by_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_subscriptions_active_by_tenant ON public.subscriptions USING btree (tenant_id) WHERE ((tenant_id IS NOT NULL) AND (status = 'active'::text)); + + +-- +-- Name: uq_subscriptions_active_personal_by_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_subscriptions_active_personal_by_user ON public.subscriptions USING btree (user_id) WHERE ((tenant_id IS NULL) AND (status = 'active'::text)); + + +-- +-- Name: uq_tenant_invites_pending; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_tenant_invites_pending ON public.tenant_invites USING btree (tenant_id, lower(email), role) WHERE ((accepted_at IS NULL) AND (revoked_at IS NULL)); + + +-- +-- Name: uq_tenant_members_tenant_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_tenant_members_tenant_user ON public.tenant_members USING btree (tenant_id, user_id); + + +-- +-- Name: ux_subscriptions_active_per_personal_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX ux_subscriptions_active_per_personal_user ON public.subscriptions USING btree (user_id) WHERE ((status = 'active'::text) AND (tenant_id IS NULL)); + + +-- +-- Name: ux_subscriptions_active_per_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX ux_subscriptions_active_per_tenant ON public.subscriptions USING btree (tenant_id) WHERE ((status = 'active'::text) AND (tenant_id IS NOT NULL)); + + +-- +-- Name: ix_realtime_subscription_entity; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX ix_realtime_subscription_entity ON realtime.subscription USING btree (entity); + + +-- +-- Name: messages_inserted_at_topic_index; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_inserted_at_topic_index ON ONLY realtime.messages USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_24_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_24_inserted_at_topic_idx ON realtime.messages_2026_03_24 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_25_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_25_inserted_at_topic_idx ON realtime.messages_2026_03_25 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_26_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_26_inserted_at_topic_idx ON realtime.messages_2026_03_26 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_27_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_27_inserted_at_topic_idx ON realtime.messages_2026_03_27 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_28_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_28_inserted_at_topic_idx ON realtime.messages_2026_03_28 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_29_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_29_inserted_at_topic_idx ON realtime.messages_2026_03_29 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_30_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_30_inserted_at_topic_idx ON realtime.messages_2026_03_30 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: subscription_subscription_id_entity_filters_key; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE UNIQUE INDEX subscription_subscription_id_entity_filters_key ON realtime.subscription USING btree (subscription_id, entity, filters); + + +-- +-- Name: bname; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX bname ON storage.buckets USING btree (name); + + +-- +-- Name: bucketid_objname; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX bucketid_objname ON storage.objects USING btree (bucket_id, name); + + +-- +-- Name: buckets_analytics_unique_name_idx; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX buckets_analytics_unique_name_idx ON storage.buckets_analytics USING btree (name) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_iceberg_namespaces_bucket_id; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX idx_iceberg_namespaces_bucket_id ON storage.iceberg_namespaces USING btree (catalog_id, name); + + +-- +-- Name: idx_iceberg_tables_location; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX idx_iceberg_tables_location ON storage.iceberg_tables USING btree (location); + + +-- +-- Name: idx_iceberg_tables_namespace_id; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX idx_iceberg_tables_namespace_id ON storage.iceberg_tables USING btree (catalog_id, namespace_id, name); + + +-- +-- Name: idx_multipart_uploads_list; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX idx_multipart_uploads_list ON storage.s3_multipart_uploads USING btree (bucket_id, key, created_at); + + +-- +-- Name: idx_objects_bucket_id_name; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX idx_objects_bucket_id_name ON storage.objects USING btree (bucket_id, name COLLATE "C"); + + +-- +-- Name: idx_objects_bucket_id_name_lower; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX idx_objects_bucket_id_name_lower ON storage.objects USING btree (bucket_id, lower(name) COLLATE "C"); + + +-- +-- Name: name_prefix_search; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX name_prefix_search ON storage.objects USING btree (name text_pattern_ops); + + +-- +-- Name: vector_indexes_name_bucket_id_idx; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX vector_indexes_name_bucket_id_idx ON storage.vector_indexes USING btree (name, bucket_id); + + +-- +-- Name: supabase_functions_hooks_h_table_id_h_name_idx; Type: INDEX; Schema: supabase_functions; Owner: - +-- + +CREATE INDEX supabase_functions_hooks_h_table_id_h_name_idx ON supabase_functions.hooks USING btree (hook_table_id, hook_name); + + +-- +-- Name: supabase_functions_hooks_request_id_idx; Type: INDEX; Schema: supabase_functions; Owner: - +-- + +CREATE INDEX supabase_functions_hooks_request_id_idx ON supabase_functions.hooks USING btree (request_id); + + +-- +-- Name: messages_2026_03_24_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_24_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_24_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_24_pkey; + + +-- +-- Name: messages_2026_03_25_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_25_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_25_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_25_pkey; + + +-- +-- Name: messages_2026_03_26_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_26_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_26_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_26_pkey; + + +-- +-- Name: messages_2026_03_27_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_27_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_27_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_27_pkey; + + +-- +-- Name: messages_2026_03_28_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_28_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_28_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_28_pkey; + + +-- +-- Name: messages_2026_03_29_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_29_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_29_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_29_pkey; + + +-- +-- Name: messages_2026_03_30_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_30_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_30_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_30_pkey; + + +-- +-- Name: users on_auth_user_created; Type: TRIGGER; Schema: auth; Owner: - +-- + +CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.handle_new_user(); + + +-- +-- Name: users trg_seed_patient_groups; Type: TRIGGER; Schema: auth; Owner: - +-- + +CREATE TRIGGER trg_seed_patient_groups AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.on_new_user_seed_patient_groups(); + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER agenda_bloqueios_updated_at BEFORE UPDATE ON public.agenda_bloqueios FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agendador_configuracoes agendador_slug_trigger; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER agendador_slug_trigger BEFORE INSERT OR UPDATE ON public.agendador_configuracoes FOR EACH ROW EXECUTE FUNCTION public.agendador_gerar_slug(); + + +-- +-- Name: tenant_members prevent_saas_membership_trigger; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER prevent_saas_membership_trigger BEFORE INSERT ON public.tenant_members FOR EACH ROW EXECUTE FUNCTION public.prevent_saas_membership(); + + +-- +-- Name: insurance_plan_services set_insurance_plan_services_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER set_insurance_plan_services_updated_at BEFORE UPDATE ON public.insurance_plan_services FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: user_settings t_user_settings_set_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER t_user_settings_set_updated_at BEFORE UPDATE ON public.user_settings FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_configuracoes tg_agenda_configuracoes_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_configuracoes_updated_at BEFORE UPDATE ON public.agenda_configuracoes FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_eventos tg_agenda_eventos_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_eventos_updated_at BEFORE UPDATE ON public.agenda_eventos FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_excecoes tg_agenda_excecoes_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_excecoes_updated_at BEFORE UPDATE ON public.agenda_excecoes FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_regras_semanais tg_agenda_regras_semanais_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_regras_semanais_updated_at BEFORE UPDATE ON public.agenda_regras_semanais FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: recurrence_rules tg_recurrence_rules_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_recurrence_rules_updated_at BEFORE UPDATE ON public.recurrence_rules FOR EACH ROW EXECUTE FUNCTION public.set_updated_at_recurrence(); + + +-- +-- Name: plan_public tr_plan_public_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tr_plan_public_updated_at BEFORE UPDATE ON public.plan_public FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: profiles trg_account_type_immutable; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_account_type_immutable BEFORE UPDATE OF account_type ON public.profiles FOR EACH ROW EXECUTE FUNCTION public.guard_account_type_immutable(); + + +-- +-- Name: agenda_configuracoes trg_agenda_cfg_sync; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_cfg_sync BEFORE INSERT OR UPDATE ON public.agenda_configuracoes FOR EACH ROW EXECUTE FUNCTION public.agenda_cfg_sync(); + + +-- +-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_del; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_eventos_busy_mirror_del AFTER DELETE ON public.agenda_eventos FOR EACH ROW WHEN (((old.mirror_of_event_id IS NULL) AND (old.tenant_id = old.owner_id))) EXECUTE FUNCTION public.sync_busy_mirror_agenda_eventos(); + + +-- +-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_ins; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_eventos_busy_mirror_ins AFTER INSERT ON public.agenda_eventos FOR EACH ROW WHEN (((new.mirror_of_event_id IS NULL) AND (new.tenant_id = new.owner_id) AND (new.visibility_scope = ANY (ARRAY['busy_only'::text, 'private'::text])))) EXECUTE FUNCTION public.sync_busy_mirror_agenda_eventos(); + + +-- +-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_upd; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_eventos_busy_mirror_upd AFTER UPDATE ON public.agenda_eventos FOR EACH ROW WHEN (((new.mirror_of_event_id IS NULL) AND (new.tenant_id = new.owner_id) AND ((new.visibility_scope IS DISTINCT FROM old.visibility_scope) OR (new.inicio_em IS DISTINCT FROM old.inicio_em) OR (new.fim_em IS DISTINCT FROM old.fim_em) OR (new.owner_id IS DISTINCT FROM old.owner_id) OR (new.tenant_id IS DISTINCT FROM old.tenant_id)))) EXECUTE FUNCTION public.sync_busy_mirror_agenda_eventos(); + + +-- +-- Name: agenda_regras_semanais trg_agenda_regras_semanais_no_overlap; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_regras_semanais_no_overlap BEFORE INSERT OR UPDATE ON public.agenda_regras_semanais FOR EACH ROW EXECUTE FUNCTION public.fn_agenda_regras_semanais_no_overlap(); + + +-- +-- Name: agenda_eventos trg_auto_financial_from_session; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_auto_financial_from_session AFTER UPDATE OF status ON public.agenda_eventos FOR EACH ROW EXECUTE FUNCTION public.auto_create_financial_record_from_session(); + + +-- +-- Name: notification_preferences trg_cancel_notifs_on_opt_out; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_cancel_notifs_on_opt_out AFTER UPDATE ON public.notification_preferences FOR EACH ROW EXECUTE FUNCTION public.cancel_notifications_on_opt_out(); + + +-- +-- Name: agenda_eventos trg_cancel_notifs_on_session_cancel; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_cancel_notifs_on_session_cancel AFTER UPDATE ON public.agenda_eventos FOR EACH ROW WHEN ((new.status IS DISTINCT FROM old.status)) EXECUTE FUNCTION public.cancel_notifications_on_session_cancel(); + + +-- +-- Name: company_profiles trg_company_profiles_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_company_profiles_updated_at BEFORE UPDATE ON public.company_profiles FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: determined_commitment_fields trg_determined_commitment_fields_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_determined_commitment_fields_updated_at BEFORE UPDATE ON public.determined_commitment_fields FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: determined_commitments trg_determined_commitments_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_determined_commitments_updated_at BEFORE UPDATE ON public.determined_commitments FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: email_layout_config trg_email_layout_config_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_email_layout_config_updated_at BEFORE UPDATE ON public.email_layout_config FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: email_templates_global trg_email_templates_global_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_email_templates_global_updated_at BEFORE UPDATE ON public.email_templates_global FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: email_templates_tenant trg_email_templates_tenant_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_email_templates_tenant_updated_at BEFORE UPDATE ON public.email_templates_tenant FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: financial_exceptions trg_financial_exceptions_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_financial_exceptions_updated_at BEFORE UPDATE ON public.financial_exceptions FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: financial_records trg_financial_records_auto_overdue; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_financial_records_auto_overdue BEFORE UPDATE ON public.financial_records FOR EACH ROW EXECUTE FUNCTION public.trg_fn_financial_records_auto_overdue(); + + +-- +-- Name: financial_records trg_financial_records_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_financial_records_updated_at BEFORE UPDATE ON public.financial_records FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: global_notices trg_global_notices_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_global_notices_updated_at BEFORE UPDATE ON public.global_notices FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: insurance_plans trg_insurance_plans_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_insurance_plans_updated_at BEFORE UPDATE ON public.insurance_plans FOR EACH ROW EXECUTE FUNCTION public.set_insurance_plans_updated_at(); + + +-- +-- Name: plans trg_no_change_core_plan_key; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_no_change_core_plan_key BEFORE UPDATE ON public.plans FOR EACH ROW EXECUTE FUNCTION public.guard_no_change_core_plan_key(); + + +-- +-- Name: plans trg_no_change_plan_target; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_no_change_plan_target BEFORE UPDATE ON public.plans FOR EACH ROW EXECUTE FUNCTION public.guard_no_change_plan_target(); + + +-- +-- Name: plans trg_no_delete_core_plans; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_no_delete_core_plans BEFORE DELETE ON public.plans FOR EACH ROW EXECUTE FUNCTION public.guard_no_delete_core_plans(); + + +-- +-- Name: notification_channels trg_notification_channels_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_channels_updated_at BEFORE UPDATE ON public.notification_channels FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_logs trg_notification_logs_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_logs_updated_at BEFORE UPDATE ON public.notification_logs FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_preferences trg_notification_preferences_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_preferences_updated_at BEFORE UPDATE ON public.notification_preferences FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_queue trg_notification_queue_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_queue_updated_at BEFORE UPDATE ON public.notification_queue FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_schedules trg_notification_schedules_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_schedules_updated_at BEFORE UPDATE ON public.notification_schedules FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_templates trg_notification_templates_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_templates_updated_at BEFORE UPDATE ON public.notification_templates FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patient_intake_requests trg_notify_on_intake; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notify_on_intake AFTER INSERT ON public.patient_intake_requests FOR EACH ROW EXECUTE FUNCTION public.notify_on_intake(); + + +-- +-- Name: agendador_solicitacoes trg_notify_on_scheduling; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notify_on_scheduling AFTER INSERT ON public.agendador_solicitacoes FOR EACH ROW EXECUTE FUNCTION public.notify_on_scheduling(); + + +-- +-- Name: agenda_eventos trg_notify_on_session_status; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notify_on_session_status AFTER UPDATE OF status ON public.agenda_eventos FOR EACH ROW EXECUTE FUNCTION public.notify_on_session_status(); + + +-- +-- Name: tenant_members trg_patient_cannot_own_tenant; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_cannot_own_tenant BEFORE INSERT OR UPDATE ON public.tenant_members FOR EACH ROW EXECUTE FUNCTION public.guard_patient_cannot_own_tenant(); + + +-- +-- Name: patient_groups trg_patient_groups_set_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_groups_set_updated_at BEFORE UPDATE ON public.patient_groups FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patient_intake_requests trg_patient_intake_requests_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_intake_requests_updated_at BEFORE UPDATE ON public.patient_intake_requests FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patient_tags trg_patient_tags_set_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_tags_set_updated_at BEFORE UPDATE ON public.patient_tags FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patients trg_patients_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patients_updated_at BEFORE UPDATE ON public.patients FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patients trg_patients_validate_members; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patients_validate_members BEFORE INSERT OR UPDATE OF tenant_id, responsible_member_id, patient_scope, therapist_member_id ON public.patients FOR EACH ROW EXECUTE FUNCTION public.patients_validate_member_consistency(); + + +-- +-- Name: payment_settings trg_payment_settings_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_payment_settings_updated_at BEFORE UPDATE ON public.payment_settings FOR EACH ROW EXECUTE FUNCTION public.update_payment_settings_updated_at(); + + +-- +-- Name: patient_groups trg_prevent_promoting_to_system; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_prevent_promoting_to_system BEFORE UPDATE ON public.patient_groups FOR EACH ROW EXECUTE FUNCTION public.prevent_promoting_to_system(); + + +-- +-- Name: patient_groups trg_prevent_system_group_changes; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_prevent_system_group_changes BEFORE DELETE OR UPDATE ON public.patient_groups FOR EACH ROW EXECUTE FUNCTION public.prevent_system_group_changes(); + + +-- +-- Name: professional_pricing trg_professional_pricing_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_professional_pricing_updated_at BEFORE UPDATE ON public.professional_pricing FOR EACH ROW EXECUTE FUNCTION public.update_professional_pricing_updated_at(); + + +-- +-- Name: profiles trg_profiles_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_profiles_updated_at BEFORE UPDATE ON public.profiles FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: services trg_services_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_services_updated_at BEFORE UPDATE ON public.services FOR EACH ROW EXECUTE FUNCTION public.set_services_updated_at(); + + +-- +-- Name: subscription_intents trg_subscription_intents_view_insert; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_subscription_intents_view_insert INSTEAD OF INSERT ON public.subscription_intents FOR EACH ROW EXECUTE FUNCTION public.subscription_intents_view_insert(); + + +-- +-- Name: subscriptions trg_subscriptions_validate_scope; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_subscriptions_validate_scope BEFORE INSERT OR UPDATE ON public.subscriptions FOR EACH ROW EXECUTE FUNCTION public.subscriptions_validate_scope(); + + +-- +-- Name: tenant_features trg_tenant_features_guard_with_plan; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_tenant_features_guard_with_plan BEFORE INSERT OR UPDATE ON public.tenant_features FOR EACH ROW EXECUTE FUNCTION public.tenant_features_guard_with_plan(); + + +-- +-- Name: tenant_features trg_tenant_features_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_tenant_features_updated_at BEFORE UPDATE ON public.tenant_features FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: tenants trg_tenant_kind_immutable; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_tenant_kind_immutable BEFORE UPDATE OF kind ON public.tenants FOR EACH ROW EXECUTE FUNCTION public.guard_tenant_kind_immutable(); + + +-- +-- Name: therapist_payouts trg_therapist_payouts_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_therapist_payouts_updated_at BEFORE UPDATE ON public.therapist_payouts FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: user_settings trg_user_settings_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_user_settings_updated_at BEFORE UPDATE ON public.user_settings FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: subscription tr_check_filters; Type: TRIGGER; Schema: realtime; Owner: - +-- + +CREATE TRIGGER tr_check_filters BEFORE INSERT OR UPDATE ON realtime.subscription FOR EACH ROW EXECUTE FUNCTION realtime.subscription_check_filters(); + + +-- +-- Name: buckets enforce_bucket_name_length_trigger; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER enforce_bucket_name_length_trigger BEFORE INSERT OR UPDATE OF name ON storage.buckets FOR EACH ROW EXECUTE FUNCTION storage.enforce_bucket_name_length(); + + +-- +-- Name: buckets protect_buckets_delete; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER protect_buckets_delete BEFORE DELETE ON storage.buckets FOR EACH STATEMENT EXECUTE FUNCTION storage.protect_delete(); + + +-- +-- Name: objects protect_objects_delete; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER protect_objects_delete BEFORE DELETE ON storage.objects FOR EACH STATEMENT EXECUTE FUNCTION storage.protect_delete(); + + +-- +-- Name: objects update_objects_updated_at; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER update_objects_updated_at BEFORE UPDATE ON storage.objects FOR EACH ROW EXECUTE FUNCTION storage.update_updated_at_column(); + + +-- +-- Name: extensions extensions_tenant_external_id_fkey; Type: FK CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.extensions + ADD CONSTRAINT extensions_tenant_external_id_fkey FOREIGN KEY (tenant_external_id) REFERENCES _realtime.tenants(external_id) ON DELETE CASCADE; + + +-- +-- Name: identities identities_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.identities + ADD CONSTRAINT identities_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: mfa_amr_claims mfa_amr_claims_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_amr_claims + ADD CONSTRAINT mfa_amr_claims_session_id_fkey FOREIGN KEY (session_id) REFERENCES auth.sessions(id) ON DELETE CASCADE; + + +-- +-- Name: mfa_challenges mfa_challenges_auth_factor_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_challenges + ADD CONSTRAINT mfa_challenges_auth_factor_id_fkey FOREIGN KEY (factor_id) REFERENCES auth.mfa_factors(id) ON DELETE CASCADE; + + +-- +-- Name: mfa_factors mfa_factors_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_factors + ADD CONSTRAINT mfa_factors_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_authorizations oauth_authorizations_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_client_id_fkey FOREIGN KEY (client_id) REFERENCES auth.oauth_clients(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_authorizations oauth_authorizations_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_consents oauth_consents_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_client_id_fkey FOREIGN KEY (client_id) REFERENCES auth.oauth_clients(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_consents oauth_consents_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: one_time_tokens one_time_tokens_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.one_time_tokens + ADD CONSTRAINT one_time_tokens_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: refresh_tokens refresh_tokens_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens + ADD CONSTRAINT refresh_tokens_session_id_fkey FOREIGN KEY (session_id) REFERENCES auth.sessions(id) ON DELETE CASCADE; + + +-- +-- Name: saml_providers saml_providers_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_providers + ADD CONSTRAINT saml_providers_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; + + +-- +-- Name: saml_relay_states saml_relay_states_flow_state_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_relay_states + ADD CONSTRAINT saml_relay_states_flow_state_id_fkey FOREIGN KEY (flow_state_id) REFERENCES auth.flow_state(id) ON DELETE CASCADE; + + +-- +-- Name: saml_relay_states saml_relay_states_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_relay_states + ADD CONSTRAINT saml_relay_states_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; + + +-- +-- Name: sessions sessions_oauth_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sessions + ADD CONSTRAINT sessions_oauth_client_id_fkey FOREIGN KEY (oauth_client_id) REFERENCES auth.oauth_clients(id) ON DELETE CASCADE; + + +-- +-- Name: sessions sessions_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sessions + ADD CONSTRAINT sessions_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: sso_domains sso_domains_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sso_domains + ADD CONSTRAINT sso_domains_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; + + +-- +-- Name: addon_credits addon_credits_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT addon_credits_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id); + + +-- +-- Name: addon_credits addon_credits_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT addon_credits_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id); + + +-- +-- Name: addon_transactions addon_transactions_admin_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_admin_user_id_fkey FOREIGN KEY (admin_user_id) REFERENCES auth.users(id); + + +-- +-- Name: addon_transactions addon_transactions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id); + + +-- +-- Name: addon_transactions addon_transactions_product_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.addon_products(id); + + +-- +-- Name: addon_transactions addon_transactions_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id); + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_bloqueios + ADD CONSTRAINT agenda_bloqueios_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_bloqueios + ADD CONSTRAINT agenda_bloqueios_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_configuracoes + ADD CONSTRAINT agenda_configuracoes_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_eventos agenda_eventos_billing_contract_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_billing_contract_id_fkey FOREIGN KEY (billing_contract_id) REFERENCES public.billing_contracts(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_determined_commitment_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_determined_commitment_fk FOREIGN KEY (determined_commitment_id) REFERENCES public.determined_commitments(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_insurance_plan_id_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_insurance_plan_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_insurance_plan_service_id_fkey FOREIGN KEY (insurance_plan_service_id) REFERENCES public.insurance_plan_services(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_recurrence_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_recurrence_id_fkey FOREIGN KEY (recurrence_id) REFERENCES public.recurrence_rules(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_terapeuta_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_terapeuta_fk FOREIGN KEY (terapeuta_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_excecoes agenda_excecoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_excecoes + ADD CONSTRAINT agenda_excecoes_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_online_slots agenda_online_slots_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_online_slots agenda_online_slots_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_regras_semanais + ADD CONSTRAINT agenda_regras_semanais_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais + ADD CONSTRAINT agenda_slots_bloqueados_semanais_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_regras + ADD CONSTRAINT agenda_slots_regras_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_configuracoes agendador_configuracoes_owner_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_configuracoes + ADD CONSTRAINT agendador_configuracoes_owner_fk FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_configuracoes agendador_configuracoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_configuracoes + ADD CONSTRAINT agendador_configuracoes_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_solicitacoes agendador_sol_owner_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_solicitacoes + ADD CONSTRAINT agendador_sol_owner_fk FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_solicitacoes agendador_sol_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_solicitacoes + ADD CONSTRAINT agendador_sol_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: billing_contracts billing_contracts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.billing_contracts + ADD CONSTRAINT billing_contracts_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: billing_contracts billing_contracts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.billing_contracts + ADD CONSTRAINT billing_contracts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: commitment_services commitment_services_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_services + ADD CONSTRAINT commitment_services_commitment_id_fkey FOREIGN KEY (commitment_id) REFERENCES public.agenda_eventos(id) ON DELETE CASCADE; + + +-- +-- Name: commitment_services commitment_services_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_services + ADD CONSTRAINT commitment_services_service_id_fkey FOREIGN KEY (service_id) REFERENCES public.services(id) ON DELETE RESTRICT; + + +-- +-- Name: commitment_time_logs commitment_time_logs_calendar_event_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_calendar_event_id_fkey FOREIGN KEY (calendar_event_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; + + +-- +-- Name: commitment_time_logs commitment_time_logs_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_commitment_id_fkey FOREIGN KEY (commitment_id) REFERENCES public.determined_commitments(id) ON DELETE CASCADE; + + +-- +-- Name: commitment_time_logs commitment_time_logs_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: company_profiles company_profiles_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.company_profiles + ADD CONSTRAINT company_profiles_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: determined_commitment_fields determined_commitment_fields_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitment_fields + ADD CONSTRAINT determined_commitment_fields_commitment_id_fkey FOREIGN KEY (commitment_id) REFERENCES public.determined_commitments(id) ON DELETE CASCADE; + + +-- +-- Name: determined_commitment_fields determined_commitment_fields_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitment_fields + ADD CONSTRAINT determined_commitment_fields_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: determined_commitments determined_commitments_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitments + ADD CONSTRAINT determined_commitments_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: email_layout_config email_layout_config_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_layout_config + ADD CONSTRAINT email_layout_config_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: email_templates_tenant email_templates_tenant_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: email_templates_tenant email_templates_tenant_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: feriados feriados_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: feriados feriados_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: financial_categories financial_categories_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_categories + ADD CONSTRAINT financial_categories_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: financial_exceptions financial_exceptions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_exceptions + ADD CONSTRAINT financial_exceptions_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: financial_records financial_records_agenda_evento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_agenda_evento_id_fkey FOREIGN KEY (agenda_evento_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_category_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_category_id_fkey FOREIGN KEY (category_id) REFERENCES public.financial_categories(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_insurance_plan_id_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_user_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: global_notices global_notices_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.global_notices + ADD CONSTRAINT global_notices_created_by_fkey FOREIGN KEY (created_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: insurance_plan_services insurance_plan_services_plan_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plan_services + ADD CONSTRAINT insurance_plan_services_plan_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE CASCADE; + + +-- +-- Name: insurance_plans insurance_plans_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plans + ADD CONSTRAINT insurance_plans_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: module_features module_features_feature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.module_features + ADD CONSTRAINT module_features_feature_id_fkey FOREIGN KEY (feature_id) REFERENCES public.features(id) ON DELETE CASCADE; + + +-- +-- Name: module_features module_features_module_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.module_features + ADD CONSTRAINT module_features_module_id_fkey FOREIGN KEY (module_id) REFERENCES public.modules(id) ON DELETE CASCADE; + + +-- +-- Name: notice_dismissals notice_dismissals_notice_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_notice_id_fkey FOREIGN KEY (notice_id) REFERENCES public.global_notices(id) ON DELETE CASCADE; + + +-- +-- Name: notice_dismissals notice_dismissals_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notification_channels notification_channels_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_channels + ADD CONSTRAINT notification_channels_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notification_logs notification_logs_queue_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_logs + ADD CONSTRAINT notification_logs_queue_id_fkey FOREIGN KEY (queue_id) REFERENCES public.notification_queue(id) ON DELETE SET NULL; + + +-- +-- Name: notification_schedules notification_schedules_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_schedules + ADD CONSTRAINT notification_schedules_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notification_templates notification_templates_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_templates + ADD CONSTRAINT notification_templates_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notifications notifications_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT notifications_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: patient_discounts patient_discounts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_discounts + ADD CONSTRAINT patient_discounts_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: patient_discounts patient_discounts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_discounts + ADD CONSTRAINT patient_discounts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_group_patient patient_group_patient_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_group_patient + ADD CONSTRAINT patient_group_patient_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_group_patient patient_group_patient_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_group_patient + ADD CONSTRAINT patient_group_patient_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_groups patient_groups_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_groups + ADD CONSTRAINT patient_groups_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_intake_requests patient_intake_requests_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_intake_requests + ADD CONSTRAINT patient_intake_requests_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_invites patient_invites_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_invites + ADD CONSTRAINT patient_invites_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag patient_patient_tag_tag_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT patient_patient_tag_tag_id_fkey FOREIGN KEY (tag_id) REFERENCES public.patient_tags(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag patient_patient_tag_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT patient_patient_tag_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_tags patient_tags_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_tags + ADD CONSTRAINT patient_tags_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patients patients_responsible_member_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_responsible_member_id_fkey FOREIGN KEY (responsible_member_id) REFERENCES public.tenant_members(id) ON DELETE RESTRICT; + + +-- +-- Name: patients patients_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patients patients_therapist_member_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_therapist_member_id_fkey FOREIGN KEY (therapist_member_id) REFERENCES public.tenant_members(id); + + +-- +-- Name: patients patients_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: payment_settings payment_settings_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: payment_settings payment_settings_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: plan_features plan_features_feature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_features + ADD CONSTRAINT plan_features_feature_id_fkey FOREIGN KEY (feature_id) REFERENCES public.features(id) ON DELETE CASCADE; + + +-- +-- Name: plan_features plan_features_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_features + ADD CONSTRAINT plan_features_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: plan_prices plan_prices_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_prices + ADD CONSTRAINT plan_prices_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: plan_public_bullets plan_public_bullets_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public_bullets + ADD CONSTRAINT plan_public_bullets_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: plan_public plan_public_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public + ADD CONSTRAINT plan_public_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag ppt_patient_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT ppt_patient_fk FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag ppt_tag_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT ppt_tag_fk FOREIGN KEY (tag_id) REFERENCES public.patient_tags(id) ON DELETE CASCADE; + + +-- +-- Name: professional_pricing professional_pricing_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.professional_pricing + ADD CONSTRAINT professional_pricing_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: profiles profiles_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.profiles + ADD CONSTRAINT profiles_id_fkey FOREIGN KEY (id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_agenda_evento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_agenda_evento_id_fkey FOREIGN KEY (agenda_evento_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_recurrence_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_recurrence_id_fkey FOREIGN KEY (recurrence_id) REFERENCES public.recurrence_rules(id) ON DELETE CASCADE; + + +-- +-- Name: recurrence_rule_services recurrence_rule_services_rule_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rule_services + ADD CONSTRAINT recurrence_rule_services_rule_id_fkey FOREIGN KEY (rule_id) REFERENCES public.recurrence_rules(id) ON DELETE CASCADE; + + +-- +-- Name: recurrence_rule_services recurrence_rule_services_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rule_services + ADD CONSTRAINT recurrence_rule_services_service_id_fkey FOREIGN KEY (service_id) REFERENCES public.services(id) ON DELETE RESTRICT; + + +-- +-- Name: recurrence_rules recurrence_rules_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rules + ADD CONSTRAINT recurrence_rules_insurance_plan_id_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + + +-- +-- Name: recurrence_rules recurrence_rules_insurance_plan_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rules + ADD CONSTRAINT recurrence_rules_insurance_plan_service_id_fkey FOREIGN KEY (insurance_plan_service_id) REFERENCES public.insurance_plan_services(id) ON DELETE SET NULL; + + +-- +-- Name: saas_admins saas_admins_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_admins + ADD CONSTRAINT saas_admins_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: saas_doc_votos saas_doc_votos_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_doc_id_fkey FOREIGN KEY (doc_id) REFERENCES public.saas_docs(id) ON DELETE CASCADE; + + +-- +-- Name: saas_doc_votos saas_doc_votos_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: saas_faq_itens saas_faq_itens_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_faq_itens + ADD CONSTRAINT saas_faq_itens_doc_id_fkey FOREIGN KEY (doc_id) REFERENCES public.saas_docs(id) ON DELETE CASCADE; + + +-- +-- Name: services services_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.services + ADD CONSTRAINT services_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: subscription_intents_personal sint_personal_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_personal + ADD CONSTRAINT sint_personal_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES public.subscriptions(id) ON DELETE SET NULL; + + +-- +-- Name: subscription_intents_tenant sint_tenant_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_tenant + ADD CONSTRAINT sint_tenant_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES public.subscriptions(id) ON DELETE SET NULL; + + +-- +-- Name: subscription_events subscription_events_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_events + ADD CONSTRAINT subscription_events_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES public.subscriptions(id) ON DELETE CASCADE; + + +-- +-- Name: subscription_intents_personal subscription_intents_personal_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_personal + ADD CONSTRAINT subscription_intents_personal_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE RESTRICT; + + +-- +-- Name: subscription_intents_tenant subscription_intents_tenant_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_tenant + ADD CONSTRAINT subscription_intents_tenant_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE RESTRICT; + + +-- +-- Name: subscription_intents_legacy subscription_intents_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_legacy + ADD CONSTRAINT subscription_intents_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: subscriptions subscriptions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscriptions + ADD CONSTRAINT subscriptions_owner_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: subscriptions subscriptions_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscriptions + ADD CONSTRAINT subscriptions_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE RESTRICT; + + +-- +-- Name: support_sessions support_sessions_admin_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_admin_fk FOREIGN KEY (admin_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: support_sessions support_sessions_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_features tenant_features_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_features + ADD CONSTRAINT tenant_features_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_invites tenant_invites_accepted_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_accepted_by_fkey FOREIGN KEY (accepted_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: tenant_invites tenant_invites_invited_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_invited_by_fkey FOREIGN KEY (invited_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: tenant_invites tenant_invites_revoked_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_revoked_by_fkey FOREIGN KEY (revoked_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: tenant_invites tenant_invites_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_members tenant_members_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_members tenant_members_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_modules tenant_modules_module_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_module_id_fkey FOREIGN KEY (module_id) REFERENCES public.modules(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_modules tenant_modules_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: therapist_payout_records therapist_payout_records_financial_record_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payout_records + ADD CONSTRAINT therapist_payout_records_financial_record_id_fkey FOREIGN KEY (financial_record_id) REFERENCES public.financial_records(id) ON DELETE RESTRICT; + + +-- +-- Name: therapist_payout_records therapist_payout_records_payout_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payout_records + ADD CONSTRAINT therapist_payout_records_payout_id_fkey FOREIGN KEY (payout_id) REFERENCES public.therapist_payouts(id) ON DELETE CASCADE; + + +-- +-- Name: therapist_payouts therapist_payouts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payouts + ADD CONSTRAINT therapist_payouts_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: therapist_payouts therapist_payouts_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payouts + ADD CONSTRAINT therapist_payouts_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: twilio_subaccount_usage twilio_subaccount_usage_channel_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.twilio_subaccount_usage + ADD CONSTRAINT twilio_subaccount_usage_channel_fk FOREIGN KEY (channel_id) REFERENCES public.notification_channels(id) ON DELETE CASCADE; + + +-- +-- Name: user_settings user_settings_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_settings + ADD CONSTRAINT user_settings_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: iceberg_namespaces iceberg_namespaces_catalog_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_namespaces + ADD CONSTRAINT iceberg_namespaces_catalog_id_fkey FOREIGN KEY (catalog_id) REFERENCES storage.buckets_analytics(id) ON DELETE CASCADE; + + +-- +-- Name: iceberg_tables iceberg_tables_catalog_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_tables + ADD CONSTRAINT iceberg_tables_catalog_id_fkey FOREIGN KEY (catalog_id) REFERENCES storage.buckets_analytics(id) ON DELETE CASCADE; + + +-- +-- Name: iceberg_tables iceberg_tables_namespace_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_tables + ADD CONSTRAINT iceberg_tables_namespace_id_fkey FOREIGN KEY (namespace_id) REFERENCES storage.iceberg_namespaces(id) ON DELETE CASCADE; + + +-- +-- Name: objects objects_bucketId_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.objects + ADD CONSTRAINT "objects_bucketId_fkey" FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); + + +-- +-- Name: s3_multipart_uploads s3_multipart_uploads_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads + ADD CONSTRAINT s3_multipart_uploads_bucket_id_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); + + +-- +-- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads_parts + ADD CONSTRAINT s3_multipart_uploads_parts_bucket_id_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); + + +-- +-- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_upload_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads_parts + ADD CONSTRAINT s3_multipart_uploads_parts_upload_id_fkey FOREIGN KEY (upload_id) REFERENCES storage.s3_multipart_uploads(id) ON DELETE CASCADE; + + +-- +-- Name: vector_indexes vector_indexes_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.vector_indexes + ADD CONSTRAINT vector_indexes_bucket_id_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets_vectors(id); + + +-- +-- Name: audit_log_entries; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.audit_log_entries ENABLE ROW LEVEL SECURITY; + +-- +-- Name: flow_state; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.flow_state ENABLE ROW LEVEL SECURITY; + +-- +-- Name: identities; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.identities ENABLE ROW LEVEL SECURITY; + +-- +-- Name: instances; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.instances ENABLE ROW LEVEL SECURITY; + +-- +-- Name: mfa_amr_claims; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.mfa_amr_claims ENABLE ROW LEVEL SECURITY; + +-- +-- Name: mfa_challenges; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.mfa_challenges ENABLE ROW LEVEL SECURITY; + +-- +-- Name: mfa_factors; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.mfa_factors ENABLE ROW LEVEL SECURITY; + +-- +-- Name: one_time_tokens; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.one_time_tokens ENABLE ROW LEVEL SECURITY; + +-- +-- Name: refresh_tokens; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.refresh_tokens ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saml_providers; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.saml_providers ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saml_relay_states; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.saml_relay_states ENABLE ROW LEVEL SECURITY; + +-- +-- Name: schema_migrations; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.schema_migrations ENABLE ROW LEVEL SECURITY; + +-- +-- Name: sessions; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.sessions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: sso_domains; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.sso_domains ENABLE ROW LEVEL SECURITY; + +-- +-- Name: sso_providers; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.sso_providers ENABLE ROW LEVEL SECURITY; + +-- +-- Name: users; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.users ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_credits; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.addon_credits ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_credits addon_credits_admin_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_credits_admin_select ON public.addon_credits FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_credits addon_credits_admin_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_credits_admin_write ON public.addon_credits TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_credits addon_credits_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_credits_select_own ON public.addon_credits FOR SELECT TO authenticated USING ((public.is_tenant_member(tenant_id) OR (owner_id = auth.uid()))); + + +-- +-- Name: addon_products; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.addon_products ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_products addon_products_admin_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_products_admin_all ON public.addon_products TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_products addon_products_select_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_products_select_authenticated ON public.addon_products FOR SELECT TO authenticated USING (((deleted_at IS NULL) AND (is_active = true) AND (is_visible = true))); + + +-- +-- Name: addon_transactions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.addon_transactions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_transactions addon_transactions_admin_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_transactions_admin_insert ON public.addon_transactions FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_transactions addon_transactions_admin_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_transactions_admin_select ON public.addon_transactions FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_transactions addon_transactions_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_transactions_select_own ON public.addon_transactions FOR SELECT TO authenticated USING ((public.is_tenant_member(tenant_id) OR (owner_id = auth.uid()))); + + +-- +-- Name: agenda_bloqueios; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_bloqueios ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_configuracoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_configuracoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_configuracoes agenda_configuracoes_clinic_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_configuracoes_clinic_read ON public.agenda_configuracoes FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_clinic_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_configuracoes_clinic_write ON public.agenda_configuracoes USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_configuracoes_owner ON public.agenda_configuracoes USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_eventos; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_eventos ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_eventos agenda_eventos_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_delete ON public.agenda_eventos FOR DELETE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.delete'::text))); + + +-- +-- Name: agenda_eventos agenda_eventos_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_insert ON public.agenda_eventos FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.create'::text))); + + +-- +-- Name: agenda_eventos agenda_eventos_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_owner_all ON public.agenda_eventos TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_eventos agenda_eventos_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_select ON public.agenda_eventos FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_eventos agenda_eventos_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_update ON public.agenda_eventos FOR UPDATE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_excecoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_excecoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_excecoes agenda_excecoes_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_excecoes_owner ON public.agenda_excecoes USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_excecoes agenda_excecoes_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_excecoes_select ON public.agenda_excecoes FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_excecoes agenda_excecoes_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_excecoes_write ON public.agenda_excecoes USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_online_slots; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_online_slots ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_online_slots agenda_online_slots_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_online_slots_owner ON public.agenda_online_slots USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_online_slots agenda_online_slots_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_online_slots_select ON public.agenda_online_slots FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_online_slots agenda_online_slots_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_online_slots_write ON public.agenda_online_slots USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_regras_semanais; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_regras_semanais ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_regras_semanais_owner ON public.agenda_regras_semanais USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_regras_semanais_select ON public.agenda_regras_semanais FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_regras_semanais_write ON public.agenda_regras_semanais USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_slots_bloqueados_semanais; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_slots_bloqueados_semanais ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_bloqueados_semanais_select ON public.agenda_slots_bloqueados_semanais FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_bloqueados_semanais_write ON public.agenda_slots_bloqueados_semanais USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_slots_regras; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_slots_regras ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_slots_regras agenda_slots_regras_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_regras_select ON public.agenda_slots_regras FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_regras_write ON public.agenda_slots_regras USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agendador_configuracoes agendador_cfg_public_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_cfg_public_read ON public.agendador_configuracoes FOR SELECT TO anon USING (((ativo = true) AND (link_slug IS NOT NULL))); + + +-- +-- Name: agendador_configuracoes agendador_cfg_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_cfg_select ON public.agendador_configuracoes FOR SELECT USING ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_configuracoes agendador_cfg_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_cfg_write ON public.agendador_configuracoes USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_configuracoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agendador_configuracoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agendador_solicitacoes agendador_sol_owner_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_owner_select ON public.agendador_solicitacoes FOR SELECT USING ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_solicitacoes agendador_sol_owner_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_owner_write ON public.agendador_solicitacoes USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_solicitacoes agendador_sol_patient_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_patient_read ON public.agendador_solicitacoes FOR SELECT TO authenticated USING (((auth.uid() = user_id) OR (auth.uid() = owner_id))); + + +-- +-- Name: agendador_solicitacoes agendador_sol_public_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_public_insert ON public.agendador_solicitacoes FOR INSERT TO anon WITH CHECK (true); + + +-- +-- Name: agendador_solicitacoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agendador_solicitacoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: billing_contracts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.billing_contracts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: billing_contracts billing_contracts: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "billing_contracts: owner full access" ON public.billing_contracts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_delete ON public.agenda_bloqueios FOR DELETE TO authenticated USING ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_insert ON public.agenda_bloqueios FOR INSERT TO authenticated WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_select_clinic; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_select_clinic ON public.agenda_bloqueios FOR SELECT TO authenticated USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE ((tenant_members.user_id = auth.uid()) AND (tenant_members.role = ANY (ARRAY['admin'::text, 'clinic_admin'::text, 'tenant_admin'::text, 'secretary'::text])))))); + + +-- +-- Name: agenda_bloqueios bloqueios_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_select_own ON public.agenda_bloqueios FOR SELECT TO authenticated USING ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_update ON public.agenda_bloqueios FOR UPDATE TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: saas_docs clinic_admin_read_all_docs; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY clinic_admin_read_all_docs ON public.saas_docs FOR SELECT TO authenticated USING (((ativo = true) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = ANY (ARRAY['clinic_admin'::text, 'tenant_admin'::text]))))))); + + +-- +-- Name: commitment_services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.commitment_services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: commitment_services commitment_services: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "commitment_services: owner full access" ON public.commitment_services USING ((EXISTS ( SELECT 1 + FROM public.services s + WHERE ((s.id = commitment_services.service_id) AND (s.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.services s + WHERE ((s.id = commitment_services.service_id) AND (s.owner_id = auth.uid()))))); + + +-- +-- Name: commitment_time_logs; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.commitment_time_logs ENABLE ROW LEVEL SECURITY; + +-- +-- Name: company_profiles; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.company_profiles ENABLE ROW LEVEL SECURITY; + +-- +-- Name: company_profiles company_profiles_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_delete ON public.company_profiles FOR DELETE USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: company_profiles company_profiles_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_insert ON public.company_profiles FOR INSERT WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: company_profiles company_profiles_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_select ON public.company_profiles FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: company_profiles company_profiles_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_update ON public.company_profiles FOR UPDATE USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: commitment_time_logs ctl_delete_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_delete_for_active_member ON public.commitment_time_logs FOR DELETE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: commitment_time_logs ctl_insert_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_insert_for_active_member ON public.commitment_time_logs FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: commitment_time_logs ctl_select_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_select_for_active_member ON public.commitment_time_logs FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: commitment_time_logs ctl_update_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_update_for_active_member ON public.commitment_time_logs FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitments dc_delete_custom_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_delete_custom_for_active_member ON public.determined_commitments FOR DELETE TO authenticated USING (((is_native = false) AND (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: determined_commitments dc_insert_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_insert_for_active_member ON public.determined_commitments FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitments dc_select_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_select_for_active_member ON public.determined_commitments FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitments dc_update_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_update_for_active_member ON public.determined_commitments FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_delete_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_delete_for_active_member ON public.determined_commitment_fields FOR DELETE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_insert_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_insert_for_active_member ON public.determined_commitment_fields FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_select_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_select_for_active_member ON public.determined_commitment_fields FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_update_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_update_for_active_member ON public.determined_commitment_fields FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: agenda_bloqueios delete own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "delete own" ON public.agenda_bloqueios FOR DELETE USING ((owner_id = auth.uid())); + + +-- +-- Name: determined_commitment_fields; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.determined_commitment_fields ENABLE ROW LEVEL SECURITY; + +-- +-- Name: determined_commitments; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.determined_commitments ENABLE ROW LEVEL SECURITY; + +-- +-- Name: dev_user_credentials dev_creds_select_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dev_creds_select_saas_admin ON public.dev_user_credentials FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles p + WHERE ((p.id = auth.uid()) AND (p.role = 'saas_admin'::text))))); + + +-- +-- Name: dev_user_credentials dev_creds_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dev_creds_write_saas_admin ON public.dev_user_credentials TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles p + WHERE ((p.id = auth.uid()) AND (p.role = 'saas_admin'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.profiles p + WHERE ((p.id = auth.uid()) AND (p.role = 'saas_admin'::text))))); + + +-- +-- Name: dev_user_credentials; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.dev_user_credentials ENABLE ROW LEVEL SECURITY; + +-- +-- Name: email_layout_config; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.email_layout_config ENABLE ROW LEVEL SECURITY; + +-- +-- Name: email_templates_global; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.email_templates_global ENABLE ROW LEVEL SECURITY; + +-- +-- Name: email_templates_tenant; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.email_templates_tenant ENABLE ROW LEVEL SECURITY; + +-- +-- Name: entitlements_invalidation ent_inv_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ent_inv_select_own ON public.entitlements_invalidation FOR SELECT USING (((owner_id = auth.uid()) OR public.is_saas_admin())); + + +-- +-- Name: entitlements_invalidation ent_inv_update_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ent_inv_update_saas ON public.entitlements_invalidation FOR UPDATE USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: entitlements_invalidation ent_inv_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ent_inv_write_saas ON public.entitlements_invalidation FOR INSERT WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: entitlements_invalidation; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.entitlements_invalidation ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_faq faq_admin_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_admin_write ON public.saas_faq TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = ANY (ARRAY['saas_admin'::text, 'tenant_admin'::text, 'clinic_admin'::text])))))); + + +-- +-- Name: saas_faq faq_auth_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_auth_read ON public.saas_faq FOR SELECT TO authenticated USING ((ativo = true)); + + +-- +-- Name: saas_faq_itens faq_itens_admin_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_itens_admin_write ON public.saas_faq_itens TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = ANY (ARRAY['saas_admin'::text, 'tenant_admin'::text, 'clinic_admin'::text])))))); + + +-- +-- Name: saas_faq_itens faq_itens_auth_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_itens_auth_read ON public.saas_faq_itens FOR SELECT TO authenticated USING (((ativo = true) AND (EXISTS ( SELECT 1 + FROM public.saas_docs d + WHERE ((d.id = saas_faq_itens.doc_id) AND (d.ativo = true)))))); + + +-- +-- Name: saas_faq faq_public_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_public_read ON public.saas_faq FOR SELECT USING (((publico = true) AND (ativo = true))); + + +-- +-- Name: features; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.features ENABLE ROW LEVEL SECURITY; + +-- +-- Name: features features_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY features_read_authenticated ON public.features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: features features_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY features_write_saas_admin ON public.features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: feriados; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.feriados ENABLE ROW LEVEL SECURITY; + +-- +-- Name: feriados feriados_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_delete ON public.feriados FOR DELETE USING ((owner_id = auth.uid())); + + +-- +-- Name: feriados feriados_global_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_global_select ON public.feriados FOR SELECT USING ((tenant_id IS NULL)); + + +-- +-- Name: feriados feriados_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_insert ON public.feriados FOR INSERT WITH CHECK ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_saas_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_saas_delete ON public.feriados FOR DELETE USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_saas_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_saas_insert ON public.feriados FOR INSERT WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_saas_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_saas_select ON public.feriados FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_select ON public.feriados FOR SELECT USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: financial_categories; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.financial_categories ENABLE ROW LEVEL SECURITY; + +-- +-- Name: financial_categories financial_categories_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_categories_self ON public.financial_categories USING ((auth.uid() = user_id)) WITH CHECK ((auth.uid() = user_id)); + + +-- +-- Name: financial_exceptions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.financial_exceptions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: financial_exceptions financial_exceptions: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "financial_exceptions: owner full access" ON public.financial_exceptions USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: financial_exceptions financial_exceptions: tenant members read clinic rules; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "financial_exceptions: tenant members read clinic rules" ON public.financial_exceptions FOR SELECT USING (((owner_id IS NULL) AND (EXISTS ( SELECT 1 + FROM public.owner_users ou + WHERE ((ou.owner_id = financial_exceptions.tenant_id) AND (ou.user_id = auth.uid())))))); + + +-- +-- Name: financial_records; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.financial_records ENABLE ROW LEVEL SECURITY; + +-- +-- Name: financial_records financial_records_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_records_self ON public.financial_records USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: financial_records financial_records_tenant_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_records_tenant_admin ON public.financial_records FOR SELECT USING (((tenant_id IS NOT NULL) AND public.is_tenant_admin(tenant_id))); + + +-- +-- Name: financial_records financial_records_tenant_member_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_records_tenant_member_read ON public.financial_records FOR SELECT USING (((tenant_id IS NOT NULL) AND (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = financial_records.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: email_templates_global global templates readable by authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "global templates readable by authenticated" ON public.email_templates_global FOR SELECT USING ((auth.role() = 'authenticated'::text)); + + +-- +-- Name: global_notices; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.global_notices ENABLE ROW LEVEL SECURITY; + +-- +-- Name: global_notices global_notices_saas_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY global_notices_saas_all ON public.global_notices TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: global_notices global_notices_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY global_notices_select ON public.global_notices FOR SELECT TO authenticated USING ((is_active = true)); + + +-- +-- Name: agenda_bloqueios insert own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "insert own" ON public.agenda_bloqueios FOR INSERT WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: insurance_plan_services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.insurance_plan_services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: insurance_plan_services insurance_plan_services_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY insurance_plan_services_owner ON public.insurance_plan_services USING ((EXISTS ( SELECT 1 + FROM public.insurance_plans ip + WHERE ((ip.id = insurance_plan_services.insurance_plan_id) AND (ip.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.insurance_plans ip + WHERE ((ip.id = insurance_plan_services.insurance_plan_id) AND (ip.owner_id = auth.uid()))))); + + +-- +-- Name: insurance_plans; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.insurance_plans ENABLE ROW LEVEL SECURITY; + +-- +-- Name: insurance_plans insurance_plans: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "insurance_plans: owner full access" ON public.insurance_plans USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: login_carousel_slides; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.login_carousel_slides ENABLE ROW LEVEL SECURITY; + +-- +-- Name: module_features; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.module_features ENABLE ROW LEVEL SECURITY; + +-- +-- Name: module_features module_features_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY module_features_read_authenticated ON public.module_features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: module_features module_features_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY module_features_write_saas_admin ON public.module_features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: modules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.modules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: modules modules_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY modules_read_authenticated ON public.modules FOR SELECT TO authenticated USING (true); + + +-- +-- Name: modules modules_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY modules_write_saas_admin ON public.modules TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: notice_dismissals; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notice_dismissals ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notice_dismissals notice_dismissals_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notice_dismissals_own ON public.notice_dismissals TO authenticated USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: notification_logs notif_logs_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_logs_owner ON public.notification_logs FOR SELECT USING ((owner_id = auth.uid())); + + +-- +-- Name: notification_preferences notif_prefs_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_prefs_owner ON public.notification_preferences USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: notification_queue notif_queue_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_queue_owner ON public.notification_queue FOR SELECT USING ((owner_id = auth.uid())); + + +-- +-- Name: notification_schedules notif_schedules_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_schedules_owner ON public.notification_schedules USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: notification_templates notif_templates_admin_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_templates_admin_all ON public.notification_templates TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: notification_templates notif_templates_read_global; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_templates_read_global ON public.notification_templates FOR SELECT TO authenticated USING (((deleted_at IS NULL) AND (((tenant_id IS NULL) AND (is_default = true)) OR (owner_id = auth.uid()) OR public.is_tenant_member(tenant_id)))); + + +-- +-- Name: notification_templates notif_templates_write_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_templates_write_owner ON public.notification_templates TO authenticated USING (((owner_id = auth.uid()) OR public.is_tenant_member(tenant_id))) WITH CHECK (((owner_id = auth.uid()) OR public.is_tenant_member(tenant_id))); + + +-- +-- Name: notification_channels; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_channels ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_channels notification_channels_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notification_channels_owner ON public.notification_channels USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: notification_logs; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_logs ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_preferences; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_preferences ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_queue; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_queue ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_schedules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_schedules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_templates; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_templates ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notifications; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notifications owner only; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "owner only" ON public.notifications USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: owner_users; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.owner_users ENABLE ROW LEVEL SECURITY; + +-- +-- Name: owner_users owner_users: user can read own links; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "owner_users: user can read own links" ON public.owner_users FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: patient_discounts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_discounts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_discounts patient_discounts: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "patient_discounts: owner full access" ON public.patient_discounts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_group_patient; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_group_patient ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_group_patient patient_group_patient_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_group_patient_owner_all ON public.patient_group_patient TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.patients p + WHERE ((p.id = patient_group_patient.patient_id) AND (p.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.patients p + WHERE ((p.id = patient_group_patient.patient_id) AND (p.owner_id = auth.uid()))))); + + +-- +-- Name: patient_group_patient patient_group_patient_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_group_patient_select ON public.patient_group_patient FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_group_patient patient_group_patient_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_group_patient_write ON public.patient_group_patient USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_groups; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_groups ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_groups patient_groups_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_groups_owner_all ON public.patient_groups TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_groups patient_groups_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_groups_select ON public.patient_groups FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_groups patient_groups_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_groups_write ON public.patient_groups USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_intake_requests; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_intake_requests ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_intake_requests patient_intake_requests_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_intake_requests_owner_all ON public.patient_intake_requests TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_intake_requests patient_intake_requests_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_intake_requests_select ON public.patient_intake_requests FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_intake_requests patient_intake_requests_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_intake_requests_write ON public.patient_intake_requests USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_invites; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_invites ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_invites patient_invites_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_invites_owner_all ON public.patient_invites TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_invites patient_invites_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_invites_select ON public.patient_invites FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_invites patient_invites_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_invites_write ON public.patient_invites USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_patient_tag; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_patient_tag ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_patient_tag patient_patient_tag_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_patient_tag_owner_all ON public.patient_patient_tag TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_patient_tag patient_patient_tag_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_patient_tag_select ON public.patient_patient_tag FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_patient_tag patient_patient_tag_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_patient_tag_write ON public.patient_patient_tag USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_tags; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_tags ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_tags patient_tags_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_tags_owner_all ON public.patient_tags TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_tags patient_tags_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_tags_select ON public.patient_tags FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_tags patient_tags_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_tags_write ON public.patient_tags USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patients; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patients ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patients patients_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_delete ON public.patients FOR DELETE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.delete'::text))); + + +-- +-- Name: patients patients_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_insert ON public.patients FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.create'::text))); + + +-- +-- Name: patients patients_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_owner_all ON public.patients TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patients patients_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_select ON public.patients FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patients patients_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_update ON public.patients FOR UPDATE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: payment_settings; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.payment_settings ENABLE ROW LEVEL SECURITY; + +-- +-- Name: payment_settings payment_settings: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "payment_settings: owner full access" ON public.payment_settings USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: plan_features; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.plan_features ENABLE ROW LEVEL SECURITY; + +-- +-- Name: plan_features plan_features_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plan_features_read_authenticated ON public.plan_features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plan_features plan_features_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plan_features_write_saas_admin ON public.plan_features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: plans; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.plans ENABLE ROW LEVEL SECURITY; + +-- +-- Name: plans plans_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plans_read_authenticated ON public.plans FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plans plans_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plans_write_saas_admin ON public.plans TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: professional_pricing; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.professional_pricing ENABLE ROW LEVEL SECURITY; + +-- +-- Name: professional_pricing professional_pricing: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "professional_pricing: owner full access" ON public.professional_pricing USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: profiles; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; + +-- +-- Name: profiles profiles_insert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_insert_own ON public.profiles FOR INSERT WITH CHECK ((id = auth.uid())); + + +-- +-- Name: profiles profiles_read_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_read_saas_admin ON public.profiles FOR SELECT USING (public.is_saas_admin()); + + +-- +-- Name: profiles profiles_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_select_own ON public.profiles FOR SELECT USING ((id = auth.uid())); + + +-- +-- Name: profiles profiles_update_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_update_own ON public.profiles FOR UPDATE USING ((id = auth.uid())) WITH CHECK ((id = auth.uid())); + + +-- +-- Name: login_carousel_slides public_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY public_read ON public.login_carousel_slides FOR SELECT USING ((ativo = true)); + + +-- +-- Name: features read features (auth); Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "read features (auth)" ON public.features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plan_features read plan_features (auth); Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "read plan_features (auth)" ON public.plan_features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plans read plans (auth); Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "read plans (auth)" ON public.plans FOR SELECT TO authenticated USING (true); + + +-- +-- Name: recurrence_exceptions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.recurrence_exceptions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: recurrence_exceptions recurrence_exceptions_tenant; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_exceptions_tenant ON public.recurrence_exceptions TO authenticated USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))) WITH CHECK ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: recurrence_rule_services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.recurrence_rule_services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: recurrence_rule_services recurrence_rule_services: clinic read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "recurrence_rule_services: clinic read" ON public.recurrence_rule_services FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND public.is_clinic_tenant(r.tenant_id) AND public.is_tenant_member(r.tenant_id) AND public.tenant_has_feature(r.tenant_id, 'agenda.view'::text))))); + + +-- +-- Name: recurrence_rule_services recurrence_rule_services: clinic write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "recurrence_rule_services: clinic write" ON public.recurrence_rule_services USING ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND public.is_clinic_tenant(r.tenant_id) AND public.is_tenant_member(r.tenant_id) AND public.tenant_has_feature(r.tenant_id, 'agenda.edit'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND public.is_clinic_tenant(r.tenant_id) AND public.is_tenant_member(r.tenant_id) AND public.tenant_has_feature(r.tenant_id, 'agenda.edit'::text))))); + + +-- +-- Name: recurrence_rule_services recurrence_rule_services: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "recurrence_rule_services: owner full access" ON public.recurrence_rule_services TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND (r.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND (r.owner_id = auth.uid()))))); + + +-- +-- Name: recurrence_rules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.recurrence_rules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: recurrence_rules recurrence_rules_clinic_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_rules_clinic_read ON public.recurrence_rules FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: recurrence_rules recurrence_rules_clinic_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_rules_clinic_write ON public.recurrence_rules USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: recurrence_rules recurrence_rules_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_rules_owner ON public.recurrence_rules TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: subscription_intents_legacy saas_admin can read subscription_intents; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "saas_admin can read subscription_intents" ON public.subscription_intents_legacy FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins a + WHERE (a.user_id = auth.uid())))); + + +-- +-- Name: subscription_intents_legacy saas_admin can update subscription_intents; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "saas_admin can update subscription_intents" ON public.subscription_intents_legacy FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins a + WHERE (a.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins a + WHERE (a.user_id = auth.uid())))); + + +-- +-- Name: login_carousel_slides saas_admin_full; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY saas_admin_full ON public.login_carousel_slides USING ((EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text))))); + + +-- +-- Name: saas_docs saas_admin_full_access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY saas_admin_full_access ON public.saas_docs TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: saas_admins; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_admins ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_admins saas_admins_select_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY saas_admins_select_self ON public.saas_admins FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: saas_doc_votos; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_doc_votos ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_docs; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_docs ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_faq; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_faq ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_faq_itens; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_faq_itens ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_bloqueios select own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "select own" ON public.agenda_bloqueios FOR SELECT USING ((owner_id = auth.uid())); + + +-- +-- Name: twilio_subaccount_usage service_role_manage_usage; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY service_role_manage_usage ON public.twilio_subaccount_usage USING ((auth.role() = 'service_role'::text)); + + +-- +-- Name: services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: services services: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "services: owner full access" ON public.services USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: subscription_events; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.subscription_events ENABLE ROW LEVEL SECURITY; + +-- +-- Name: subscription_events subscription_events_read_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_events_read_saas ON public.subscription_events FOR SELECT USING (public.is_saas_admin()); + + +-- +-- Name: subscription_events subscription_events_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_events_write_saas ON public.subscription_events FOR INSERT WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: subscription_intents_legacy subscription_intents_insert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_intents_insert_own ON public.subscription_intents_legacy FOR INSERT TO authenticated WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: subscription_intents_legacy; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.subscription_intents_legacy ENABLE ROW LEVEL SECURITY; + +-- +-- Name: subscription_intents_legacy subscription_intents_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_intents_select_own ON public.subscription_intents_legacy FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: subscriptions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.subscriptions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: subscriptions subscriptions read own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "subscriptions read own" ON public.subscriptions FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: subscriptions subscriptions: read if linked owner_users; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "subscriptions: read if linked owner_users" ON public.subscriptions FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.owner_users ou + WHERE ((ou.owner_id = subscriptions.user_id) AND (ou.user_id = auth.uid()))))); + + +-- +-- Name: subscriptions subscriptions_insert_own_personal; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_insert_own_personal ON public.subscriptions FOR INSERT TO authenticated WITH CHECK (((user_id = auth.uid()) AND (tenant_id IS NULL))); + + +-- +-- Name: subscriptions subscriptions_no_direct_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_no_direct_update ON public.subscriptions FOR UPDATE TO authenticated USING (false) WITH CHECK (false); + + +-- +-- Name: subscriptions subscriptions_read_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_read_own ON public.subscriptions FOR SELECT TO authenticated USING (((user_id = auth.uid()) OR public.is_saas_admin())); + + +-- +-- Name: subscriptions subscriptions_select_for_tenant_members; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_select_for_tenant_members ON public.subscriptions FOR SELECT TO authenticated USING (((tenant_id IS NOT NULL) AND (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = subscriptions.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: subscriptions subscriptions_select_own_personal; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_select_own_personal ON public.subscriptions FOR SELECT TO authenticated USING (((user_id = auth.uid()) AND (tenant_id IS NULL))); + + +-- +-- Name: subscriptions subscriptions_update_only_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_update_only_saas_admin ON public.subscriptions FOR UPDATE TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: support_sessions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.support_sessions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: support_sessions support_sessions_saas_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY support_sessions_saas_delete ON public.support_sessions FOR DELETE USING (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text)))))); + + +-- +-- Name: support_sessions support_sessions_saas_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY support_sessions_saas_insert ON public.support_sessions FOR INSERT WITH CHECK (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text)))))); + + +-- +-- Name: support_sessions support_sessions_saas_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY support_sessions_saas_select ON public.support_sessions FOR SELECT USING (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text)))))); + + +-- +-- Name: email_templates_tenant tenant manages own overrides; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "tenant manages own overrides" ON public.email_templates_tenant USING ((tenant_id = auth.uid())) WITH CHECK ((tenant_id = auth.uid())); + + +-- +-- Name: email_layout_config tenant owns email layout config; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "tenant owns email layout config" ON public.email_layout_config USING ((tenant_id = auth.uid())) WITH CHECK ((tenant_id = auth.uid())); + + +-- +-- Name: tenant_members; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.tenant_members ENABLE ROW LEVEL SECURITY; + +-- +-- Name: tenant_members tenant_members_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_members_write_saas ON public.tenant_members TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: tenant_modules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.tenant_modules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: tenant_modules tenant_modules_read_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_modules_read_own ON public.tenant_modules FOR SELECT TO authenticated USING (((owner_id = auth.uid()) OR public.is_saas_admin())); + + +-- +-- Name: tenant_modules tenant_modules_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_modules_write_saas ON public.tenant_modules TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: twilio_subaccount_usage tenant_select_own_usage; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_select_own_usage ON public.twilio_subaccount_usage FOR SELECT USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: tenants; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.tenants ENABLE ROW LEVEL SECURITY; + +-- +-- Name: tenants tenants_read_members; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenants_read_members ON public.tenants FOR SELECT TO authenticated USING ((public.is_saas_admin() OR (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = tenants.id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: tenants tenants_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenants_write_saas ON public.tenants TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: therapist_payout_records; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.therapist_payout_records ENABLE ROW LEVEL SECURITY; + +-- +-- Name: therapist_payout_records therapist_payout_records_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payout_records_self ON public.therapist_payout_records USING ((EXISTS ( SELECT 1 + FROM public.therapist_payouts tp + WHERE ((tp.id = therapist_payout_records.payout_id) AND (tp.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.therapist_payouts tp + WHERE ((tp.id = therapist_payout_records.payout_id) AND (tp.owner_id = auth.uid()))))); + + +-- +-- Name: therapist_payout_records therapist_payout_records_tenant_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payout_records_tenant_admin ON public.therapist_payout_records FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.therapist_payouts tp + WHERE ((tp.id = therapist_payout_records.payout_id) AND public.is_tenant_admin(tp.tenant_id))))); + + +-- +-- Name: therapist_payouts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.therapist_payouts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: therapist_payouts therapist_payouts_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payouts_self ON public.therapist_payouts USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: therapist_payouts therapist_payouts_tenant_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payouts_tenant_admin ON public.therapist_payouts FOR SELECT USING (((tenant_id IS NOT NULL) AND public.is_tenant_admin(tenant_id))); + + +-- +-- Name: tenant_members tm_select_admin_all_members; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tm_select_admin_all_members ON public.tenant_members FOR SELECT TO authenticated USING (public.is_tenant_admin(tenant_id)); + + +-- +-- Name: tenant_members tm_select_own_membership; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tm_select_own_membership ON public.tenant_members FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: twilio_subaccount_usage; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.twilio_subaccount_usage ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_bloqueios update own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "update own" ON public.agenda_bloqueios FOR UPDATE USING ((owner_id = auth.uid())); + + +-- +-- Name: user_settings; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.user_settings ENABLE ROW LEVEL SECURITY; + +-- +-- Name: user_settings user_settings_insert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY user_settings_insert_own ON public.user_settings FOR INSERT WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: user_settings user_settings_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY user_settings_select_own ON public.user_settings FOR SELECT USING ((user_id = auth.uid())); + + +-- +-- Name: user_settings user_settings_update_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY user_settings_update_own ON public.user_settings FOR UPDATE USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: saas_docs users_read_usuario_docs; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY users_read_usuario_docs ON public.saas_docs FOR SELECT TO authenticated USING (((ativo = true) AND (tipo_acesso = 'usuario'::text))); + + +-- +-- Name: saas_doc_votos votos_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY votos_select_own ON public.saas_doc_votos FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: saas_doc_votos votos_upsert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY votos_upsert_own ON public.saas_doc_votos TO authenticated USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: messages; Type: ROW SECURITY; Schema: realtime; Owner: - +-- + +ALTER TABLE realtime.messages ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects Allow authenticated updates; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "Allow authenticated updates" ON storage.objects FOR UPDATE TO authenticated USING ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); + + +-- +-- Name: objects Allow authenticated uploads; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "Allow authenticated uploads" ON storage.objects FOR INSERT TO authenticated WITH CHECK ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); + + +-- +-- Name: objects Public read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "Public read" ON storage.objects FOR SELECT USING ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); + + +-- +-- Name: objects agendador_storage_owner_delete; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_owner_delete ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects agendador_storage_owner_insert; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_owner_insert ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects agendador_storage_owner_update; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_owner_update ON storage.objects FOR UPDATE TO authenticated USING (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects agendador_storage_public_read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_public_read ON storage.objects FOR SELECT USING ((bucket_id = 'agendador'::text)); + + +-- +-- Name: objects avatars authenticated upload; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars authenticated upload" ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects avatars owner delete; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars owner delete" ON storage.objects FOR DELETE USING (((bucket_id = 'avatars'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects avatars owner update; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars owner update" ON storage.objects FOR UPDATE USING (((bucket_id = 'avatars'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects avatars public read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars public read" ON storage.objects FOR SELECT USING ((bucket_id = 'avatars'::text)); + + +-- +-- Name: objects avatars_delete_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_delete_own ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_delete_own_folder; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_delete_own_folder ON storage.objects FOR DELETE USING (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); + + +-- +-- Name: objects avatars_insert_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_insert_own ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_insert_own_folder; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_insert_own_folder ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); + + +-- +-- Name: objects avatars_read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_read ON storage.objects FOR SELECT USING ((bucket_id = 'avatars'::text)); + + +-- +-- Name: objects avatars_select_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_select_own ON storage.objects FOR SELECT TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_update_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_update_own ON storage.objects FOR UPDATE TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))) WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_update_own_folder; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_update_own_folder ON storage.objects FOR UPDATE USING (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))) WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); + + +-- +-- Name: buckets; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.buckets ENABLE ROW LEVEL SECURITY; + +-- +-- Name: buckets_analytics; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.buckets_analytics ENABLE ROW LEVEL SECURITY; + +-- +-- Name: buckets_vectors; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.buckets_vectors ENABLE ROW LEVEL SECURITY; + +-- +-- Name: iceberg_namespaces; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.iceberg_namespaces ENABLE ROW LEVEL SECURITY; + +-- +-- Name: iceberg_tables; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.iceberg_tables ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects intake_read_anon; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_read_anon ON storage.objects FOR SELECT TO anon USING (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: objects intake_read_public; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_read_public ON storage.objects FOR SELECT USING (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: objects intake_upload_anon; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_upload_anon ON storage.objects FOR INSERT TO anon WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: objects intake_upload_public; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_upload_public ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: migrations; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.migrations ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects public_read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY public_read ON storage.objects FOR SELECT USING ((bucket_id = 'saas-docs'::text)); + + +-- +-- Name: s3_multipart_uploads; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.s3_multipart_uploads ENABLE ROW LEVEL SECURITY; + +-- +-- Name: s3_multipart_uploads_parts; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.s3_multipart_uploads_parts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects saas_admin_delete; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY saas_admin_delete ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'saas-docs'::text) AND (EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid()))))); + + +-- +-- Name: objects saas_admin_upload; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY saas_admin_upload ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'saas-docs'::text) AND (EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid()))))); + + +-- +-- Name: vector_indexes; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.vector_indexes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: supabase_realtime; Type: PUBLICATION; Schema: -; Owner: - +-- + +CREATE PUBLICATION supabase_realtime WITH (publish = 'insert, update, delete, truncate'); + + +-- +-- Name: supabase_realtime_messages_publication; Type: PUBLICATION; Schema: -; Owner: - +-- + +CREATE PUBLICATION supabase_realtime_messages_publication WITH (publish = 'insert, update, delete, truncate'); + + +-- +-- Name: supabase_realtime notifications; Type: PUBLICATION TABLE; Schema: public; Owner: - +-- + +ALTER PUBLICATION supabase_realtime ADD TABLE ONLY public.notifications; + + +-- +-- Name: supabase_realtime_messages_publication messages; Type: PUBLICATION TABLE; Schema: realtime; Owner: - +-- + +ALTER PUBLICATION supabase_realtime_messages_publication ADD TABLE ONLY realtime.messages; + + +-- +-- Name: issue_graphql_placeholder; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_graphql_placeholder ON sql_drop + WHEN TAG IN ('DROP EXTENSION') + EXECUTE FUNCTION extensions.set_graphql_placeholder(); + + +-- +-- Name: issue_pg_cron_access; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_pg_cron_access ON ddl_command_end + WHEN TAG IN ('CREATE EXTENSION') + EXECUTE FUNCTION extensions.grant_pg_cron_access(); + + +-- +-- Name: issue_pg_graphql_access; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_pg_graphql_access ON ddl_command_end + WHEN TAG IN ('CREATE FUNCTION') + EXECUTE FUNCTION extensions.grant_pg_graphql_access(); + + +-- +-- Name: issue_pg_net_access; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_pg_net_access ON ddl_command_end + WHEN TAG IN ('CREATE EXTENSION') + EXECUTE FUNCTION extensions.grant_pg_net_access(); + + +-- +-- Name: pgrst_ddl_watch; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER pgrst_ddl_watch ON ddl_command_end + EXECUTE FUNCTION extensions.pgrst_ddl_watch(); + + +-- +-- Name: pgrst_drop_watch; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER pgrst_drop_watch ON sql_drop + EXECUTE FUNCTION extensions.pgrst_drop_watch(); + + +-- +-- PostgreSQL database dump complete +-- + +\unrestrict oIIu7S4MFsQnGbpSaoEafTAdeIOsvmomx1CO8wf3ssLisMzLEg3OFPh8agVudRl + diff --git a/database-novo/backups/2026-03-27/schema.sql b/database-novo/backups/2026-03-27/schema.sql new file mode 100644 index 0000000..67bef02 --- /dev/null +++ b/database-novo/backups/2026-03-27/schema.sql @@ -0,0 +1,19202 @@ +-- +-- PostgreSQL database dump +-- + +\restrict acnCnMZQsJO473l3dvqVdklBN15FPebE5Zi8Gz0mj5MpfIJdgRLhsX941fb70hT + +-- Dumped from database version 17.6 +-- Dumped by pg_dump version 17.6 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET transaction_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Name: _realtime; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA _realtime; + + +-- +-- Name: auth; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA auth; + + +-- +-- Name: pg_cron; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_cron WITH SCHEMA pg_catalog; + + +-- +-- Name: EXTENSION pg_cron; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_cron IS 'Job scheduler for PostgreSQL'; + + +-- +-- Name: extensions; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA extensions; + + +-- +-- Name: graphql; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA graphql; + + +-- +-- Name: graphql_public; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA graphql_public; + + +-- +-- Name: pg_net; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_net WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION pg_net; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_net IS 'Async HTTP'; + + +-- +-- Name: pgbouncer; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA pgbouncer; + + +-- +-- Name: realtime; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA realtime; + + +-- +-- Name: storage; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA storage; + + +-- +-- Name: supabase_functions; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA supabase_functions; + + +-- +-- Name: vault; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA vault; + + +-- +-- Name: btree_gist; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS btree_gist WITH SCHEMA public; + + +-- +-- Name: EXTENSION btree_gist; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION btree_gist IS 'support for indexing common datatypes in GiST'; + + +-- +-- Name: citext; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public; + + +-- +-- Name: EXTENSION citext; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION citext IS 'data type for case-insensitive character strings'; + + +-- +-- Name: pg_graphql; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_graphql WITH SCHEMA graphql; + + +-- +-- Name: EXTENSION pg_graphql; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_graphql IS 'pg_graphql: GraphQL support'; + + +-- +-- Name: pg_stat_statements; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION pg_stat_statements; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_stat_statements IS 'track planning and execution statistics of all SQL statements executed'; + + +-- +-- Name: pg_trgm; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA public; + + +-- +-- Name: EXTENSION pg_trgm; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_trgm IS 'text similarity measurement and index searching based on trigrams'; + + +-- +-- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pgcrypto IS 'cryptographic functions'; + + +-- +-- Name: supabase_vault; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS supabase_vault WITH SCHEMA vault; + + +-- +-- Name: EXTENSION supabase_vault; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION supabase_vault IS 'Supabase Vault Extension'; + + +-- +-- Name: uuid-ossp; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION "uuid-ossp"; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)'; + + +-- +-- Name: aal_level; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.aal_level AS ENUM ( + 'aal1', + 'aal2', + 'aal3' +); + + +-- +-- Name: code_challenge_method; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.code_challenge_method AS ENUM ( + 's256', + 'plain' +); + + +-- +-- Name: factor_status; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.factor_status AS ENUM ( + 'unverified', + 'verified' +); + + +-- +-- Name: factor_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.factor_type AS ENUM ( + 'totp', + 'webauthn', + 'phone' +); + + +-- +-- Name: oauth_authorization_status; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_authorization_status AS ENUM ( + 'pending', + 'approved', + 'denied', + 'expired' +); + + +-- +-- Name: oauth_client_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_client_type AS ENUM ( + 'public', + 'confidential' +); + + +-- +-- Name: oauth_registration_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_registration_type AS ENUM ( + 'dynamic', + 'manual' +); + + +-- +-- Name: oauth_response_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_response_type AS ENUM ( + 'code' +); + + +-- +-- Name: one_time_token_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.one_time_token_type AS ENUM ( + 'confirmation_token', + 'reauthentication_token', + 'recovery_token', + 'email_change_token_new', + 'email_change_token_current', + 'phone_change_token' +); + + +-- +-- Name: commitment_log_source; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.commitment_log_source AS ENUM ( + 'manual', + 'auto' +); + + +-- +-- Name: determined_field_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.determined_field_type AS ENUM ( + 'text', + 'textarea', + 'number', + 'date', + 'select', + 'boolean' +); + + +-- +-- Name: financial_record_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.financial_record_type AS ENUM ( + 'receita', + 'despesa' +); + + +-- +-- Name: recurrence_exception_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.recurrence_exception_type AS ENUM ( + 'cancel_session', + 'reschedule_session', + 'patient_missed', + 'therapist_canceled', + 'holiday_block' +); + + +-- +-- Name: recurrence_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.recurrence_type AS ENUM ( + 'weekly', + 'biweekly', + 'monthly', + 'yearly', + 'custom_weekdays' +); + + +-- +-- Name: status_agenda_serie; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.status_agenda_serie AS ENUM ( + 'ativo', + 'pausado', + 'cancelado' +); + + +-- +-- Name: status_evento_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.status_evento_agenda AS ENUM ( + 'agendado', + 'realizado', + 'faltou', + 'cancelado', + 'remarcar' +); + + +-- +-- Name: status_excecao_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.status_excecao_agenda AS ENUM ( + 'pendente', + 'ativo', + 'arquivado' +); + + +-- +-- Name: tipo_evento_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.tipo_evento_agenda AS ENUM ( + 'sessao', + 'bloqueio' +); + + +-- +-- Name: tipo_excecao_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.tipo_excecao_agenda AS ENUM ( + 'bloqueio', + 'horario_extra' +); + + +-- +-- Name: action; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.action AS ENUM ( + 'INSERT', + 'UPDATE', + 'DELETE', + 'TRUNCATE', + 'ERROR' +); + + +-- +-- Name: equality_op; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.equality_op AS ENUM ( + 'eq', + 'neq', + 'lt', + 'lte', + 'gt', + 'gte', + 'in' +); + + +-- +-- Name: user_defined_filter; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.user_defined_filter AS ( + column_name text, + op realtime.equality_op, + value text +); + + +-- +-- Name: wal_column; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.wal_column AS ( + name text, + type_name text, + type_oid oid, + value jsonb, + is_pkey boolean, + is_selectable boolean +); + + +-- +-- Name: wal_rls; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.wal_rls AS ( + wal jsonb, + is_rls_enabled boolean, + subscription_ids uuid[], + errors text[] +); + + +-- +-- Name: buckettype; Type: TYPE; Schema: storage; Owner: - +-- + +CREATE TYPE storage.buckettype AS ENUM ( + 'STANDARD', + 'ANALYTICS', + 'VECTOR' +); + + +-- +-- Name: email(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.email() RETURNS text + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim.email', true), ''), + (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'email') + )::text +$$; + + +-- +-- Name: FUNCTION email(); Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON FUNCTION auth.email() IS 'Deprecated. Use auth.jwt() -> ''email'' instead.'; + + +-- +-- Name: jwt(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.jwt() RETURNS jsonb + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim', true), ''), + nullif(current_setting('request.jwt.claims', true), '') + )::jsonb +$$; + + +-- +-- Name: role(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.role() RETURNS text + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim.role', true), ''), + (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'role') + )::text +$$; + + +-- +-- Name: FUNCTION role(); Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON FUNCTION auth.role() IS 'Deprecated. Use auth.jwt() -> ''role'' instead.'; + + +-- +-- Name: uid(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.uid() RETURNS uuid + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim.sub', true), ''), + (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'sub') + )::uuid +$$; + + +-- +-- Name: FUNCTION uid(); Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON FUNCTION auth.uid() IS 'Deprecated. Use auth.jwt() -> ''sub'' instead.'; + + +-- +-- Name: grant_pg_cron_access(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.grant_pg_cron_access() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT + FROM pg_event_trigger_ddl_commands() AS ev + JOIN pg_extension AS ext + ON ev.objid = ext.oid + WHERE ext.extname = 'pg_cron' + ) + THEN + grant usage on schema cron to postgres with grant option; + + alter default privileges in schema cron grant all on tables to postgres with grant option; + alter default privileges in schema cron grant all on functions to postgres with grant option; + alter default privileges in schema cron grant all on sequences to postgres with grant option; + + alter default privileges for user supabase_admin in schema cron grant all + on sequences to postgres with grant option; + alter default privileges for user supabase_admin in schema cron grant all + on tables to postgres with grant option; + alter default privileges for user supabase_admin in schema cron grant all + on functions to postgres with grant option; + + grant all privileges on all tables in schema cron to postgres with grant option; + revoke all on table cron.job from postgres; + grant select on table cron.job to postgres with grant option; + END IF; +END; +$$; + + +-- +-- Name: FUNCTION grant_pg_cron_access(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.grant_pg_cron_access() IS 'Grants access to pg_cron'; + + +-- +-- Name: grant_pg_graphql_access(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.grant_pg_graphql_access() RETURNS event_trigger + LANGUAGE plpgsql + AS $_$ +DECLARE + func_is_graphql_resolve bool; +BEGIN + func_is_graphql_resolve = ( + SELECT n.proname = 'resolve' + FROM pg_event_trigger_ddl_commands() AS ev + LEFT JOIN pg_catalog.pg_proc AS n + ON ev.objid = n.oid + ); + + IF func_is_graphql_resolve + THEN + -- Update public wrapper to pass all arguments through to the pg_graphql resolve func + DROP FUNCTION IF EXISTS graphql_public.graphql; + create or replace function graphql_public.graphql( + "operationName" text default null, + query text default null, + variables jsonb default null, + extensions jsonb default null + ) + returns jsonb + language sql + as $$ + select graphql.resolve( + query := query, + variables := coalesce(variables, '{}'), + "operationName" := "operationName", + extensions := extensions + ); + $$; + + -- This hook executes when `graphql.resolve` is created. That is not necessarily the last + -- function in the extension so we need to grant permissions on existing entities AND + -- update default permissions to any others that are created after `graphql.resolve` + grant usage on schema graphql to postgres, anon, authenticated, service_role; + grant select on all tables in schema graphql to postgres, anon, authenticated, service_role; + grant execute on all functions in schema graphql to postgres, anon, authenticated, service_role; + grant all on all sequences in schema graphql to postgres, anon, authenticated, service_role; + alter default privileges in schema graphql grant all on tables to postgres, anon, authenticated, service_role; + alter default privileges in schema graphql grant all on functions to postgres, anon, authenticated, service_role; + alter default privileges in schema graphql grant all on sequences to postgres, anon, authenticated, service_role; + + -- Allow postgres role to allow granting usage on graphql and graphql_public schemas to custom roles + grant usage on schema graphql_public to postgres with grant option; + grant usage on schema graphql to postgres with grant option; + END IF; + +END; +$_$; + + +-- +-- Name: FUNCTION grant_pg_graphql_access(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.grant_pg_graphql_access() IS 'Grants access to pg_graphql'; + + +-- +-- Name: grant_pg_net_access(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.grant_pg_net_access() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_event_trigger_ddl_commands() AS ev + JOIN pg_extension AS ext + ON ev.objid = ext.oid + WHERE ext.extname = 'pg_net' + ) + THEN + GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role; + + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + + REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + + GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + END IF; +END; +$$; + + +-- +-- Name: FUNCTION grant_pg_net_access(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.grant_pg_net_access() IS 'Grants access to pg_net'; + + +-- +-- Name: pgrst_ddl_watch(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.pgrst_ddl_watch() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +DECLARE + cmd record; +BEGIN + FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands() + LOOP + IF cmd.command_tag IN ( + 'CREATE SCHEMA', 'ALTER SCHEMA' + , 'CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO', 'ALTER TABLE' + , 'CREATE FOREIGN TABLE', 'ALTER FOREIGN TABLE' + , 'CREATE VIEW', 'ALTER VIEW' + , 'CREATE MATERIALIZED VIEW', 'ALTER MATERIALIZED VIEW' + , 'CREATE FUNCTION', 'ALTER FUNCTION' + , 'CREATE TRIGGER' + , 'CREATE TYPE', 'ALTER TYPE' + , 'CREATE RULE' + , 'COMMENT' + ) + -- don't notify in case of CREATE TEMP table or other objects created on pg_temp + AND cmd.schema_name is distinct from 'pg_temp' + THEN + NOTIFY pgrst, 'reload schema'; + END IF; + END LOOP; +END; $$; + + +-- +-- Name: pgrst_drop_watch(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.pgrst_drop_watch() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +DECLARE + obj record; +BEGIN + FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() + LOOP + IF obj.object_type IN ( + 'schema' + , 'table' + , 'foreign table' + , 'view' + , 'materialized view' + , 'function' + , 'trigger' + , 'type' + , 'rule' + ) + AND obj.is_temporary IS false -- no pg_temp objects + THEN + NOTIFY pgrst, 'reload schema'; + END IF; + END LOOP; +END; $$; + + +-- +-- Name: set_graphql_placeholder(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.set_graphql_placeholder() RETURNS event_trigger + LANGUAGE plpgsql + AS $_$ + DECLARE + graphql_is_dropped bool; + BEGIN + graphql_is_dropped = ( + SELECT ev.schema_name = 'graphql_public' + FROM pg_event_trigger_dropped_objects() AS ev + WHERE ev.schema_name = 'graphql_public' + ); + + IF graphql_is_dropped + THEN + create or replace function graphql_public.graphql( + "operationName" text default null, + query text default null, + variables jsonb default null, + extensions jsonb default null + ) + returns jsonb + language plpgsql + as $$ + DECLARE + server_version float; + BEGIN + server_version = (SELECT (SPLIT_PART((select version()), ' ', 2))::float); + + IF server_version >= 14 THEN + RETURN jsonb_build_object( + 'errors', jsonb_build_array( + jsonb_build_object( + 'message', 'pg_graphql extension is not enabled.' + ) + ) + ); + ELSE + RETURN jsonb_build_object( + 'errors', jsonb_build_array( + jsonb_build_object( + 'message', 'pg_graphql is only available on projects running Postgres 14 onwards.' + ) + ) + ); + END IF; + END; + $$; + END IF; + + END; +$_$; + + +-- +-- Name: FUNCTION set_graphql_placeholder(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.set_graphql_placeholder() IS 'Reintroduces placeholder function for graphql_public.graphql'; + + +-- +-- Name: get_auth(text); Type: FUNCTION; Schema: pgbouncer; Owner: - +-- + +CREATE FUNCTION pgbouncer.get_auth(p_usename text) RETURNS TABLE(username text, password text) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $_$ +begin + raise debug 'PgBouncer auth request: %', p_usename; + + return query + select + rolname::text, + case when rolvaliduntil < now() + then null + else rolpassword::text + end + from pg_authid + where rolname=$1 and rolcanlogin; +end; +$_$; + + +-- +-- Name: __rls_ping(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.__rls_ping() RETURNS text + LANGUAGE sql STABLE + AS $$ + select 'ok'::text; +$$; + + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: subscriptions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscriptions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid, + plan_id uuid NOT NULL, + status text DEFAULT 'active'::text NOT NULL, + current_period_start timestamp with time zone, + current_period_end timestamp with time zone, + cancel_at_period_end boolean DEFAULT false NOT NULL, + provider text DEFAULT 'manual'::text NOT NULL, + provider_customer_id text, + provider_subscription_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid, + plan_key text, + "interval" text, + source text DEFAULT 'manual'::text NOT NULL, + started_at timestamp with time zone DEFAULT now() NOT NULL, + canceled_at timestamp with time zone, + activated_at timestamp with time zone, + past_due_since timestamp with time zone, + suspended_at timestamp with time zone, + suspended_reason text, + cancelled_at timestamp with time zone, + cancel_reason text, + expired_at timestamp with time zone, + CONSTRAINT subscriptions_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text]))), + CONSTRAINT subscriptions_owner_xor CHECK ((((tenant_id IS NOT NULL) AND (user_id IS NULL)) OR ((tenant_id IS NULL) AND (user_id IS NOT NULL)))), + CONSTRAINT subscriptions_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'active'::text, 'past_due'::text, 'suspended'::text, 'cancelled'::text, 'expired'::text]))) +); + + +-- +-- Name: activate_subscription_from_intent(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_intent record; + v_sub public.subscriptions; + v_days int; + v_user_id uuid; + v_plan_id uuid; + v_target text; +begin + -- l?? pela VIEW unificada + select * into v_intent + from public.subscription_intents + where id = p_intent_id; + + if not found then + raise exception 'Intent n??o encontrado: %', p_intent_id; + end if; + + if v_intent.status <> 'paid' then + raise exception 'Intent precisa estar paid para ativar assinatura'; + end if; + + -- resolve target e plan_id via plans.key + select p.id, p.target + into v_plan_id, v_target + from public.plans p + where p.key = v_intent.plan_key + limit 1; + + if v_plan_id is null then + raise exception 'Plano n??o encontrado em plans.key = %', v_intent.plan_key; + end if; + + v_target := lower(coalesce(v_target, '')); + + -- ??? supervisor adicionado + if v_target not in ('clinic', 'therapist', 'supervisor') then + raise exception 'Target inv??lido em plans.target: %', v_target; + end if; + + -- regra por target + if v_target = 'clinic' then + if v_intent.tenant_id is null then + raise exception 'Intent sem tenant_id'; + end if; + else + -- therapist ou supervisor: vinculado ao user + v_user_id := v_intent.user_id; + if v_user_id is null then + v_user_id := v_intent.created_by_user_id; + end if; + end if; + + if v_target in ('therapist', 'supervisor') and v_user_id is null then + raise exception 'N??o foi poss??vel determinar user_id para assinatura %.', v_target; + end if; + + -- cancela assinatura ativa anterior + if v_target = 'clinic' then + update public.subscriptions + set status = 'cancelled', + cancelled_at = now() + where tenant_id = v_intent.tenant_id + and plan_id = v_plan_id + and status = 'active'; + else + -- therapist ou supervisor + update public.subscriptions + set status = 'cancelled', + cancelled_at = now() + where user_id = v_user_id + and plan_id = v_plan_id + and status = 'active' + and tenant_id is null; + end if; + + -- dura????o do plano (30 dias para mensal) + v_days := case + when lower(coalesce(v_intent.interval, 'month')) = 'year' then 365 + else 30 + end; + + -- cria nova assinatura + insert into public.subscriptions ( + user_id, + plan_id, + status, + started_at, + expires_at, + cancelled_at, + activated_at, + tenant_id, + plan_key, + interval, + source, + created_at, + updated_at + ) + values ( + case when v_target = 'clinic' then null else v_user_id end, + v_plan_id, + 'active', + now(), + now() + make_interval(days => v_days), + null, + now(), + case when v_target = 'clinic' then v_intent.tenant_id else null end, + v_intent.plan_key, + v_intent.interval, + 'manual', + now(), + now() + ) + returning * into v_sub; + + -- grava v??nculo intent ??? subscription + if v_target = 'clinic' then + update public.subscription_intents_tenant + set subscription_id = v_sub.id + where id = p_intent_id; + else + update public.subscription_intents_personal + set subscription_id = v_sub.id + where id = p_intent_id; + end if; + + return v_sub; +end; +$$; + + +-- +-- Name: admin_credit_addon(uuid, text, integer, uuid, text, text, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Cr??dito manual'::text, p_payment_method text DEFAULT 'manual'::text, p_price_cents integer DEFAULT 0) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_credit addon_credits%ROWTYPE; + v_balance_before INTEGER; + v_balance_after INTEGER; + v_tx_id UUID; +BEGIN + -- Upsert addon_credits + INSERT INTO addon_credits (tenant_id, addon_type, balance, total_purchased) + VALUES (p_tenant_id, p_addon_type, 0, 0) + ON CONFLICT (tenant_id, addon_type) DO NOTHING; + + -- Lock e leitura + SELECT * INTO v_credit + FROM addon_credits + WHERE tenant_id = p_tenant_id AND addon_type = p_addon_type + FOR UPDATE; + + v_balance_before := v_credit.balance; + v_balance_after := v_credit.balance + p_amount; + + -- Atualiza saldo + UPDATE addon_credits + SET balance = v_balance_after, + total_purchased = total_purchased + p_amount, + low_balance_notified = CASE WHEN v_balance_after > COALESCE(low_balance_threshold, 10) THEN false ELSE low_balance_notified END, + updated_at = now() + WHERE id = v_credit.id; + + -- Registra transa????o + INSERT INTO addon_transactions ( + tenant_id, addon_type, type, amount, + balance_before, balance_after, + product_id, description, + admin_user_id, payment_method, price_cents + ) VALUES ( + p_tenant_id, p_addon_type, 'purchase', p_amount, + v_balance_before, v_balance_after, + p_product_id, p_description, + auth.uid(), p_payment_method, p_price_cents + ) + RETURNING id INTO v_tx_id; + + RETURN jsonb_build_object( + 'success', true, + 'transaction_id', v_tx_id, + 'balance_before', v_balance_before, + 'balance_after', v_balance_after + ); +END; +$$; + + +-- +-- Name: FUNCTION admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) IS 'Admin adiciona cr??ditos de add-on a um tenant. Cria registro se n??o existir.'; + + +-- +-- Name: admin_delete_email_template_global(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_delete_email_template_global(p_id uuid) RETURNS boolean + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + DELETE FROM public.email_templates_global WHERE id = p_id; + IF NOT FOUND THEN + RAISE EXCEPTION 'Template com id % n??o encontrado', p_id; + END IF; + RETURN true; +END; +$$; + + +-- +-- Name: admin_fix_plan_target(text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_plan_id uuid; +begin + -- (opcional) restringe targets v??lidos + if p_new_target not in ('clinic','therapist') then + raise exception 'Target inv??lido: %', p_new_target using errcode='P0001'; + end if; + + -- trava o plano + select id into v_plan_id + from public.plans + where key = p_plan_key + for update; + + if v_plan_id is null then + raise exception 'Plano n??o encontrado: %', p_plan_key using errcode='P0001'; + end if; + + -- seguran??a: n??o mexer se existe subscription + if exists (select 1 from public.subscriptions s where s.plan_id = v_plan_id) then + raise exception 'Plano % possui subscriptions. Migra????o bloqueada.', p_plan_key using errcode='P0001'; + end if; + + -- liga bypass SOMENTE nesta transa????o + perform set_config('app.plan_migration_bypass', '1', true); + + update public.plans + set target = p_new_target + where id = v_plan_id; + +end +$$; + + +-- +-- Name: admin_upsert_email_template_global(uuid, text, text, text, text, text, text, boolean, jsonb); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_upsert_email_template_global(p_id uuid DEFAULT NULL::uuid, p_key text DEFAULT NULL::text, p_domain text DEFAULT NULL::text, p_channel text DEFAULT 'email'::text, p_subject text DEFAULT NULL::text, p_body_html text DEFAULT NULL::text, p_body_text text DEFAULT NULL::text, p_is_active boolean DEFAULT true, p_variables jsonb DEFAULT '{}'::jsonb) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_result jsonb; + v_id uuid; +BEGIN + -- UPDATE existente + IF p_id IS NOT NULL THEN + UPDATE public.email_templates_global + SET + subject = COALESCE(p_subject, subject), + body_html = COALESCE(p_body_html, body_html), + body_text = p_body_text, + is_active = p_is_active, + variables = COALESCE(p_variables, variables), + version = version + 1 + WHERE id = p_id + RETURNING to_jsonb(email_templates_global.*) INTO v_result; + + IF v_result IS NULL THEN + RAISE EXCEPTION 'Template com id % n??o encontrado', p_id; + END IF; + + RETURN v_result; + END IF; + + -- INSERT novo + IF p_key IS NULL OR p_domain IS NULL OR p_subject IS NULL OR p_body_html IS NULL THEN + RAISE EXCEPTION 'key, domain, subject e body_html s??o obrigat??rios para novo template'; + END IF; + + INSERT INTO public.email_templates_global (key, domain, channel, subject, body_html, body_text, is_active, variables) + VALUES (p_key, p_domain, p_channel, p_subject, p_body_html, p_body_text, p_is_active, p_variables) + RETURNING to_jsonb(email_templates_global.*) INTO v_result; + + RETURN v_result; +END; +$$; + + +-- +-- Name: agenda_cfg_sync(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agenda_cfg_sync() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if new.agenda_view_mode = 'custom' then + new.usar_horario_admin_custom := true; + new.admin_inicio_visualizacao := new.agenda_custom_start; + new.admin_fim_visualizacao := new.agenda_custom_end; + else + new.usar_horario_admin_custom := false; + end if; + + return new; +end; +$$; + + +-- +-- Name: agendador_dias_disponiveis(text, integer, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer) RETURNS TABLE(data date, tem_slots boolean) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_owner_id uuid; + v_antecedencia int; + v_agora timestamptz; + v_data date; + v_data_inicio date; + v_data_fim date; + v_db_dow int; + v_tem_slot boolean; + v_bloqueado boolean; +BEGIN + SELECT c.owner_id, c.antecedencia_minima_horas + INTO v_owner_id, v_antecedencia + FROM public.agendador_configuracoes c + WHERE c.link_slug = p_slug AND c.ativo = true + LIMIT 1; + + IF v_owner_id IS NULL THEN RETURN; END IF; + + v_agora := now(); + v_data_inicio := make_date(p_ano, p_mes, 1); + v_data_fim := (v_data_inicio + interval '1 month' - interval '1 day')::date; + + v_data := v_data_inicio; + WHILE v_data <= v_data_fim LOOP + v_db_dow := extract(dow from v_data::timestamp)::int; + + -- ?????? Dia inteiro bloqueado? (agenda_bloqueios) ??????????????????????????????????????????????????????????????????????????? + SELECT EXISTS ( + SELECT 1 FROM public.agenda_bloqueios b + WHERE b.owner_id = v_owner_id + AND b.data_inicio <= v_data + AND COALESCE(b.data_fim, v_data) >= v_data + AND b.hora_inicio IS NULL -- bloqueio de dia inteiro + AND ( + (NOT b.recorrente) + OR (b.recorrente AND b.dia_semana = v_db_dow) + ) + ) INTO v_bloqueado; + + IF v_bloqueado THEN + v_data := v_data + 1; + CONTINUE; + END IF; + + -- ?????? Tem slots dispon??veis no dia? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????? + SELECT EXISTS ( + SELECT 1 FROM public.agenda_online_slots s + WHERE s.owner_id = v_owner_id + AND s.weekday = v_db_dow + AND s.enabled = true + AND (v_data::text || ' ' || s.time::text)::timestamp + AT TIME ZONE 'America/Sao_Paulo' + >= v_agora + (v_antecedencia || ' hours')::interval + ) INTO v_tem_slot; + + IF v_tem_slot THEN + data := v_data; + tem_slots := true; + RETURN NEXT; + END IF; + + v_data := v_data + 1; + END LOOP; +END; +$$; + + +-- +-- Name: agendador_gerar_slug(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agendador_gerar_slug() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_slug text; + v_exists boolean; +BEGIN + -- s?? gera se ativou e n??o tem slug ainda + IF NEW.ativo = true AND (NEW.link_slug IS NULL OR NEW.link_slug = '') THEN + LOOP + v_slug := lower(substring(replace(gen_random_uuid()::text, '-', ''), 1, 8)); + SELECT EXISTS ( + SELECT 1 FROM public.agendador_configuracoes + WHERE link_slug = v_slug AND owner_id <> NEW.owner_id + ) INTO v_exists; + EXIT WHEN NOT v_exists; + END LOOP; + NEW.link_slug := v_slug; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: agendador_slots_disponiveis(text, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) RETURNS TABLE(hora time without time zone, disponivel boolean) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_owner_id uuid; + v_duracao int; + v_antecedencia int; + v_agora timestamptz; + v_db_dow int; + v_slot time; + v_slot_fim time; + v_slot_ts timestamptz; + v_ocupado boolean; + -- loop de recorr??ncias + v_rule RECORD; + v_rule_start_dow int; + v_first_occ date; + v_day_diff int; + v_ex_type text; +BEGIN + SELECT c.owner_id, c.duracao_sessao_min, c.antecedencia_minima_horas + INTO v_owner_id, v_duracao, v_antecedencia + FROM public.agendador_configuracoes c + WHERE c.link_slug = p_slug AND c.ativo = true + LIMIT 1; + + IF v_owner_id IS NULL THEN RETURN; END IF; + + v_agora := now(); + v_db_dow := extract(dow from p_data::timestamp)::int; + + -- ?????? Dia inteiro bloqueado? (agenda_bloqueios sem hora) ????????????????????????????????????????????????????????? + -- Se sim, n??o h?? nenhum slot dispon??vel ??? retorna vazio. + IF EXISTS ( + SELECT 1 FROM public.agenda_bloqueios b + WHERE b.owner_id = v_owner_id + AND b.data_inicio <= p_data + AND COALESCE(b.data_fim, p_data) >= p_data + AND b.hora_inicio IS NULL -- bloqueio de dia inteiro + AND ( + (NOT b.recorrente) + OR (b.recorrente AND b.dia_semana = v_db_dow) + ) + ) THEN + RETURN; + END IF; + + FOR v_slot IN + SELECT s.time + FROM public.agenda_online_slots s + WHERE s.owner_id = v_owner_id + AND s.weekday = v_db_dow + AND s.enabled = true + ORDER BY s.time + LOOP + v_slot_fim := v_slot + (v_duracao || ' minutes')::interval; + v_ocupado := false; + + -- ?????? Anteced??ncia m??nima ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + v_slot_ts := (p_data::text || ' ' || v_slot::text)::timestamp + AT TIME ZONE 'America/Sao_Paulo'; + IF v_slot_ts < v_agora + (v_antecedencia || ' hours')::interval THEN + v_ocupado := true; + END IF; + + -- ?????? Bloqueio de hor??rio espec??fico (agenda_bloqueios com hora) ????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 FROM public.agenda_bloqueios b + WHERE b.owner_id = v_owner_id + AND b.data_inicio <= p_data + AND COALESCE(b.data_fim, p_data) >= p_data + AND b.hora_inicio IS NOT NULL + AND b.hora_inicio < v_slot_fim + AND b.hora_fim > v_slot + AND ( + (NOT b.recorrente) + OR (b.recorrente AND b.dia_semana = v_db_dow) + ) + ) INTO v_ocupado; + END IF; + + -- ?????? Eventos avulsos internos (agenda_eventos) ???????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 FROM public.agenda_eventos e + WHERE e.owner_id = v_owner_id + AND e.status::text NOT IN ('cancelado', 'faltou') + AND (e.inicio_em AT TIME ZONE 'America/Sao_Paulo')::date = p_data + AND (e.inicio_em AT TIME ZONE 'America/Sao_Paulo')::time < v_slot_fim + AND (e.fim_em AT TIME ZONE 'America/Sao_Paulo')::time > v_slot + ) INTO v_ocupado; + END IF; + + -- ?????? Recorr??ncias ativas (recurrence_rules) ????????????????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + FOR v_rule IN + SELECT + r.id, + r.start_date::date AS start_date, + r.end_date::date AS end_date, + r.start_time::time AS start_time, + r.end_time::time AS end_time, + COALESCE(r.interval, 1)::int AS interval + FROM public.recurrence_rules r + WHERE r.owner_id = v_owner_id + AND r.status = 'ativo' + AND p_data >= r.start_date::date + AND (r.end_date IS NULL OR p_data <= r.end_date::date) + AND v_db_dow = ANY(r.weekdays) + AND r.start_time::time < v_slot_fim + AND r.end_time::time > v_slot + LOOP + v_rule_start_dow := extract(dow from v_rule.start_date)::int; + v_first_occ := v_rule.start_date + + (((v_db_dow - v_rule_start_dow + 7) % 7))::int; + v_day_diff := (p_data - v_first_occ)::int; + + IF v_day_diff >= 0 AND v_day_diff % (7 * v_rule.interval) = 0 THEN + v_ex_type := NULL; + SELECT ex.type INTO v_ex_type + FROM public.recurrence_exceptions ex + WHERE ex.recurrence_id = v_rule.id + AND ex.original_date = p_data + LIMIT 1; + + IF v_ex_type IS NULL OR v_ex_type NOT IN ( + 'cancel_session', 'patient_missed', + 'therapist_canceled', 'holiday_block', + 'reschedule_session' + ) THEN + v_ocupado := true; + EXIT; + END IF; + END IF; + END LOOP; + END IF; + + -- ?????? Recorr??ncias remarcadas para este dia ???????????????????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 + FROM public.recurrence_exceptions ex + JOIN public.recurrence_rules r ON r.id = ex.recurrence_id + WHERE r.owner_id = v_owner_id + AND r.status = 'ativo' + AND ex.type = 'reschedule_session' + AND ex.new_date = p_data + AND COALESCE(ex.new_start_time, r.start_time)::time < v_slot_fim + AND COALESCE(ex.new_end_time, r.end_time)::time > v_slot + ) INTO v_ocupado; + END IF; + + -- ?????? Solicita????es p??blicas pendentes ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 FROM public.agendador_solicitacoes sol + WHERE sol.owner_id = v_owner_id + AND sol.status = 'pendente' + AND sol.data_solicitada = p_data + AND sol.hora_solicitada = v_slot + AND (sol.reservado_ate IS NULL OR sol.reservado_ate > v_agora) + ) INTO v_ocupado; + END IF; + + hora := v_slot; + disponivel := NOT v_ocupado; + RETURN NEXT; + END LOOP; +END; +$$; + + +-- +-- Name: auto_create_financial_record_from_session(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.auto_create_financial_record_from_session() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_price NUMERIC(10,2); + v_services_total NUMERIC(10,2); + v_already_billed BOOLEAN; +BEGIN + -- ?????? Guards de sa??da r??pida ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + + -- S?? processa quando o status muda PARA 'realizado' + IF NEW.status::TEXT <> 'realizado' THEN + RETURN NEW; + END IF; + + -- S?? processa quando houve mudan??a real de status + IF OLD.status IS NOT DISTINCT FROM NEW.status THEN + RETURN NEW; + END IF; + + -- S?? sess??es (n??o bloqueios, feriados, etc.) + IF NEW.tipo::TEXT <> 'sessao' THEN + RETURN NEW; + END IF; + + -- Paciente obrigat??rio para vincular a cobran??a + IF NEW.patient_id IS NULL THEN + RETURN NEW; + END IF; + + -- Sess??es de pacote t??m cobran??a gerenciada por billing_contract + IF NEW.billing_contract_id IS NOT NULL THEN + RETURN NEW; + END IF; + + -- Idempot??ncia: j?? existe financial_record para este evento? + SELECT billed INTO v_already_billed + FROM public.agenda_eventos + WHERE id = NEW.id; + + IF v_already_billed = TRUE THEN + -- Confirma no financial_records tamb??m (dupla verifica????o) + IF EXISTS ( + SELECT 1 FROM public.financial_records + WHERE agenda_evento_id = NEW.id AND deleted_at IS NULL + ) THEN + RETURN NEW; + END IF; + END IF; + + -- ?????? Busca do pre??o ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + + v_price := NULL; + + -- Prioridade 1: soma dos servi??os da regra de recorr??ncia + IF NEW.recurrence_id IS NOT NULL THEN + SELECT COALESCE(SUM(rrs.final_price), 0) + INTO v_services_total + FROM public.recurrence_rule_services rrs + WHERE rrs.rule_id = NEW.recurrence_id; + + IF v_services_total > 0 THEN + v_price := v_services_total; + END IF; + + -- Prioridade 2: price direto da regra (fallback se sem servi??os) + IF v_price IS NULL OR v_price = 0 THEN + SELECT price INTO v_price + FROM public.recurrence_rules + WHERE id = NEW.recurrence_id; + END IF; + END IF; + + -- Prioridade 3: price do pr??prio evento de agenda + IF v_price IS NULL OR v_price = 0 THEN + v_price := NEW.price; + END IF; + + -- Sem pre??o ??? n??o criar registro (n??o ?? erro, apenas skip silencioso) + IF v_price IS NULL OR v_price <= 0 THEN + RETURN NEW; + END IF; + + -- ?????? Cria????o do financial_record ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + + INSERT INTO public.financial_records ( + owner_id, + tenant_id, + patient_id, + agenda_evento_id, + type, + amount, + discount_amount, + final_amount, + clinic_fee_pct, + clinic_fee_amount, + status, + due_date + -- payment_method: NULL at?? o momento do pagamento (mark_as_paid preenche) + ) VALUES ( + NEW.owner_id, + NEW.tenant_id, + NEW.patient_id, + NEW.id, + 'receita', + v_price, + 0, + v_price, + 0, -- clinic_fee_pct: sem campo de configura????o global no schema atual. + 0, -- clinic_fee_amount: calculado manualmente ou via update posterior. + 'pending', + (NEW.inicio_em::DATE + 7) -- vencimento padr??o: 7 dias ap??s a sess??o + ); + + -- ?????? Marca sess??o como billed ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + -- UPDATE em billed (n??o em status) ??? n??o re-dispara este trigger + UPDATE public.agenda_eventos + SET billed = TRUE + WHERE id = NEW.id; + + RETURN NEW; + +EXCEPTION + WHEN OTHERS THEN + -- Log silencioso: nunca bloquear a agenda por falha financeira + RAISE WARNING '[auto_create_financial_record_from_session] evento=% erro=%', + NEW.id, SQLERRM; + RETURN NEW; +END; +$$; + + +-- +-- Name: FUNCTION auto_create_financial_record_from_session(); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.auto_create_financial_record_from_session() IS 'Trigger que cria automaticamente um financial_record (receita, pending) quando uma sess??o de agenda ?? marcada como realizada. Prioridade de pre??o: recurrence_rule_services > recurrence_rules.price > agenda_eventos.price. Skip silencioso se sem pre??o, pacote ou registro j?? existente.'; + + +-- +-- Name: can_delete_patient(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.can_delete_patient(p_patient_id uuid) RETURNS boolean + LANGUAGE sql STABLE SECURITY DEFINER + AS $$ + SELECT NOT EXISTS ( + SELECT 1 FROM public.agenda_eventos WHERE patient_id = p_patient_id + UNION ALL + SELECT 1 FROM public.recurrence_rules WHERE patient_id = p_patient_id + UNION ALL + SELECT 1 FROM public.billing_contracts WHERE patient_id = p_patient_id + ); +$$; + + +-- +-- Name: cancel_notifications_on_opt_out(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_notifications_on_opt_out() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + -- WhatsApp opt-out + IF OLD.whatsapp_opt_in = true AND NEW.whatsapp_opt_in = false THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, 'whatsapp' + ); + END IF; + -- Email opt-out + IF OLD.email_opt_in = true AND NEW.email_opt_in = false THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, 'email' + ); + END IF; + -- SMS opt-out + IF OLD.sms_opt_in = true AND NEW.sms_opt_in = false THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, 'sms' + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: cancel_notifications_on_session_cancel(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_notifications_on_session_cancel() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF NEW.status IN ('cancelado', 'excluido') + AND OLD.status NOT IN ('cancelado', 'excluido') + THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, NULL, NEW.id + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: cancel_patient_pending_notifications(uuid, text, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text DEFAULT NULL::text, p_evento_id uuid DEFAULT NULL::uuid) RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_canceled integer; +BEGIN + UPDATE public.notification_queue + SET status = 'cancelado', + updated_at = now() + WHERE patient_id = p_patient_id + AND status IN ('pendente', 'processando') + AND (p_channel IS NULL OR channel = p_channel) + AND (p_evento_id IS NULL OR agenda_evento_id = p_evento_id); + + GET DIAGNOSTICS v_canceled = ROW_COUNT; + RETURN v_canceled; +END; +$$; + + +-- +-- Name: cancel_recurrence_from(uuid, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + UPDATE public.recurrence_rules + SET + end_date = p_from_date - INTERVAL '1 day', + open_ended = false, + status = CASE + WHEN p_from_date <= start_date THEN 'cancelado' + ELSE status + END, + updated_at = now() + WHERE id = p_recurrence_id; +END; +$$; + + +-- +-- Name: cancel_subscription(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_subscription(p_subscription_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_owner_type text; + v_owner_ref uuid; +begin + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + if v_sub.status = 'canceled' then + return v_sub; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set status = 'canceled', + cancel_at_period_end = false, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'canceled', + v_sub.plan_id, + v_sub.plan_id, + auth.uid(), + 'Cancelamento manual via admin', + 'admin_panel', + jsonb_build_object('previous_status', 'active') + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation(owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; + +end; +$$; + + +-- +-- Name: cancelar_eventos_serie(uuid, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone DEFAULT now()) RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_count integer; +BEGIN + UPDATE public.agenda_eventos + SET status = 'cancelado', + updated_at = now() + WHERE serie_id = p_serie_id + AND inicio_em >= p_a_partir_de + AND status NOT IN ('realizado', 'cancelado'); + + GET DIAGNOSTICS v_count = ROW_COUNT; + RETURN v_count; +END; +$$; + + +-- +-- Name: FUNCTION cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone) IS 'Cancela todos os eventos futuros de uma s??rie a partir de p_a_partir_de (inclusive). + N??o cancela eventos j?? realizados.'; + + +-- +-- Name: change_subscription_plan(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_old_plan uuid; + v_new_key text; + + v_owner_type text; + v_owner_ref uuid; + + v_new_target text; + v_sub_target text; +begin + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + v_old_plan := v_sub.plan_id; + + if v_old_plan = p_new_plan_id then + return v_sub; + end if; + + select key, target + into v_new_key, v_new_target + from public.plans + where id = p_new_plan_id; + + if v_new_key is null then + raise exception 'Plano n??o encontrado'; + end if; + + v_new_target := lower(coalesce(v_new_target, '')); + + v_sub_target := case + when v_sub.tenant_id is not null then 'clinic' + else 'therapist' + end; + + if v_new_target <> v_sub_target then + raise exception 'Plano inv??lido para este tipo de assinatura. Assinatura ?? % e o plano ?? %.', + v_sub_target, v_new_target + using errcode = 'P0001'; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set plan_id = p_new_plan_id, + plan_key = v_new_key, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'plan_changed', + v_old_plan, + p_new_plan_id, + auth.uid(), + 'Plan change via DEV menu', + 'dev_menu', + jsonb_build_object( + 'previous_plan', v_old_plan, + 'new_plan', p_new_plan_id, + 'new_plan_key', v_new_key, + 'new_plan_target', v_new_target + ) + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation (owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; +end; +$$; + + +-- +-- Name: cleanup_notification_queue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cleanup_notification_queue() RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_deleted integer; +BEGIN + DELETE FROM public.notification_queue + WHERE status IN ('enviado', 'cancelado', 'ignorado') + AND created_at < now() - interval '90 days'; + + GET DIAGNOSTICS v_deleted = ROW_COUNT; + RETURN v_deleted; +END; +$$; + + +-- +-- Name: create_clinic_tenant(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_clinic_tenant(p_name text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_uid uuid; + v_tenant uuid; + v_name text; +begin + v_uid := auth.uid(); + if v_uid is null then + raise exception 'Not authenticated'; + end if; + + v_name := nullif(trim(coalesce(p_name, '')), ''); + if v_name is null then + v_name := 'Cl??nica'; + end if; + + insert into public.tenants (name, kind, created_at) + values (v_name, 'clinic', now()) + returning id into v_tenant; + + insert into public.tenant_members (tenant_id, user_id, role, status, created_at) + values (v_tenant, v_uid, 'tenant_admin', 'active', now()); + + return v_tenant; +end; +$$; + + +-- +-- Name: financial_records; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.financial_records ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + type public.financial_record_type DEFAULT 'receita'::public.financial_record_type NOT NULL, + amount numeric(10,2) NOT NULL, + description text, + category text, + payment_method text, + paid_at timestamp with time zone, + due_date date, + installments smallint DEFAULT 1, + installment_number smallint DEFAULT 1, + installment_group uuid, + agenda_evento_id uuid, + patient_id uuid, + clinic_fee_pct numeric(5,2) DEFAULT 0, + clinic_fee_amount numeric(10,2) DEFAULT 0, + net_amount numeric(10,2) GENERATED ALWAYS AS ((amount - clinic_fee_amount)) STORED, + insurance_plan_id uuid, + notes text, + tags text[], + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + discount_amount numeric(10,2) DEFAULT 0 NOT NULL, + final_amount numeric(10,2) DEFAULT 0 NOT NULL, + status text DEFAULT 'pending'::text NOT NULL, + category_id uuid, + CONSTRAINT financial_records_amount_check CHECK ((amount >= (0)::numeric)), + CONSTRAINT financial_records_clinic_fee_amount_check CHECK ((clinic_fee_amount >= (0)::numeric)), + CONSTRAINT financial_records_clinic_fee_pct_check CHECK (((clinic_fee_pct >= (0)::numeric) AND (clinic_fee_pct <= (100)::numeric))), + CONSTRAINT financial_records_discount_amount_check CHECK ((discount_amount >= (0)::numeric)), + CONSTRAINT financial_records_final_amount_check CHECK ((final_amount >= (0)::numeric)), + CONSTRAINT financial_records_installments_check CHECK ((installments >= 1)), + CONSTRAINT financial_records_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'paid'::text, 'partial'::text, 'overdue'::text, 'cancelled'::text, 'refunded'::text]))) +); + + +-- +-- Name: create_financial_record_for_session(uuid, uuid, uuid, uuid, numeric, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_financial_record_for_session(p_tenant_id uuid, p_owner_id uuid, p_patient_id uuid, p_agenda_evento_id uuid, p_amount numeric, p_due_date date) RETURNS SETOF public.financial_records + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_existing public.financial_records%ROWTYPE; + v_new public.financial_records%ROWTYPE; +BEGIN + -- Idempot??ncia: retorna o registro existente se j?? foi criado + SELECT * INTO v_existing + FROM public.financial_records + WHERE agenda_evento_id = p_agenda_evento_id + AND deleted_at IS NULL + LIMIT 1; + + IF FOUND THEN + RETURN NEXT v_existing; + RETURN; + END IF; + + -- Cria o novo registro + INSERT INTO public.financial_records ( + tenant_id, + owner_id, + patient_id, + agenda_evento_id, + amount, + discount_amount, + final_amount, + status, + due_date + ) VALUES ( + p_tenant_id, + p_owner_id, + p_patient_id, + p_agenda_evento_id, + p_amount, + 0, + p_amount, + 'pending', + p_due_date + ) + RETURNING * INTO v_new; + + -- Marca o evento da agenda como billed = true + UPDATE public.agenda_eventos + SET billed = TRUE + WHERE id = p_agenda_evento_id; + + RETURN NEXT v_new; +END; +$$; + + +-- +-- Name: create_patient_intake_request(text, text, text, text, text, boolean); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text DEFAULT NULL::text, p_phone text DEFAULT NULL::text, p_notes text DEFAULT NULL::text, p_consent boolean DEFAULT false) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_owner uuid; + v_active boolean; + v_expires timestamptz; + v_max_uses int; + v_uses int; + v_id uuid; +begin + select owner_id, active, expires_at, max_uses, uses + into v_owner, v_active, v_expires, v_max_uses, v_uses + from public.patient_invites + where token = p_token + limit 1; + + if v_owner is null then + raise exception 'Token inv??lido'; + end if; + + if v_active is not true then + raise exception 'Link desativado'; + end if; + + if v_expires is not null and now() > v_expires then + raise exception 'Link expirado'; + end if; + + if v_max_uses is not null and v_uses >= v_max_uses then + raise exception 'Limite de uso atingido'; + end if; + + if p_name is null or length(trim(p_name)) = 0 then + raise exception 'Nome ?? obrigat??rio'; + end if; + + insert into public.patient_intake_requests + (owner_id, token, name, email, phone, notes, consent, status) + values + (v_owner, p_token, trim(p_name), + nullif(lower(trim(p_email)), ''), + nullif(trim(p_phone), ''), + nullif(trim(p_notes), ''), + coalesce(p_consent, false), + 'new') + returning id into v_id; + + update public.patient_invites + set uses = uses + 1 + where token = p_token; + + return v_id; +end; +$$; + + +-- +-- Name: create_patient_intake_request_v2(text, jsonb); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $_$ +declare + v_owner_id uuid; + v_intake_id uuid; + v_birth_raw text; + v_birth date; +begin + select owner_id + into v_owner_id + from public.patient_invites + where token = p_token; + + if v_owner_id is null then + raise exception 'Token inv??lido ou expirado'; + end if; + + v_birth_raw := nullif(trim(coalesce( + p_payload->>'data_nascimento', + '' + )), ''); + + v_birth := case + when v_birth_raw is null then null + when v_birth_raw ~ '^\d{4}-\d{2}-\d{2}$' then v_birth_raw::date + when v_birth_raw ~ '^\d{2}-\d{2}-\d{4}$' then to_date(v_birth_raw, 'DD-MM-YYYY') + else null + end; + + insert into public.patient_intake_requests ( + owner_id, + token, + status, + consent, + + nome_completo, + email_principal, + telefone, + + avatar_url, -- ???? AQUI + + data_nascimento, + cpf, + rg, + genero, + estado_civil, + profissao, + escolaridade, + nacionalidade, + naturalidade, + + cep, + pais, + cidade, + estado, + endereco, + numero, + complemento, + bairro, + + observacoes, + notas_internas, + + encaminhado_por, + onde_nos_conheceu + ) + values ( + v_owner_id, + p_token, + 'new', + coalesce((p_payload->>'consent')::boolean, false), + + nullif(trim(p_payload->>'nome_completo'), ''), + nullif(trim(p_payload->>'email_principal'), ''), + nullif(regexp_replace(coalesce(p_payload->>'telefone',''), '\D', '', 'g'), ''), + + nullif(trim(p_payload->>'avatar_url'), ''), -- ???? AQUI + + v_birth, + nullif(regexp_replace(coalesce(p_payload->>'cpf',''), '\D', '', 'g'), ''), + nullif(trim(p_payload->>'rg'), ''), + nullif(trim(p_payload->>'genero'), ''), + nullif(trim(p_payload->>'estado_civil'), ''), + nullif(trim(p_payload->>'profissao'), ''), + nullif(trim(p_payload->>'escolaridade'), ''), + nullif(trim(p_payload->>'nacionalidade'), ''), + nullif(trim(p_payload->>'naturalidade'), ''), + + nullif(regexp_replace(coalesce(p_payload->>'cep',''), '\D', '', 'g'), ''), + nullif(trim(p_payload->>'pais'), ''), + nullif(trim(p_payload->>'cidade'), ''), + nullif(trim(p_payload->>'estado'), ''), + nullif(trim(p_payload->>'endereco'), ''), + nullif(trim(p_payload->>'numero'), ''), + nullif(trim(p_payload->>'complemento'), ''), + nullif(trim(p_payload->>'bairro'), ''), + + nullif(trim(p_payload->>'observacoes'), ''), + nullif(trim(p_payload->>'notas_internas'), ''), + + nullif(trim(p_payload->>'encaminhado_por'), ''), + nullif(trim(p_payload->>'onde_nos_conheceu'), '') + ) + returning id into v_intake_id; + + return v_intake_id; +end; +$_$; + + +-- +-- Name: create_support_session(uuid, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer DEFAULT 60) RETURNS json + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_admin_id uuid; + v_role text; + v_token text; + v_expires timestamp with time zone; + v_session support_sessions; +BEGIN + -- Verifica autentica????o + v_admin_id := auth.uid(); + IF v_admin_id IS NULL THEN + RAISE EXCEPTION 'N??o autenticado.' USING ERRCODE = 'P0001'; + END IF; + + -- Verifica role saas_admin + SELECT role INTO v_role + FROM public.profiles + WHERE id = v_admin_id; + + IF v_role <> 'saas_admin' THEN + RAISE EXCEPTION 'Acesso negado. Somente saas_admin pode criar sess??es de suporte.' + USING ERRCODE = 'P0002'; + END IF; + + -- Valida TTL (1 a 120 minutos) + IF p_ttl_minutes < 1 OR p_ttl_minutes > 120 THEN + RAISE EXCEPTION 'TTL inv??lido. Use entre 1 e 120 minutos.' + USING ERRCODE = 'P0003'; + END IF; + + -- Valida tenant + IF NOT EXISTS (SELECT 1 FROM public.tenants WHERE id = p_tenant_id) THEN + RAISE EXCEPTION 'Tenant n??o encontrado.' + USING ERRCODE = 'P0004'; + END IF; + + -- Gera token ??nico (64 chars hex, sem pgcrypto) + v_token := replace(gen_random_uuid()::text, '-', '') || replace(gen_random_uuid()::text, '-', ''); + v_expires := now() + (p_ttl_minutes || ' minutes')::interval; + + -- Insere sess??o + INSERT INTO public.support_sessions (tenant_id, admin_id, token, expires_at) + VALUES (p_tenant_id, v_admin_id, v_token, v_expires) + RETURNING * INTO v_session; + + RETURN json_build_object( + 'token', v_session.token, + 'expires_at', v_session.expires_at, + 'session_id', v_session.id + ); +END; +$$; + + +-- +-- Name: therapist_payouts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.therapist_payouts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + period_start date NOT NULL, + period_end date NOT NULL, + total_sessions integer DEFAULT 0 NOT NULL, + gross_amount numeric(10,2) DEFAULT 0 NOT NULL, + clinic_fee_total numeric(10,2) DEFAULT 0 NOT NULL, + net_amount numeric(10,2) DEFAULT 0 NOT NULL, + status text DEFAULT 'pending'::text NOT NULL, + paid_at timestamp with time zone, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT therapist_payouts_clinic_fee_total_check CHECK ((clinic_fee_total >= (0)::numeric)), + CONSTRAINT therapist_payouts_gross_amount_check CHECK ((gross_amount >= (0)::numeric)), + CONSTRAINT therapist_payouts_net_amount_check CHECK ((net_amount >= (0)::numeric)), + CONSTRAINT therapist_payouts_period_chk CHECK ((period_end >= period_start)), + CONSTRAINT therapist_payouts_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'paid'::text, 'cancelled'::text]))) +); + + +-- +-- Name: create_therapist_payout(uuid, uuid, date, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) RETURNS public.therapist_payouts + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_payout public.therapist_payouts%ROWTYPE; + v_total_sessions INTEGER; + v_gross NUMERIC(10,2); + v_clinic_fee NUMERIC(10,2); + v_net NUMERIC(10,2); +BEGIN + -- ?????? Verifica????o de permiss??o ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + -- Apenas o pr??prio terapeuta ou o tenant_admin pode criar o repasse + IF auth.uid() <> p_therapist_id AND NOT public.is_tenant_admin(p_tenant_id) THEN + RAISE EXCEPTION 'Sem permiss??o para criar repasse para este terapeuta.'; + END IF; + + -- ?????? Verifica se j?? existe repasse para o mesmo per??odo ??????????????????????????????????????????????????? + IF EXISTS ( + SELECT 1 FROM public.therapist_payouts + WHERE owner_id = p_therapist_id + AND tenant_id = p_tenant_id + AND period_start = p_period_start + AND period_end = p_period_end + AND status <> 'cancelled' + ) THEN + RAISE EXCEPTION + 'J?? existe um repasse ativo para o per??odo % a % deste terapeuta.', + p_period_start, p_period_end; + END IF; + + -- ?????? Agrega os financial_records eleg??veis ?????????????????????????????????????????????????????????????????????????????????????????? + -- Eleg??veis: paid, receita, owner=terapeuta, tenant correto, paid_at no per??odo, + -- n??o soft-deleted, ainda n??o vinculados a nenhum payout. + SELECT + COUNT(*) AS total_sessions, + COALESCE(SUM(amount), 0) AS gross_amount, + COALESCE(SUM(clinic_fee_amount), 0) AS clinic_fee_total, + COALESCE(SUM(net_amount), 0) AS net_amount + INTO + v_total_sessions, v_gross, v_clinic_fee, v_net + FROM public.financial_records fr + WHERE fr.owner_id = p_therapist_id + AND fr.tenant_id = p_tenant_id + AND fr.type = 'receita' + AND fr.status = 'paid' + AND fr.deleted_at IS NULL + AND fr.paid_at::DATE BETWEEN p_period_start AND p_period_end + AND NOT EXISTS ( + SELECT 1 FROM public.therapist_payout_records tpr + WHERE tpr.financial_record_id = fr.id + ); + + -- Sem registros eleg??veis ??? n??o criar payout vazio + IF v_total_sessions = 0 THEN + RAISE EXCEPTION + 'Nenhum registro financeiro eleg??vel encontrado para o per??odo % a %.', + p_period_start, p_period_end; + END IF; + + -- ?????? Cria o repasse ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + INSERT INTO public.therapist_payouts ( + owner_id, + tenant_id, + period_start, + period_end, + total_sessions, + gross_amount, + clinic_fee_total, + net_amount, + status + ) VALUES ( + p_therapist_id, + p_tenant_id, + p_period_start, + p_period_end, + v_total_sessions, + v_gross, + v_clinic_fee, + v_net, + 'pending' + ) + RETURNING * INTO v_payout; + + -- ?????? Vincula os financial_records ao repasse ???????????????????????????????????????????????????????????????????????????????????? + INSERT INTO public.therapist_payout_records (payout_id, financial_record_id) + SELECT v_payout.id, fr.id + FROM public.financial_records fr + WHERE fr.owner_id = p_therapist_id + AND fr.tenant_id = p_tenant_id + AND fr.type = 'receita' + AND fr.status = 'paid' + AND fr.deleted_at IS NULL + AND fr.paid_at::DATE BETWEEN p_period_start AND p_period_end + AND NOT EXISTS ( + SELECT 1 FROM public.therapist_payout_records tpr + WHERE tpr.financial_record_id = fr.id + ); + + RETURN v_payout; +END; +$$; + + +-- +-- Name: FUNCTION create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) IS 'Cria um repasse para o terapeuta com todos os financial_records paid+receita do per??odo que ainda n??o estejam vinculados a outro repasse. Lan??a exce????o se n??o houver registros eleg??veis ou se j?? houver repasse ativo no per??odo.'; + + +-- +-- Name: current_member_id(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.current_member_id(p_tenant_id uuid) RETURNS uuid + LANGUAGE sql STABLE + AS $$ + select tm.id + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + limit 1 +$$; + + +-- +-- Name: current_member_role(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.current_member_role(p_tenant_id uuid) RETURNS text + LANGUAGE sql STABLE + AS $$ + select tm.role + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + limit 1 +$$; + + +-- +-- Name: debit_addon_credit(uuid, text, uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Consumo'::text) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_credit addon_credits%ROWTYPE; + v_balance_before INTEGER; + v_balance_after INTEGER; +BEGIN + -- Lock e leitura + SELECT * INTO v_credit + FROM addon_credits + WHERE tenant_id = p_tenant_id AND addon_type = p_addon_type AND is_active = true + FOR UPDATE; + + IF NOT FOUND THEN + RETURN jsonb_build_object('success', false, 'reason', 'no_credits', 'balance', 0); + END IF; + + -- Verifica saldo + IF v_credit.balance <= 0 THEN + RETURN jsonb_build_object('success', false, 'reason', 'insufficient_balance', 'balance', 0); + END IF; + + -- Verifica rate limit di??rio + IF v_credit.daily_limit IS NOT NULL THEN + -- Reset se passou do dia + IF v_credit.daily_reset_at IS NULL OR v_credit.daily_reset_at < date_trunc('day', now()) THEN + UPDATE addon_credits SET daily_used = 0, daily_reset_at = date_trunc('day', now()) + interval '1 day' WHERE id = v_credit.id; + v_credit.daily_used := 0; + END IF; + + IF v_credit.daily_used >= v_credit.daily_limit THEN + RETURN jsonb_build_object('success', false, 'reason', 'daily_limit_reached', 'balance', v_credit.balance); + END IF; + END IF; + + -- Verifica rate limit hor??rio + IF v_credit.hourly_limit IS NOT NULL THEN + IF v_credit.hourly_reset_at IS NULL OR v_credit.hourly_reset_at < date_trunc('hour', now()) THEN + UPDATE addon_credits SET hourly_used = 0, hourly_reset_at = date_trunc('hour', now()) + interval '1 hour' WHERE id = v_credit.id; + v_credit.hourly_used := 0; + END IF; + + IF v_credit.hourly_used >= v_credit.hourly_limit THEN + RETURN jsonb_build_object('success', false, 'reason', 'hourly_limit_reached', 'balance', v_credit.balance); + END IF; + END IF; + + -- Verifica expira????o + IF v_credit.expires_at IS NOT NULL AND v_credit.expires_at < now() THEN + RETURN jsonb_build_object('success', false, 'reason', 'credits_expired', 'balance', v_credit.balance); + END IF; + + v_balance_before := v_credit.balance; + v_balance_after := v_credit.balance - 1; + + -- Debita + UPDATE addon_credits + SET balance = v_balance_after, + total_consumed = total_consumed + 1, + daily_used = COALESCE(daily_used, 0) + 1, + hourly_used = COALESCE(hourly_used, 0) + 1, + updated_at = now() + WHERE id = v_credit.id; + + -- Registra transa????o + INSERT INTO addon_transactions ( + tenant_id, addon_type, type, amount, + balance_before, balance_after, + queue_id, description + ) VALUES ( + p_tenant_id, p_addon_type, 'consume', -1, + v_balance_before, v_balance_after, + p_queue_id, p_description + ); + + RETURN jsonb_build_object( + 'success', true, + 'balance_before', v_balance_before, + 'balance_after', v_balance_after + ); +END; +$$; + + +-- +-- Name: FUNCTION debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) IS 'Debita 1 cr??dito de add-on. Verifica saldo, rate limits e expira????o.'; + + +-- +-- Name: delete_commitment_full(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_is_native boolean; + v_fields int := 0; + v_logs int := 0; + v_parent int := 0; +begin + if auth.uid() is null then + raise exception 'Not authenticated'; + end if; + + if not exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.status = 'active' + ) then + raise exception 'Not allowed'; + end if; + + select dc.is_native + into v_is_native + from public.determined_commitments dc + where dc.tenant_id = p_tenant_id + and dc.id = p_commitment_id; + + if v_is_native is null then + raise exception 'Commitment not found'; + end if; + + if v_is_native = true then + raise exception 'Cannot delete native commitment'; + end if; + + delete from public.determined_commitment_fields + where tenant_id = p_tenant_id + and commitment_id = p_commitment_id; + get diagnostics v_fields = row_count; + + delete from public.commitment_time_logs + where tenant_id = p_tenant_id + and commitment_id = p_commitment_id; + get diagnostics v_logs = row_count; + + delete from public.determined_commitments + where tenant_id = p_tenant_id + and id = p_commitment_id; + get diagnostics v_parent = row_count; + + if v_parent <> 1 then + raise exception 'Parent not deleted (RLS/owner issue).'; + end if; + + return jsonb_build_object( + 'ok', true, + 'deleted', jsonb_build_object( + 'fields', v_fields, + 'logs', v_logs, + 'commitment', v_parent + ) + ); +end; +$$; + + +-- +-- Name: delete_determined_commitment(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_is_native boolean; + v_fields_deleted int := 0; + v_logs_deleted int := 0; + v_commitment_deleted int := 0; +begin + if auth.uid() is null then + raise exception 'Not authenticated'; + end if; + + if not exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.status = 'active' + ) then + raise exception 'Not allowed'; + end if; + + select dc.is_native + into v_is_native + from public.determined_commitments dc + where dc.tenant_id = p_tenant_id + and dc.id = p_commitment_id; + + if v_is_native is null then + raise exception 'Commitment not found for tenant'; + end if; + + if v_is_native = true then + raise exception 'Cannot delete native commitment'; + end if; + + delete from public.determined_commitment_fields f + where f.tenant_id = p_tenant_id + and f.commitment_id = p_commitment_id; + get diagnostics v_fields_deleted = row_count; + + delete from public.commitment_time_logs l + where l.tenant_id = p_tenant_id + and l.commitment_id = p_commitment_id; + get diagnostics v_logs_deleted = row_count; + + delete from public.determined_commitments dc + where dc.tenant_id = p_tenant_id + and dc.id = p_commitment_id; + get diagnostics v_commitment_deleted = row_count; + + if v_commitment_deleted <> 1 then + raise exception 'Delete did not remove the commitment (tenant mismatch?)'; + end if; + + return jsonb_build_object( + 'ok', true, + 'tenant_id', p_tenant_id, + 'commitment_id', p_commitment_id, + 'deleted', jsonb_build_object( + 'fields', v_fields_deleted, + 'logs', v_logs_deleted, + 'commitment', v_commitment_deleted + ) + ); +end; +$$; + + +-- +-- Name: dev_list_auth_users(integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_list_auth_users(p_limit integer DEFAULT 50) RETURNS TABLE(id uuid, email text, created_at timestamp with time zone) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +begin + -- s?? saas_admin pode ver + if not exists ( + select 1 + from public.profiles p + where p.id = auth.uid() + and p.role = 'saas_admin' + ) then + return; + end if; + + return query + select + u.id, + u.email, + u.created_at + from auth.users u + order by u.created_at desc + limit greatest(1, least(coalesce(p_limit, 50), 500)); +end; +$$; + + +-- +-- Name: dev_list_custom_users(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_list_custom_users() RETURNS TABLE(user_id uuid, email text, created_at timestamp with time zone, global_role text, tenant_role text, tenant_id uuid, password_dev text, kind text) + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + with base as ( + select + u.id as user_id, + lower(u.email) as email, + u.created_at + from auth.users u + where lower(u.email) not in ( + 'clinic@agenciapsi.com.br', + 'therapist@agenciapsi.com.br', + 'patient@agenciapsi.com.br', + 'saas@agenciapsi.com.br' + ) + ), + prof as ( + select p.id, p.role as global_role + from public.profiles p + ), + last_membership as ( + select distinct on (tm.user_id) + tm.user_id, + tm.tenant_id, + tm.role as tenant_role, + tm.created_at + from public.tenant_members tm + where tm.status = 'active' + order by tm.user_id, tm.created_at desc + ) + select + b.user_id, + b.email, + b.created_at, + pr.global_role, + lm.tenant_role, + lm.tenant_id, + dc.password_dev, + dc.kind + from base b + left join prof pr on pr.id = b.user_id + left join last_membership lm on lm.user_id = b.user_id + left join public.dev_user_credentials dc on lower(dc.email) = b.email + order by b.created_at desc; +$$; + + +-- +-- Name: dev_list_intent_leads(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_list_intent_leads() RETURNS TABLE(email text, last_intent_at timestamp with time zone, plan_key text, billing_interval text, status text, tenant_id uuid) + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + select + lower(si.email) as email, + max(si.created_at) as last_intent_at, + (array_agg(si.plan_key order by si.created_at desc))[1] as plan_key, + (array_agg(si.interval order by si.created_at desc))[1] as billing_interval, + (array_agg(si.status order by si.created_at desc))[1] as status, + (array_agg(si.tenant_id order by si.created_at desc))[1] as tenant_id + from public.subscription_intents si + where si.email is not null + and not exists ( + select 1 + from auth.users au + where lower(au.email) = lower(si.email) + ) + group by lower(si.email) + order by max(si.created_at) desc; +$$; + + +-- +-- Name: dev_public_debug_snapshot(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_public_debug_snapshot() RETURNS TABLE(users_total integer, tenants_total integer, intents_new_total integer, latest_intents jsonb) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $_$ +declare + v_latest jsonb; +begin + select jsonb_agg( + jsonb_build_object( + 'created_at', si.created_at, + 'email_masked', + regexp_replace(lower(si.email), '(^.).*(@.*$)', '\1***\2'), + 'plan_key', si.plan_key, + 'status', si.status + ) + order by si.created_at desc + ) + into v_latest + from ( + select si.* + from public.subscription_intents si + where si.email is not null + order by si.created_at desc + limit 5 + ) si; + + return query + select + (select count(*)::int from auth.users) as users_total, + (select count(*)::int from public.tenants) as tenants_total, + (select count(*)::int from public.subscription_intents where status = 'new') as intents_new_total, + coalesce(v_latest, '[]'::jsonb) as latest_intents; +end; +$_$; + + +-- +-- Name: ensure_personal_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.ensure_personal_tenant() RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_uid uuid; + v_existing uuid; +BEGIN + v_uid := auth.uid(); + IF v_uid IS NULL THEN + RAISE EXCEPTION 'Not authenticated'; + END IF; + + SELECT tm.tenant_id INTO v_existing + FROM public.tenant_members tm + JOIN public.tenants t ON t.id = tm.tenant_id + WHERE tm.user_id = v_uid + AND tm.status = 'active' + AND t.kind IN ('therapist', 'saas') + ORDER BY tm.created_at DESC + LIMIT 1; + + IF v_existing IS NOT NULL THEN + RETURN v_existing; + END IF; + + RETURN public.provision_account_tenant(v_uid, 'therapist'); +END; +$$; + + +-- +-- Name: ensure_personal_tenant_for_user(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_uid uuid; + v_existing uuid; + v_tenant uuid; + v_email text; + v_name text; +begin + v_uid := p_user_id; + if v_uid is null then + raise exception 'Missing user id'; + end if; + + -- s?? considera tenant pessoal (kind='saas') + select tm.tenant_id + into v_existing + from public.tenant_members tm + join public.tenants t on t.id = tm.tenant_id + where tm.user_id = v_uid + and tm.status = 'active' + and t.kind = 'saas' + order by tm.created_at desc + limit 1; + + if v_existing is not null then + return v_existing; + end if; + + select email into v_email + from auth.users + where id = v_uid; + + v_name := coalesce(split_part(v_email, '@', 1), 'Conta'); + + insert into public.tenants (name, kind, created_at) + values (v_name || ' (Pessoal)', 'saas', now()) + returning id into v_tenant; + + insert into public.tenant_members (tenant_id, user_id, role, status, created_at) + values (v_tenant, v_uid, 'tenant_admin', 'active', now()); + + return v_tenant; +end; +$$; + + +-- +-- Name: faq_votar(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.faq_votar(faq_id uuid) RETURNS void + LANGUAGE sql SECURITY DEFINER + AS $$ + update public.saas_faq + set votos = votos + 1, + updated_at = now() + where id = faq_id + and ativo = true; +$$; + + +-- +-- Name: fix_all_subscription_mismatches(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.fix_all_subscription_mismatches() RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + r record; +begin + for r in + select distinct s.user_id as owner_id + from public.subscriptions s + where s.status = 'active' + and s.user_id is not null + loop + perform public.rebuild_owner_entitlements(r.owner_id); + end loop; +end; +$$; + + +-- +-- Name: fn_agenda_regras_semanais_no_overlap(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.fn_agenda_regras_semanais_no_overlap() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_count int; +begin + if new.ativo is false then + return new; + end if; + + select count(*) into v_count + from public.agenda_regras_semanais r + where r.owner_id = new.owner_id + and r.dia_semana = new.dia_semana + and r.ativo is true + and (tg_op = 'INSERT' or r.id <> new.id) + and (new.hora_inicio < r.hora_fim and new.hora_fim > r.hora_inicio); + + if v_count > 0 then + raise exception 'Janela sobreposta: j?? existe uma regra ativa nesse intervalo.'; + end if; + + return new; +end; +$$; + + +-- +-- Name: get_financial_report(uuid, date, date, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text DEFAULT 'month'::text) RETURNS TABLE(group_key text, group_label text, total_receitas numeric, total_despesas numeric, saldo numeric, total_pendente numeric, total_overdue numeric, count_records bigint) + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + AS $$ + + -- ?????? Valida p_group_by antes de executar ?????????????????????????????????????????????????????????????????????????????????????????????????????? + -- (lan??a erro se valor inv??lido; plpgsql seria necess??rio para isso em SQL puro, + -- ent??o usamos um CTE de valida????o com CASE WHEN para retornar vazio em vez de erro) + + WITH base AS ( + SELECT + fr.type, + fr.amount, + fr.final_amount, + fr.status, + fr.deleted_at, + -- Chave de agrupamento calculada conforme p_group_by + CASE p_group_by + WHEN 'month' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'YYYY-MM' + ) + WHEN 'week' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'IYYY-"W"IW' + ) + WHEN 'category' THEN COALESCE(fr.category_id::TEXT, fr.category, 'sem_categoria') + WHEN 'patient' THEN COALESCE(fr.patient_id::TEXT, 'sem_paciente') + ELSE NULL -- group_by inv??lido ??? group_key NULL ??? retorno vazio + END AS gkey, + -- Label leg??vel (enriquecido via JOIN abaixo quando poss??vel) + CASE p_group_by + WHEN 'month' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'YYYY-MM' + ) + WHEN 'week' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'IYYY-"W"IW' + ) + WHEN 'category' THEN COALESCE(fc.name, fr.category, 'Sem categoria') + WHEN 'patient' THEN COALESCE(p.nome_completo, fr.patient_id::TEXT, 'Sem paciente') + ELSE NULL + END AS glabel + FROM public.financial_records fr + LEFT JOIN public.financial_categories fc + ON fc.id = fr.category_id + LEFT JOIN public.patients p + ON p.id = fr.patient_id + WHERE fr.owner_id = p_owner_id + AND fr.deleted_at IS NULL + AND COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE) + BETWEEN p_start_date AND p_end_date + ) + + SELECT + gkey AS group_key, + glabel AS group_label, + + COALESCE(SUM(final_amount) FILTER (WHERE type = 'receita' AND status = 'paid'), 0) + AS total_receitas, + + COALESCE(SUM(final_amount) FILTER (WHERE type = 'despesa' AND status = 'paid'), 0) + AS total_despesas, + + COALESCE(SUM(final_amount) FILTER (WHERE type = 'receita' AND status = 'paid'), 0) + - COALESCE(SUM(final_amount) FILTER (WHERE type = 'despesa' AND status = 'paid'), 0) + AS saldo, + + COALESCE(SUM(final_amount) FILTER (WHERE status = 'pending'), 0) AS total_pendente, + + COALESCE(SUM(final_amount) FILTER (WHERE status = 'overdue'), 0) AS total_overdue, + + COUNT(*) AS count_records + + FROM base + WHERE gkey IS NOT NULL -- descarta p_group_by inv??lido + GROUP BY gkey, glabel + ORDER BY gkey ASC; + +$$; + + +-- +-- Name: FUNCTION get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text) IS 'Relat??rio financeiro agrupado por m??s, semana ISO, categoria ou paciente. p_group_by aceita: ''month'' | ''week'' | ''category'' | ''patient''. Totais de receita/despesa consideram apenas registros com status=paid. total_pendente e total_overdue incluem todos os tipos (receita + despesa).'; + + +-- +-- Name: get_financial_summary(uuid, integer, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_month integer) RETURNS TABLE(total_receitas numeric, total_despesas numeric, total_pendente numeric, saldo_liquido numeric, total_repasse numeric, count_receitas bigint, count_despesas bigint) + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + AS $$ + SELECT + -- Receitas pagas no per??odo + COALESCE(SUM(amount) FILTER ( + WHERE type = 'receita' AND status = 'paid' + ), 0) AS total_receitas, + + -- Despesas pagas no per??odo + COALESCE(SUM(amount) FILTER ( + WHERE type = 'despesa' AND status = 'paid' + ), 0) AS total_despesas, + + -- Tudo pendente ou vencido (receitas + despesas) + COALESCE(SUM(amount) FILTER ( + WHERE status IN ('pending', 'overdue') + ), 0) AS total_pendente, + + -- Saldo l??quido (receitas pagas ??? despesas pagas) + COALESCE(SUM(amount) FILTER ( + WHERE type = 'receita' AND status = 'paid' + ), 0) + - COALESCE(SUM(amount) FILTER ( + WHERE type = 'despesa' AND status = 'paid' + ), 0) AS saldo_liquido, + + -- Total repassado ?? cl??nica (apenas receitas pagas) + COALESCE(SUM(clinic_fee_amount) FILTER ( + WHERE type = 'receita' AND status = 'paid' + ), 0) AS total_repasse, + + -- Contadores (excluindo soft-deleted) + COUNT(*) FILTER (WHERE type = 'receita' AND deleted_at IS NULL) AS count_receitas, + COUNT(*) FILTER (WHERE type = 'despesa' AND deleted_at IS NULL) AS count_despesas + + FROM public.financial_records + WHERE owner_id = p_owner_id + AND deleted_at IS NULL + AND EXTRACT(YEAR FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_year + AND EXTRACT(MONTH FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_month; +$$; + + +-- +-- Name: get_my_email(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.get_my_email() RETURNS text + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ + select lower(email) + from auth.users + where id = auth.uid(); +$$; + + +-- +-- Name: guard_account_type_immutable(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_account_type_immutable() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF OLD.account_type <> 'free' AND NEW.account_type IS DISTINCT FROM OLD.account_type THEN + RAISE EXCEPTION 'account_type ?? imut??vel ap??s escolha (atual: "%" para tentativa: "%"). Para mudar de perfil, crie uma nova conta.', OLD.account_type, NEW.account_type + USING ERRCODE = 'P0001'; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: guard_locked_commitment(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_locked_commitment() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if (old.is_locked = true) then + if (tg_op = 'DELETE') then + raise exception 'Compromisso bloqueado n??o pode ser exclu??do.'; + end if; + + if (tg_op = 'UPDATE') then + if (new.active = false) then + raise exception 'Compromisso bloqueado n??o pode ser desativado.'; + end if; + + -- trava renomear (mant??m o "Sess??o" sempre igual) + if (new.name is distinct from old.name) then + raise exception 'Compromisso bloqueado n??o pode ser renomeado.'; + end if; + + -- se quiser travar descri????o tamb??m, descomente: + -- if (new.description is distinct from old.description) then + -- raise exception 'Compromisso bloqueado n??o pode alterar descri????o.'; + -- end if; + end if; + end if; + + return new; +end; +$$; + + +-- +-- Name: guard_no_change_core_plan_key(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_no_change_core_plan_key() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') + and new.key is distinct from old.key then + raise exception 'N??o ?? permitido alterar a key do plano padr??o (%).', old.key + using errcode = 'P0001'; + end if; + + return new; +end $$; + + +-- +-- Name: guard_no_change_plan_target(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_no_change_plan_target() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_bypass text; +begin + -- bypass controlado por sess??o/transa????o: + -- s?? passa se app.plan_migration_bypass = '1' + v_bypass := current_setting('app.plan_migration_bypass', true); + + if v_bypass = '1' then + return new; + end if; + + -- comportamento original (bloqueia qualquer mudan??a) + if new.target is distinct from old.target then + raise exception 'N??o ?? permitido alterar target do plano (%) de % para %.', + old.key, old.target, new.target + using errcode = 'P0001'; + end if; + + return new; +end +$$; + + +-- +-- Name: guard_no_delete_core_plans(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_no_delete_core_plans() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') then + raise exception 'Plano padr??o (%) n??o pode ser removido.', old.key + using errcode = 'P0001'; + end if; + + return old; +end $$; + + +-- +-- Name: guard_patient_cannot_own_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_patient_cannot_own_tenant() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_account_type text; +BEGIN + SELECT account_type INTO v_account_type + FROM public.profiles + WHERE id = NEW.user_id; + + IF v_account_type = 'patient' AND NEW.role IN ('tenant_admin', 'therapist') THEN + RAISE EXCEPTION 'Usu??rio com perfil "patient" n??o pode ser propriet??rio ou terapeuta de um tenant. Se tornou profissional? Crie uma nova conta.' + USING ERRCODE = 'P0001'; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: guard_tenant_kind_immutable(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_tenant_kind_immutable() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF NEW.kind IS DISTINCT FROM OLD.kind THEN + RAISE EXCEPTION 'tenants.kind ?? imut??vel ap??s cria????o. Tentativa de alterar "%" para "%".', OLD.kind, NEW.kind + USING ERRCODE = 'P0001'; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: handle_new_user(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.handle_new_user() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + INSERT INTO public.profiles (id, role, account_type) + VALUES (NEW.id, 'portal_user', 'free') + ON CONFLICT (id) DO NOTHING; + RETURN NEW; +END; +$$; + + +-- +-- Name: handle_new_user_create_personal_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.handle_new_user_create_personal_tenant() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + -- Desabilitado. Tenant criado no onboarding via provision_account_tenant(). + RETURN NEW; +END; +$$; + + +-- +-- Name: has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.owner_feature_entitlements e + where e.owner_id = p_owner_id + and e.feature_key = p_feature_key + ); +$$; + + +-- +-- Name: is_clinic_tenant(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_clinic_tenant(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + SELECT EXISTS ( + SELECT 1 FROM public.tenants t + WHERE t.id = _tenant_id + AND t.kind IN ('clinic', 'clinic_coworking', 'clinic_reception', 'clinic_full') + ); +$$; + + +-- +-- Name: is_saas_admin(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_saas_admin() RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 from public.saas_admins sa + where sa.user_id = auth.uid() + ); +$$; + + +-- +-- Name: is_tenant_admin(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_tenant_admin(p_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ + select exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.role = 'tenant_admin' + and tm.status = 'active' + ); +$$; + + +-- +-- Name: is_tenant_member(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_tenant_member(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.tenant_members m + where m.tenant_id = _tenant_id + and m.user_id = auth.uid() + and m.status = 'active' + ); +$$; + + +-- +-- Name: is_therapist_tenant(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_therapist_tenant(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + SELECT EXISTS ( + SELECT 1 FROM public.tenants t + WHERE t.id = _tenant_id AND t.kind = 'therapist' + ); +$$; + + +-- +-- Name: jwt_email(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.jwt_email() RETURNS text + LANGUAGE sql STABLE + AS $$ + select nullif(lower(current_setting('request.jwt.claim.email', true)), ''); +$$; + + +-- +-- Name: list_financial_records(uuid, integer, integer, text, text, uuid, integer, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.list_financial_records(p_owner_id uuid, p_year integer DEFAULT NULL::integer, p_month integer DEFAULT NULL::integer, p_type text DEFAULT NULL::text, p_status text DEFAULT NULL::text, p_patient_id uuid DEFAULT NULL::uuid, p_limit integer DEFAULT 50, p_offset integer DEFAULT 0) RETURNS SETOF public.financial_records + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + AS $$ + SELECT * + FROM public.financial_records + WHERE owner_id = p_owner_id + AND deleted_at IS NULL + AND (p_type IS NULL OR type::TEXT = p_type) + AND (p_status IS NULL OR status = p_status) + AND (p_patient_id IS NULL OR patient_id = p_patient_id) + AND (p_year IS NULL OR EXTRACT(YEAR FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_year) + AND (p_month IS NULL OR EXTRACT(MONTH FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_month) + ORDER BY COALESCE(paid_at, due_date::TIMESTAMPTZ, created_at) DESC + LIMIT p_limit + OFFSET p_offset; +$$; + + +-- +-- Name: mark_as_paid(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method text) RETURNS SETOF public.financial_records + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_record public.financial_records%ROWTYPE; +BEGIN + -- Garante que o registro pertence ao usu??rio autenticado (RLS n??o aplica em SECURITY DEFINER) + SELECT * INTO v_record + FROM public.financial_records + WHERE id = p_financial_record_id + AND owner_id = auth.uid() + AND deleted_at IS NULL; + + IF NOT FOUND THEN + RAISE EXCEPTION 'Registro financeiro n??o encontrado ou sem permiss??o.'; + END IF; + + IF v_record.status NOT IN ('pending', 'overdue') THEN + RAISE EXCEPTION 'Apenas cobran??as pendentes ou vencidas podem ser marcadas como pagas.'; + END IF; + + UPDATE public.financial_records + SET status = 'paid', + paid_at = NOW(), + payment_method = p_payment_method, + updated_at = NOW() + WHERE id = p_financial_record_id + RETURNING * INTO v_record; + + RETURN NEXT v_record; +END; +$$; + + +-- +-- Name: mark_payout_as_paid(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.mark_payout_as_paid(p_payout_id uuid) RETURNS public.therapist_payouts + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_payout public.therapist_payouts%ROWTYPE; +BEGIN + -- Busca o payout + SELECT * INTO v_payout + FROM public.therapist_payouts + WHERE id = p_payout_id; + + IF NOT FOUND THEN + RAISE EXCEPTION 'Repasse n??o encontrado: %', p_payout_id; + END IF; + + -- Verifica permiss??o: apenas tenant_admin do tenant do repasse + IF NOT public.is_tenant_admin(v_payout.tenant_id) THEN + RAISE EXCEPTION 'Apenas o administrador da cl??nica pode marcar repasses como pagos.'; + END IF; + + -- Verifica status + IF v_payout.status <> 'pending' THEN + RAISE EXCEPTION + 'Repasse j?? est?? com status ''%''. Apenas repasses pendentes podem ser pagos.', + v_payout.status; + END IF; + + -- Atualiza + UPDATE public.therapist_payouts + SET + status = 'paid', + paid_at = NOW(), + updated_at = NOW() + WHERE id = p_payout_id + RETURNING * INTO v_payout; + + RETURN v_payout; +END; +$$; + + +-- +-- Name: FUNCTION mark_payout_as_paid(p_payout_id uuid); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.mark_payout_as_paid(p_payout_id uuid) IS 'Marca um repasse de terapeuta como pago. Apenas o tenant_admin pode chamar. Apenas repasses com status=pending podem ser finalizados.'; + + +-- +-- Name: my_tenants(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.my_tenants() RETURNS TABLE(tenant_id uuid, role text, status text, kind text) + LANGUAGE sql STABLE + AS $$ + select + tm.tenant_id, + tm.role, + tm.status, + t.kind + from public.tenant_members tm + join public.tenants t on t.id = tm.tenant_id + where tm.user_id = auth.uid(); +$$; + + +-- +-- Name: notice_track_click(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notice_track_click(p_notice_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +begin + update public.global_notices + set clicks_count = clicks_count + 1 + where id = p_notice_id; +end; +$$; + + +-- +-- Name: notice_track_view(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notice_track_view(p_notice_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +begin + update public.global_notices + set views_count = views_count + 1 + where id = p_notice_id; +end; +$$; + + +-- +-- Name: notify_on_intake(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notify_on_intake() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF NEW.status = 'new' THEN + INSERT INTO public.notifications ( + owner_id, + tenant_id, + type, + ref_id, + ref_table, + payload + ) + VALUES ( + NEW.owner_id, + NEW.tenant_id, + 'new_patient', + NEW.id, + 'patient_intake_requests', + jsonb_build_object( + 'title', 'Novo cadastro externo', + 'detail', COALESCE(NEW.nome_completo, 'Paciente'), + 'deeplink', '/therapist/patients/cadastro/recebidos', + 'avatar_initials', upper(left(COALESCE(NEW.nome_completo, '?'), 2)) + ) + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: notify_on_scheduling(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notify_on_scheduling() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ BEGIN IF NEW.status = 'pendente' THEN + INSERT INTO public.notifications ( owner_id, tenant_id, type, ref_id, ref_table, payload ) VALUES ( + NEW.owner_id, NEW.tenant_id, + 'new_scheduling', NEW.id, 'agendador_solicitacoes', jsonb_build_object( 'title', 'Nova solicita????o de agendamento', 'detail', COALESCE(NEW.paciente_nome, 'Paciente') || ' ' || COALESCE(NEW.paciente_sobrenome, '') || ' ??? ' || COALESCE(NEW.tipo, ''), 'deeplink', '/therapist/agendamentos-recebidos', 'avatar_initials', upper(left(COALESCE(NEW.paciente_nome, '?'), 1) || left(COALESCE(NEW.paciente_sobrenome, ''), 1)) ) ); END IF; RETURN NEW; END; $$; + + +-- +-- Name: notify_on_session_status(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notify_on_session_status() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_nome text; +BEGIN + IF NEW.status IN ('faltou', 'cancelado') AND OLD.status IS DISTINCT FROM NEW.status THEN + + SELECT nome_completo + INTO v_nome + FROM public.patients + WHERE id = NEW.patient_id + LIMIT 1; + + INSERT INTO public.notifications ( + owner_id, + tenant_id, + type, + ref_id, + ref_table, + payload + ) + VALUES ( + NEW.owner_id, + NEW.tenant_id, + 'session_status', + NEW.id, + 'agenda_eventos', + jsonb_build_object( + 'title', CASE WHEN NEW.status = 'faltou' THEN 'Paciente faltou' ELSE 'Sess??o cancelada' END, + 'detail', COALESCE(v_nome, 'Paciente') || ' ??? ' || to_char(NEW.inicio_em, 'DD/MM HH24:MI'), + 'deeplink', '/therapist/agenda', + 'avatar_initials', upper(left(COALESCE(v_nome, '?'), 2)) + ) + ); + + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: on_new_user_seed_patient_groups(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.on_new_user_seed_patient_groups() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + BEGIN + PERFORM public.seed_default_patient_groups(NEW.id); + RETURN NEW; + END; + $$; + + +-- +-- Name: patients_validate_member_consistency(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.patients_validate_member_consistency() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_tenant_responsible uuid; + v_tenant_therapist uuid; +BEGIN + -- responsible_member sempre deve existir e ser do tenant + SELECT tenant_id INTO v_tenant_responsible + FROM public.tenant_members + WHERE id = NEW.responsible_member_id; + + IF v_tenant_responsible IS NULL THEN + RAISE EXCEPTION 'Responsible member not found'; + END IF; + + IF NEW.tenant_id IS NULL THEN + RAISE EXCEPTION 'tenant_id is required'; + END IF; + + IF v_tenant_responsible <> NEW.tenant_id THEN + RAISE EXCEPTION 'Responsible member must belong to the same tenant'; + END IF; + + -- therapist scope: therapist_member_id deve existir e ser do mesmo tenant + IF NEW.patient_scope = 'therapist' THEN + IF NEW.therapist_member_id IS NULL THEN + RAISE EXCEPTION 'therapist_member_id is required when patient_scope=therapist'; + END IF; + + SELECT tenant_id INTO v_tenant_therapist + FROM public.tenant_members + WHERE id = NEW.therapist_member_id; + + IF v_tenant_therapist IS NULL THEN + RAISE EXCEPTION 'Therapist member not found'; + END IF; + + IF v_tenant_therapist <> NEW.tenant_id THEN + RAISE EXCEPTION 'Therapist member must belong to the same tenant'; + END IF; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: patients_validate_responsible_member_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.patients_validate_responsible_member_tenant() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + m_tenant uuid; +begin + select tenant_id into m_tenant + from public.tenant_members + where id = new.responsible_member_id; + + if m_tenant is null then + raise exception 'Responsible member not found'; + end if; + + if new.tenant_id is null then + raise exception 'tenant_id is required'; + end if; + + if m_tenant <> new.tenant_id then + raise exception 'Responsible member must belong to the same tenant'; + end if; + + return new; +end; +$$; + + +-- +-- Name: populate_notification_queue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.populate_notification_queue() RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + INSERT INTO public.notification_queue ( + tenant_id, owner_id, agenda_evento_id, patient_id, + channel, template_key, schedule_key, + resolved_vars, recipient_address, + scheduled_at, idempotency_key + ) + SELECT + ae.tenant_id, + ae.owner_id, + ae.id AS agenda_evento_id, + ae.patient_id, + ch.channel, + 'session.' || REPLACE(ns.event_type, '_sessao', '') || '.' || ch.channel, + ns.schedule_key, + jsonb_build_object( + 'nome_paciente', COALESCE(p.nome_completo, 'Paciente'), + 'data_sessao', TO_CHAR(ae.inicio_em AT TIME ZONE 'America/Sao_Paulo', 'DD/MM/YYYY'), + 'hora_sessao', TO_CHAR(ae.inicio_em AT TIME ZONE 'America/Sao_Paulo', 'HH24:MI'), + 'nome_terapeuta', COALESCE(prof.full_name, 'Terapeuta'), + 'modalidade', COALESCE(ae.modalidade, 'Presencial'), + 'titulo', COALESCE(ae.titulo, 'Sess??o') + ), + CASE ch.channel + WHEN 'whatsapp' THEN COALESCE(p.telefone, '') + WHEN 'sms' THEN COALESCE(p.telefone, '') + WHEN 'email' THEN COALESCE(p.email_principal, '') + END, + CASE + WHEN (ae.inicio_em - (ns.offset_minutes || ' minutes')::interval)::time + < ns.allowed_time_start + THEN DATE_TRUNC('day', ae.inicio_em - (ns.offset_minutes || ' minutes')::interval) + + ns.allowed_time_start + WHEN (ae.inicio_em - (ns.offset_minutes || ' minutes')::interval)::time + > ns.allowed_time_end + THEN DATE_TRUNC('day', ae.inicio_em - (ns.offset_minutes || ' minutes')::interval) + + ns.allowed_time_start + ELSE ae.inicio_em - (ns.offset_minutes || ' minutes')::interval + END, + ae.id::text || ':' || ns.schedule_key || ':' || ch.channel || ':' + || ae.inicio_em::date::text + FROM public.agenda_eventos ae + JOIN public.patients p ON p.id = ae.patient_id + LEFT JOIN public.profiles prof ON prof.id = ae.owner_id + JOIN public.notification_schedules ns + ON ns.owner_id = ae.owner_id + AND ns.is_active = true + AND ns.deleted_at IS NULL + AND ns.trigger_type = 'before_event' + AND ns.event_type = 'lembrete_sessao' + JOIN public.notification_channels nc + ON nc.owner_id = ae.owner_id + AND nc.is_active = true + AND nc.deleted_at IS NULL + CROSS JOIN LATERAL ( + SELECT 'whatsapp' AS channel WHERE ns.whatsapp_enabled AND nc.channel = 'whatsapp' + UNION ALL + SELECT 'email' AS channel WHERE ns.email_enabled AND nc.channel = 'email' + UNION ALL + SELECT 'sms' AS channel WHERE ns.sms_enabled AND nc.channel = 'sms' + ) ch + LEFT JOIN public.notification_preferences np + ON np.patient_id = ae.patient_id + AND np.owner_id = ae.owner_id + AND np.deleted_at IS NULL + WHERE + ae.inicio_em > now() + AND ae.inicio_em <= now() + interval '48 hours' + AND ae.status NOT IN ('cancelado', 'faltou') + AND CASE ch.channel + WHEN 'whatsapp' THEN COALESCE(p.telefone, '') != '' + WHEN 'sms' THEN COALESCE(p.telefone, '') != '' + WHEN 'email' THEN COALESCE(p.email_principal, '') != '' + END + AND CASE ch.channel + WHEN 'whatsapp' THEN COALESCE(np.whatsapp_opt_in, true) + WHEN 'email' THEN COALESCE(np.email_opt_in, true) + WHEN 'sms' THEN COALESCE(np.sms_opt_in, false) + END + AND EXISTS ( + SELECT 1 FROM public.profiles tp + WHERE tp.id = ae.owner_id + AND COALESCE(tp.notify_reminders, true) = true + ) + ON CONFLICT (idempotency_key) DO NOTHING; +END; +$$; + + +-- +-- Name: prevent_promoting_to_system(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.prevent_promoting_to_system() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if new.is_system = true and old.is_system is distinct from true then + raise exception 'N??o ?? permitido transformar um grupo comum em grupo do sistema.'; + end if; + return new; +end; +$$; + + +-- +-- Name: prevent_saas_membership(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.prevent_saas_membership() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM public.profiles + WHERE id = NEW.user_id + AND role = 'saas_admin' + ) THEN + RAISE EXCEPTION 'SaaS admin cannot belong to tenant'; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: prevent_system_group_changes(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.prevent_system_group_changes() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + -- Se for grupo do sistema, regras r??gidas: + if old.is_system = true then + + -- nunca pode deletar + if tg_op = 'DELETE' then + raise exception 'Grupos padr??o do sistema n??o podem ser alterados ou exclu??dos.'; + end if; + + if tg_op = 'UPDATE' then + -- permite SOMENTE mudar tenant_id e/ou updated_at + -- qualquer mudan??a de conte??do permanece proibida + if + new.nome is distinct from old.nome or + new.descricao is distinct from old.descricao or + new.cor is distinct from old.cor or + new.is_active is distinct from old.is_active or + new.is_system is distinct from old.is_system or + new.owner_id is distinct from old.owner_id or + new.therapist_id is distinct from old.therapist_id or + new.created_at is distinct from old.created_at + then + raise exception 'Grupos padr??o do sistema n??o podem ser alterados ou exclu??dos.'; + end if; + + -- chegou aqui: s?? tenant_id/updated_at mudaram -> ok + return new; + end if; + + end if; + + -- n??o-system: deixa passar + if tg_op = 'DELETE' then + return old; + end if; + + return new; +end; +$$; + + +-- +-- Name: provision_account_tenant(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text DEFAULT NULL::text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_tenant_id uuid; + v_account_type text; + v_name text; +BEGIN + IF p_kind NOT IN ('therapist', 'clinic_coworking', 'clinic_reception', 'clinic_full') THEN + RAISE EXCEPTION 'kind inv??lido: "%". Use: therapist, clinic_coworking, clinic_reception, clinic_full.', p_kind + USING ERRCODE = 'P0001'; + END IF; + + v_account_type := CASE WHEN p_kind = 'therapist' THEN 'therapist' ELSE 'clinic' END; + + IF EXISTS ( + SELECT 1 + FROM public.tenant_members tm + JOIN public.tenants t ON t.id = tm.tenant_id + WHERE tm.user_id = p_user_id + AND tm.role = 'tenant_admin' + AND tm.status = 'active' + AND t.kind = p_kind + ) THEN + RAISE EXCEPTION 'Usu??rio j?? possui um tenant do tipo "%".', p_kind + USING ERRCODE = 'P0001'; + END IF; + + v_name := COALESCE( + NULLIF(TRIM(p_name), ''), + ( + SELECT COALESCE(NULLIF(TRIM(pr.full_name), ''), SPLIT_PART(au.email, '@', 1)) + FROM public.profiles pr + JOIN auth.users au ON au.id = pr.id + WHERE pr.id = p_user_id + ), + 'Conta' + ); + + INSERT INTO public.tenants (name, kind, created_at) + VALUES (v_name, p_kind, now()) + RETURNING id INTO v_tenant_id; + + INSERT INTO public.tenant_members (tenant_id, user_id, role, status, created_at) + VALUES (v_tenant_id, p_user_id, 'tenant_admin', 'active', now()); + + UPDATE public.profiles + SET account_type = v_account_type + WHERE id = p_user_id; + + PERFORM public.seed_determined_commitments(v_tenant_id); + + RETURN v_tenant_id; +END; +$$; + + +-- +-- Name: FUNCTION provision_account_tenant(p_user_id uuid, p_kind text, p_name text); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) IS 'Cria o tenant do tipo correto e atualiza account_type no profile. Chamar no onboarding ap??s escolha/pagamento de plano therapist ou clinic. p_kind: therapist | clinic_coworking | clinic_reception | clinic_full'; + + +-- +-- Name: reactivate_subscription(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.reactivate_subscription(p_subscription_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_owner_type text; + v_owner_ref uuid; +begin + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + if v_sub.status = 'active' then + return v_sub; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set status = 'active', + cancel_at_period_end = false, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'reactivated', + v_sub.plan_id, + v_sub.plan_id, + auth.uid(), + 'Reativa????o manual via admin', + 'admin_panel', + jsonb_build_object('previous_status', 'canceled') + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation(owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; + +end; +$$; + + +-- +-- Name: rebuild_owner_entitlements(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_plan_id uuid; +begin + -- Plano ativo do owner (owner = subscriptions.user_id) + select s.plan_id + into v_plan_id + from public.subscriptions s + where s.user_id = p_owner_id + and s.status = 'active' + order by s.created_at desc + limit 1; + + -- Sempre zera entitlements do owner (rebuild) + delete from public.owner_feature_entitlements e + where e.owner_id = p_owner_id; + + -- Se n??o tem assinatura ativa, acabou + if v_plan_id is null then + return; + end if; + + -- Recria entitlements esperados pelo plano + insert into public.owner_feature_entitlements (owner_id, feature_key, sources, limits_list) + select + p_owner_id as owner_id, + f.key as feature_key, + array['plan'::text] as sources, + '{}'::jsonb as limits_list + from public.plan_features pf + join public.features f on f.id = pf.feature_id + where pf.plan_id = v_plan_id; + +end; +$$; + + +-- +-- Name: revoke_support_session(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.revoke_support_session(p_token text) RETURNS boolean + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_admin_id uuid; + v_role text; +BEGIN + v_admin_id := auth.uid(); + IF v_admin_id IS NULL THEN + RAISE EXCEPTION 'N??o autenticado.' USING ERRCODE = 'P0001'; + END IF; + + SELECT role INTO v_role FROM public.profiles WHERE id = v_admin_id; + IF v_role <> 'saas_admin' THEN + RAISE EXCEPTION 'Acesso negado.' USING ERRCODE = 'P0002'; + END IF; + + DELETE FROM public.support_sessions + WHERE token = p_token + AND admin_id = v_admin_id; + + RETURN FOUND; +END; +$$; + + +-- +-- Name: rotate_patient_invite_token(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.rotate_patient_invite_token(p_new_token text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_uid uuid; + v_id uuid; +begin + -- pega o usu??rio logado + v_uid := auth.uid(); + if v_uid is null then + raise exception 'Usu??rio n??o autenticado'; + end if; + + -- desativa tokens antigos ativos do usu??rio + update public.patient_invites + set active = false + where owner_id = v_uid + and active = true; + + -- cria novo token + insert into public.patient_invites (owner_id, token, active) + values (v_uid, p_new_token, true) + returning id into v_id; + + return v_id; +end; +$$; + + +-- +-- Name: saas_votar_doc(uuid, boolean); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_uid uuid := auth.uid(); + v_voto_antigo boolean; +begin + if v_uid is null then + raise exception 'N??o autenticado'; + end if; + + -- Verifica se j?? votou + select util into v_voto_antigo + from public.saas_doc_votos + where doc_id = p_doc_id and user_id = v_uid; + + if found then + -- J?? votou igual ??? cancela o voto (toggle) + if v_voto_antigo = p_util then + delete from public.saas_doc_votos + where doc_id = p_doc_id and user_id = v_uid; + + update public.saas_docs set + votos_util = greatest(0, votos_util - (case when p_util then 1 else 0 end)), + votos_nao_util = greatest(0, votos_nao_util - (case when not p_util then 1 else 0 end)), + updated_at = now() + where id = p_doc_id; + + return jsonb_build_object('acao', 'removido', 'util', null); + else + -- Mudou de voto + update public.saas_doc_votos set util = p_util, updated_at = now() + where doc_id = p_doc_id and user_id = v_uid; + + update public.saas_docs set + votos_util = greatest(0, votos_util + (case when p_util then 1 else -1 end)), + votos_nao_util = greatest(0, votos_nao_util + (case when not p_util then 1 else -1 end)), + updated_at = now() + where id = p_doc_id; + + return jsonb_build_object('acao', 'atualizado', 'util', p_util); + end if; + else + -- Primeiro voto + insert into public.saas_doc_votos (doc_id, user_id, util) + values (p_doc_id, v_uid, p_util); + + update public.saas_docs set + votos_util = votos_util + (case when p_util then 1 else 0 end), + votos_nao_util = votos_nao_util + (case when not p_util then 1 else 0 end), + updated_at = now() + where id = p_doc_id; + + return jsonb_build_object('acao', 'registrado', 'util', p_util); + end if; +end; +$$; + + +-- +-- Name: safe_delete_patient(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.safe_delete_patient(p_patient_id uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + -- Bloqueia se houver hist??rico + IF NOT public.can_delete_patient(p_patient_id) THEN + RETURN jsonb_build_object( + 'ok', false, + 'error', 'has_history', + 'message', 'Este paciente possui hist??rico cl??nico ou financeiro e n??o pode ser removido. Voc?? pode desativar ou arquivar o paciente.' + ); + END IF; + + -- Verifica ownership via RLS (owner_id ou responsible_member_id) + IF NOT EXISTS ( + SELECT 1 FROM public.patients + WHERE id = p_patient_id + AND ( + owner_id = auth.uid() + OR responsible_member_id IN ( + SELECT id FROM public.tenant_members WHERE user_id = auth.uid() + ) + ) + ) THEN + RETURN jsonb_build_object( + 'ok', false, + 'error', 'forbidden', + 'message', 'Sem permiss??o para excluir este paciente.' + ); + END IF; + + DELETE FROM public.patients WHERE id = p_patient_id; + + RETURN jsonb_build_object('ok', true); +END; +$$; + + +-- +-- Name: sanitize_phone_br(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.sanitize_phone_br(raw_phone text) RETURNS text + LANGUAGE plpgsql IMMUTABLE + AS $$ DECLARE digits text; + BEGIN + digits := regexp_replace(COALESCE(raw_phone, ''), '[^0-9]', '', 'g'); + IF digits = '' THEN RETURN ''; END IF; + IF length(digits) = 10 OR length(digits) = 11 THEN + digits := '55' || digits; + END IF; + RETURN digits; + END; $$; + + +-- +-- Name: seed_default_financial_categories(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.seed_default_financial_categories(p_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + INSERT INTO public.financial_categories (user_id, name, type, color, icon, sort_order) + VALUES + (p_user_id, 'Sess??o', 'receita', '#22c55e', 'pi pi-heart', 1), + (p_user_id, 'Supervis??o', 'receita', '#6366f1', 'pi pi-users', 2), + (p_user_id, 'Conv??nio', 'receita', '#3b82f6', 'pi pi-building', 3), + (p_user_id, 'Grupo terap??utico', 'receita', '#f59e0b', 'pi pi-sitemap', 4), + (p_user_id, 'Outro (receita)', 'receita', '#8b5cf6', 'pi pi-plus-circle', 5), + (p_user_id, 'Aluguel sala', 'despesa', '#ef4444', 'pi pi-home', 1), + (p_user_id, 'Plataforma/SaaS', 'despesa', '#f97316', 'pi pi-desktop', 2), + (p_user_id, 'Repasse cl??nica', 'despesa', '#64748b', 'pi pi-arrow-right-arrow-left', 3), + (p_user_id, 'Supervis??o (custo)', 'despesa', '#6366f1', 'pi pi-users', 4), + (p_user_id, 'Outro (despesa)', 'despesa', '#94a3b8', 'pi pi-minus-circle', 5) + ON CONFLICT DO NOTHING; +END; +$$; + + +-- +-- Name: seed_default_patient_groups(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.seed_default_patient_groups(p_tenant_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_owner_id uuid; +BEGIN + -- busca o owner (tenant_admin) do tenant + SELECT user_id INTO v_owner_id + FROM public.tenant_members + WHERE tenant_id = p_tenant_id + AND role = 'tenant_admin' + AND status = 'active' + LIMIT 1; + + IF v_owner_id IS NULL THEN + RETURN; + END IF; + + INSERT INTO public.patient_groups (owner_id, nome, cor, is_system, tenant_id) + VALUES + (v_owner_id, 'Crian??as', '#60a5fa', true, p_tenant_id), + (v_owner_id, 'Adolescentes', '#a78bfa', true, p_tenant_id), + (v_owner_id, 'Idosos', '#34d399', true, p_tenant_id) + ON CONFLICT (owner_id, nome) DO NOTHING; +END; +$$; + + +-- +-- Name: seed_determined_commitments(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.seed_determined_commitments(p_tenant_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_id uuid; +begin + -- Sess??o (locked + sempre ativa) + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'session' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'session', true, true, 'Sess??o', 'Sess??o com paciente'); + end if; + + -- Leitura + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'reading' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'reading', false, true, 'Leitura', 'Praticar leitura'); + end if; + + -- Supervis??o + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'supervision' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'supervision', false, true, 'Supervis??o', 'Supervis??o'); + end if; + + -- Aula ??? (corrigido) + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'class' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'class', false, false, 'Aula', 'Dar aula'); + end if; + + -- An??lise pessoal + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'analysis' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'analysis', false, true, 'An??lise Pessoal', 'Minha an??lise pessoal'); + end if; + + -- ------------------------------------------------------- + -- Campos padr??o (idempotentes por (commitment_id, key)) + -- ------------------------------------------------------- + + -- Leitura + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'reading' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'book') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'book', 'Livro', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'author') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'author', 'Autor', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; + + -- Supervis??o + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'supervision' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'supervisor') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'supervisor', 'Supervisor', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'topic') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'topic', 'Assunto', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; + + -- Aula + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'class' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'theme') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'theme', 'Tema', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'group') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'group', 'Turma', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; + + -- An??lise + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'analysis' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'analyst') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'analyst', 'Analista', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'focus') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'focus', 'Foco', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; +end; +$$; + + +-- +-- Name: set_insurance_plans_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_insurance_plans_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN NEW.updated_at = now(); RETURN NEW; END; $$; + + +-- +-- Name: set_owner_id(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_owner_id() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if new.owner_id is null then + new.owner_id := auth.uid(); + end if; + return new; +end; +$$; + + +-- +-- Name: set_services_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_services_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: set_tenant_feature_exception(uuid, text, boolean, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text DEFAULT NULL::text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +begin + -- ??? S?? owner ou admin do tenant podem alterar features + if not exists ( + select 1 from public.tenant_members + where tenant_id = p_tenant_id + and user_id = auth.uid() + and role in ('owner', 'admin') + and status = 'active' + ) then + raise exception 'Acesso negado: apenas owner/admin pode alterar features do tenant.'; + end if; + + insert into public.tenant_features (tenant_id, feature_key, enabled) + values (p_tenant_id, p_feature_key, p_enabled) + on conflict (tenant_id, feature_key) + do update set enabled = excluded.enabled; + + insert into public.tenant_feature_exceptions_log ( + tenant_id, feature_key, enabled, reason, created_by + ) values ( + p_tenant_id, p_feature_key, p_enabled, p_reason, auth.uid() + ); +end; +$$; + + +-- +-- Name: set_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: set_updated_at_recurrence(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_updated_at_recurrence() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN NEW.updated_at = now(); RETURN NEW; END; +$$; + + +-- +-- Name: split_recurrence_at(uuid, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.split_recurrence_at(p_recurrence_id uuid, p_from_date date) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_old public.recurrence_rules; + v_new_id uuid; +BEGIN + -- busca a regra original + SELECT * INTO v_old + FROM public.recurrence_rules + WHERE id = p_recurrence_id; + + IF NOT FOUND THEN + RAISE EXCEPTION 'recurrence_rule % n??o encontrada', p_recurrence_id; + END IF; + + -- encerra a regra antiga na data anterior + UPDATE public.recurrence_rules + SET + end_date = p_from_date - INTERVAL '1 day', + open_ended = false, + updated_at = now() + WHERE id = p_recurrence_id; + + -- cria nova regra a partir de p_from_date + INSERT INTO public.recurrence_rules ( + tenant_id, owner_id, therapist_id, patient_id, + determined_commitment_id, type, interval, weekdays, + start_time, end_time, timezone, duration_min, + start_date, end_date, max_occurrences, open_ended, + modalidade, titulo_custom, observacoes, extra_fields, status + ) + SELECT + tenant_id, owner_id, therapist_id, patient_id, + determined_commitment_id, type, interval, weekdays, + start_time, end_time, timezone, duration_min, + p_from_date, v_old.end_date, v_old.max_occurrences, v_old.open_ended, + modalidade, titulo_custom, observacoes, extra_fields, status + FROM public.recurrence_rules + WHERE id = p_recurrence_id + RETURNING id INTO v_new_id; + + RETURN v_new_id; +END; +$$; + + +-- +-- Name: subscription_intents_view_insert(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.subscription_intents_view_insert() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_target text; + v_plan_id uuid; +begin + select p.id, p.target into v_plan_id, v_target + from public.plans p + where p.key = new.plan_key; + + if v_plan_id is null then + raise exception 'Plano inv??lido: plan_key=%', new.plan_key; + end if; + + if lower(v_target) = 'clinic' then + if new.tenant_id is null then + raise exception 'Inten????o clinic exige tenant_id.'; + end if; + + insert into public.subscription_intents_tenant ( + id, tenant_id, created_by_user_id, email, + plan_id, plan_key, interval, amount_cents, currency, + status, source, notes, created_at, paid_at + ) values ( + coalesce(new.id, gen_random_uuid()), + new.tenant_id, new.created_by_user_id, new.email, + v_plan_id, new.plan_key, coalesce(new.interval,'month'), + new.amount_cents, coalesce(new.currency,'BRL'), + coalesce(new.status,'pending'), coalesce(new.source,'manual'), + new.notes, coalesce(new.created_at, now()), new.paid_at + ); + + new.plan_target := 'clinic'; + return new; + end if; + + -- therapist ou supervisor ??? tabela personal + if lower(v_target) in ('therapist', 'supervisor') then + insert into public.subscription_intents_personal ( + id, user_id, created_by_user_id, email, + plan_id, plan_key, interval, amount_cents, currency, + status, source, notes, created_at, paid_at + ) values ( + coalesce(new.id, gen_random_uuid()), + new.user_id, new.created_by_user_id, new.email, + v_plan_id, new.plan_key, coalesce(new.interval,'month'), + new.amount_cents, coalesce(new.currency,'BRL'), + coalesce(new.status,'pending'), coalesce(new.source,'manual'), + new.notes, coalesce(new.created_at, now()), new.paid_at + ); + + new.plan_target := lower(v_target); -- 'therapist' ou 'supervisor' + return new; + end if; + + raise exception 'Target de plano n??o suportado: %', v_target; +end; +$$; + + +-- +-- Name: subscriptions_validate_scope(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.subscriptions_validate_scope() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_target text; +BEGIN + SELECT lower(p.target) INTO v_target + FROM public.plans p + WHERE p.id = NEW.plan_id; + + IF v_target IS NULL THEN + RAISE EXCEPTION 'Plano inv??lido (target nulo).'; + END IF; + + IF v_target = 'clinic' THEN + IF NEW.tenant_id IS NULL THEN + RAISE EXCEPTION 'Assinatura clinic exige tenant_id.'; + END IF; + IF NEW.user_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura clinic n??o pode ter user_id (XOR).'; + END IF; + + ELSIF v_target IN ('therapist', 'supervisor') THEN + -- supervisor ?? pessoal como therapist + IF NEW.tenant_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura % n??o deve ter tenant_id.', v_target; + END IF; + IF NEW.user_id IS NULL THEN + RAISE EXCEPTION 'Assinatura % exige user_id.', v_target; + END IF; + + ELSIF v_target = 'patient' THEN + IF NEW.tenant_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura patient n??o deve ter tenant_id.'; + END IF; + IF NEW.user_id IS NULL THEN + RAISE EXCEPTION 'Assinatura patient exige user_id.'; + END IF; + + ELSE + RAISE EXCEPTION 'Target de plano inv??lido: %', v_target; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: sync_busy_mirror_agenda_eventos(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.sync_busy_mirror_agenda_eventos() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + clinic_tenant uuid; + is_personal boolean; + should_mirror boolean; +begin + -- Anti-recurs??o: espelho n??o espelha + if (tg_op <> 'DELETE') then + if new.mirror_of_event_id is not null then + return new; + end if; + else + if old.mirror_of_event_id is not null then + return old; + end if; + end if; + + -- Define se ?? pessoal e se deve espelhar + if (tg_op = 'DELETE') then + is_personal := (old.tenant_id = old.owner_id); + should_mirror := (old.visibility_scope in ('busy_only','private')); + else + is_personal := (new.tenant_id = new.owner_id); + should_mirror := (new.visibility_scope in ('busy_only','private')); + end if; + + -- Se n??o ?? pessoal, n??o faz nada + if not is_personal then + if (tg_op = 'DELETE') then + return old; + end if; + return new; + end if; + + -- DELETE: remove espelhos existentes + if (tg_op = 'DELETE') then + delete from public.agenda_eventos e + where e.mirror_of_event_id = old.id + and e.mirror_source = 'personal_busy_mirror'; + + return old; + end if; + + -- INSERT/UPDATE: + -- Se n??o deve espelhar, remove espelhos e sai + if not should_mirror then + delete from public.agenda_eventos e + where e.mirror_of_event_id = new.id + and e.mirror_source = 'personal_busy_mirror'; + + return new; + end if; + + -- Para cada cl??nica onde o usu??rio ?? therapist active, cria/atualiza o "Ocupado" + for clinic_tenant in + select tm.tenant_id + from public.tenant_members tm + where tm.user_id = new.owner_id + and tm.role = 'therapist' + and tm.status = 'active' + and tm.tenant_id <> new.owner_id + loop + insert into public.agenda_eventos ( + tenant_id, + owner_id, + terapeuta_id, + paciente_id, + tipo, + status, + titulo, + observacoes, + inicio_em, + fim_em, + mirror_of_event_id, + mirror_source, + visibility_scope, + created_at, + updated_at + ) values ( + clinic_tenant, + new.owner_id, + new.owner_id, + null, + 'bloqueio'::public.tipo_evento_agenda, + 'agendado'::public.status_evento_agenda, + 'Ocupado', + null, + new.inicio_em, + new.fim_em, + new.id, + 'personal_busy_mirror', + 'public', + now(), + now() + ) + on conflict (tenant_id, mirror_of_event_id) where mirror_of_event_id is not null + do update set + owner_id = excluded.owner_id, + terapeuta_id = excluded.terapeuta_id, + tipo = excluded.tipo, + status = excluded.status, + titulo = excluded.titulo, + observacoes = excluded.observacoes, + inicio_em = excluded.inicio_em, + fim_em = excluded.fim_em, + updated_at = now(); + end loop; + + -- Limpa espelhos de cl??nicas onde o v??nculo therapist active n??o existe mais + delete from public.agenda_eventos e + where e.mirror_of_event_id = new.id + and e.mirror_source = 'personal_busy_mirror' + and not exists ( + select 1 + from public.tenant_members tm + where tm.user_id = new.owner_id + and tm.role = 'therapist' + and tm.status = 'active' + and tm.tenant_id = e.tenant_id + ); + + return new; +end; +$$; + + +-- +-- Name: sync_overdue_financial_records(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.sync_overdue_financial_records() RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_count integer; +BEGIN + UPDATE public.financial_records + SET + status = 'overdue', + updated_at = NOW() + WHERE status = 'pending' + AND due_date IS NOT NULL + AND due_date < CURRENT_DATE + AND deleted_at IS NULL; + + GET DIAGNOSTICS v_count = ROW_COUNT; + RETURN v_count; +END; +$$; + + +-- +-- Name: FUNCTION sync_overdue_financial_records(); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.sync_overdue_financial_records() IS 'Marca como overdue todos os financial_records pendentes com due_date vencido. Pode ser chamada manualmente, via pg_cron ou via Supabase Edge Function agendada.'; + + +-- +-- Name: tenant_accept_invite(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_accept_invite(p_token uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_uid uuid; + v_email text; + v_invite public.tenant_invites%rowtype; +begin + -- 1) precisa estar autenticado + v_uid := auth.uid(); + if v_uid is null then + raise exception 'not_authenticated' using errcode = 'P0001'; + end if; + + -- 2) pega email real do usu??rio logado sem depender do JWT claim + select u.email + into v_email + from auth.users u + where u.id = v_uid; + + if v_email is null or length(trim(v_email)) = 0 then + raise exception 'missing_user_email' using errcode = 'P0001'; + end if; + + -- 3) carrega o invite e trava linha (evita 2 aceites concorrentes) + select * + into v_invite + from public.tenant_invites i + where i.token = p_token + for update; + + if not found then + raise exception 'invite_not_found' using errcode = 'P0001'; + end if; + + -- 4) valida????es de estado + if v_invite.revoked_at is not null then + raise exception 'invite_revoked' using errcode = 'P0001'; + end if; + + if v_invite.accepted_at is not null then + raise exception 'invite_already_accepted' using errcode = 'P0001'; + end if; + + if v_invite.expires_at is not null and v_invite.expires_at <= now() then + raise exception 'invite_expired' using errcode = 'P0001'; + end if; + + -- 5) valida email (case-insensitive) + if lower(trim(v_invite.email)) <> lower(trim(v_email)) then + raise exception 'email_mismatch' using errcode = 'P0001'; + end if; + + -- 6) consome o invite + update public.tenant_invites + set accepted_at = now(), + accepted_by = v_uid + where id = v_invite.id; + + -- 7) cria ou reativa o membership + insert into public.tenant_members (tenant_id, user_id, role, status, created_at) + values (v_invite.tenant_id, v_uid, v_invite.role, 'active', now()) + on conflict (tenant_id, user_id) + do update set + role = excluded.role, + status = 'active'; + + -- 8) retorno ??til pro front (voc?? j?? tenta ler tenant_id no AcceptInvitePage) + return jsonb_build_object( + 'ok', true, + 'tenant_id', v_invite.tenant_id, + 'role', v_invite.role + ); +end; +$$; + + +-- +-- Name: tenant_members; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_members ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + user_id uuid NOT NULL, + role text NOT NULL, + status text DEFAULT 'active'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_add_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text DEFAULT 'therapist'::text) RETURNS public.tenant_members + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_target_uid uuid; + v_member public.tenant_members%rowtype; + v_is_admin boolean; + v_email text; +begin + if p_tenant_id is null then + raise exception 'tenant_id ?? obrigat??rio'; + end if; + + v_email := lower(trim(coalesce(p_email, ''))); + if v_email = '' then + raise exception 'email ?? obrigat??rio'; + end if; + + -- valida role permitida + if p_role not in ('tenant_admin','therapist','secretary','patient') then + raise exception 'role inv??lida: %', p_role; + end if; + + -- apenas admin do tenant (role real no banco) + select exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.role = 'tenant_admin' + and coalesce(tm.status,'active') = 'active' + ) into v_is_admin; + + if not v_is_admin then + raise exception 'sem permiss??o: apenas admin da cl??nica pode adicionar membros'; + end if; + + -- acha usu??rio pelo e-mail no Supabase Auth + select u.id + into v_target_uid + from auth.users u + where lower(u.email) = v_email + limit 1; + + if v_target_uid is null then + raise exception 'nenhum usu??rio encontrado com este e-mail'; + end if; + + -- cria ou reativa membro + insert into public.tenant_members (tenant_id, user_id, role, status) + values (p_tenant_id, v_target_uid, p_role, 'active') + on conflict (tenant_id, user_id) + do update set + role = excluded.role, + status = 'active' + returning * into v_member; + + return v_member; +end; +$$; + + +-- +-- Name: tenant_feature_allowed(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.v_tenant_entitlements v + where v.tenant_id = p_tenant_id + and v.feature_key = p_feature_key + and coalesce(v.allowed, false) = true + ); +$$; + + +-- +-- Name: tenant_feature_enabled(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select coalesce( + (select tf.enabled + from public.tenant_features tf + where tf.tenant_id = p_tenant_id and tf.feature_key = p_feature_key), + false + ); +$$; + + +-- +-- Name: tenant_features_guard_with_plan(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_features_guard_with_plan() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_allowed boolean; +begin + -- s?? valida quando est?? habilitando + if new.enabled is distinct from true then + return new; + end if; + + -- permitido pelo plano do tenant? + select exists ( + select 1 + from public.v_tenant_entitlements_full v + where v.tenant_id = new.tenant_id + and v.feature_key = new.feature_key + and v.allowed = true + ) + into v_allowed; + + if not v_allowed then + raise exception 'Feature % n??o permitida pelo plano atual do tenant %.', + new.feature_key, new.tenant_id + using errcode = 'P0001'; + end if; + + return new; +end; +$$; + + +-- +-- Name: tenant_has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select + exists ( + select 1 + from public.v_tenant_entitlements e + where e.tenant_id = _tenant_id + and e.feature_key = _feature + and e.allowed = true + ) + or exists ( + select 1 + from public.tenant_features tf + where tf.tenant_id = _tenant_id + and tf.feature_key = _feature + and tf.enabled = true + ); +$$; + + +-- +-- Name: tenant_invite_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_email text; + v_my_email text; + v_token uuid; + v_updated int; +begin + -- valida????es b??sicas + if p_tenant_id is null then + raise exception 'tenant_id inv??lido' using errcode = 'P0001'; + end if; + + v_email := lower(trim(coalesce(p_email, ''))); + if v_email = '' then + raise exception 'Informe um email' using errcode = 'P0001'; + end if; + + -- role permitido (ajuste se quiser) + if p_role is null or p_role not in ('therapist', 'secretary') then + raise exception 'Role inv??lido (use therapist/secretary)' using errcode = 'P0001'; + end if; + + -- ??? bloqueio: auto-convite + v_my_email := public.get_my_email(); + if v_my_email is not null and v_email = v_my_email then + raise exception 'Voc?? n??o pode convidar o seu pr??prio email.' using errcode = 'P0001'; + end if; + + -- ??? bloqueio: j?? ?? membro ativo do tenant + if exists ( + select 1 + from tenant_members tm + join auth.users au on au.id = tm.user_id + where tm.tenant_id = p_tenant_id + and tm.status = 'active' + and lower(au.email) = v_email + ) then + raise exception 'Este email j?? est?? vinculado a esta cl??nica.' using errcode = 'P0001'; + end if; + + -- ??? permiss??o: s?? admin do tenant pode convidar + if not exists ( + select 1 + from tenant_members me + where me.tenant_id = p_tenant_id + and me.user_id = auth.uid() + and me.status = 'active' + and me.role in ('tenant_admin','clinic_admin') + ) then + raise exception 'Sem permiss??o para convidar membros.' using errcode = 'P0001'; + end if; + + -- Gera token (reenvio simples / regenera????o) + v_token := gen_random_uuid(); + + -- 1) tenta "regerar" um convite pendente existente (mesmo email) + update tenant_invites + set token = v_token, + role = p_role, + created_at = now(), + expires_at = now() + interval '7 days', + accepted_at = null, + revoked_at = null + where tenant_id = p_tenant_id + and lower(email) = v_email + and accepted_at is null + and revoked_at is null; + + get diagnostics v_updated = row_count; + + -- 2) se n??o atualizou nada, cria convite novo + if v_updated = 0 then + insert into tenant_invites (tenant_id, email, role, token, created_at, expires_at) + values (p_tenant_id, v_email, p_role, v_token, now(), now() + interval '7 days'); + end if; + + return v_token; +end; +$$; + + +-- +-- Name: tenant_reactivate_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + update public.tenant_members + set status = 'active' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_remove_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +declare + v_role text; +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + if p_member_user_id = auth.uid() then + raise exception 'cannot_remove_self'; + end if; + + -- pega role atual do membro (se n??o existir, erro) + select role into v_role + from public.tenant_members + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if v_role is null then + raise exception 'membership_not_found'; + end if; + + -- trava: se for therapist, n??o pode remover com eventos futuros + if v_role = 'therapist' then + if exists ( + select 1 + from public.agenda_eventos e + where e.owner_id = p_tenant_id + and e.terapeuta_id = p_member_user_id + and e.inicio_em >= now() + and e.status::text not in ('cancelado','cancelled','canceled') + limit 1 + ) then + raise exception 'cannot_remove_therapist_with_future_events'; + end if; + end if; + + update public.tenant_members + set status = 'inactive' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_remove_member_soft(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + if p_member_user_id = auth.uid() then + raise exception 'cannot_remove_self'; + end if; + + update public.tenant_members + set status = 'inactive' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_revoke_invite(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +declare + v_email text; +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + v_email := lower(trim(p_email)); + + update public.tenant_invites + set revoked_at = now(), + revoked_by = auth.uid() + where tenant_id = p_tenant_id + and lower(email) = v_email + and role = p_role + and accepted_at is null + and revoked_at is null; + + if not found then + raise exception 'invite_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_set_member_status(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + -- valida status (adapte aos seus valores reais) + if p_new_status not in ('active','inactive','suspended','invited') then + raise exception 'invalid_status: %', p_new_status; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + -- evita desativar a si mesmo (opcional) + if p_member_user_id = auth.uid() and p_new_status <> 'active' then + raise exception 'cannot_disable_self'; + end if; + + update public.tenant_members + set status = p_new_status + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_update_member_role(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + -- exige auth + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + -- valida role + if p_new_role not in ('tenant_admin','therapist','secretary','patient') then + raise exception 'invalid_role: %', p_new_role; + end if; + + -- somente tenant_admin ativo pode alterar role + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + -- evita o admin remover o pr??prio admin sem querer (opcional mas recomendado) + if p_member_user_id = auth.uid() and p_new_role <> 'tenant_admin' then + raise exception 'cannot_demote_self'; + end if; + + update public.tenant_members + set role = p_new_role + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: toggle_plan(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.toggle_plan(owner uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + current_key text; + new_key text; +begin + select p.key into current_key + from subscriptions s + join plans p on p.id = s.plan_id + where s.owner_id = owner + and s.status = 'active'; + + new_key := case + when current_key = 'pro' then 'free' + else 'pro' + end; + + update subscriptions s + set plan_id = p.id + from plans p + where p.key = new_key + and s.owner_id = owner + and s.status = 'active'; +end; +$$; + + +-- +-- Name: transition_subscription(uuid, text, text, jsonb); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text DEFAULT NULL::text, p_metadata jsonb DEFAULT NULL::jsonb) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_uid uuid; + v_is_allowed boolean := false; +begin + v_uid := auth.uid(); + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id; + + if not found then + raise exception 'Assinatura n??o encontrada'; + end if; + + -- ===================================================== + -- ???? BLOCO DE AUTORIZA????O + -- ===================================================== + + -- 1) SaaS admin pode tudo + if is_saas_admin() then + v_is_allowed := true; + end if; + + -- 2) Assinatura pessoal (therapist) + if not v_is_allowed + and v_sub.tenant_id is null + and v_sub.user_id = v_uid then + v_is_allowed := true; + end if; + + -- 3) Assinatura de clinic (tenant) + if not v_is_allowed + and v_sub.tenant_id is not null then + + if exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = v_sub.tenant_id + and tm.user_id = v_uid + and tm.status = 'active' + and tm.role = 'tenant_admin' + ) then + v_is_allowed := true; + end if; + + end if; + + if not v_is_allowed then + raise exception 'Sem permiss??o para transicionar esta assinatura'; + end if; + + -- ===================================================== + -- ???? TRANSI????O + -- ===================================================== + + update public.subscriptions + set status = p_to_status, + updated_at = now(), + cancelled_at = case when p_to_status = 'cancelled' then now() else cancelled_at end, + suspended_at = case when p_to_status = 'suspended' then now() else suspended_at end, + past_due_since = case when p_to_status = 'past_due' then now() else past_due_since end, + expired_at = case when p_to_status = 'expired' then now() else expired_at end, + activated_at = case when p_to_status = 'active' then now() else activated_at end + where id = p_subscription_id + returning * into v_sub; + + -- ===================================================== + -- ???? EVENT LOG + -- ===================================================== + + insert into public.subscription_events ( + subscription_id, + owner_id, + event_type, + created_at, + created_by, + source, + reason, + metadata, + owner_type, + owner_ref + ) + values ( + v_sub.id, + coalesce(v_sub.tenant_id, v_sub.user_id), + 'status_changed', + now(), + v_uid, + 'manual_transition', + p_reason, + p_metadata, + case when v_sub.tenant_id is not null then 'tenant' else 'personal' end, + coalesce(v_sub.tenant_id, v_sub.user_id) + ); + + return v_sub; +end; +$$; + + +-- +-- Name: trg_fn_financial_records_auto_overdue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.trg_fn_financial_records_auto_overdue() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + IF NEW.status = 'pending' + AND NEW.due_date IS NOT NULL + AND NEW.due_date < CURRENT_DATE + THEN + NEW.status := 'overdue'; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: unstick_notification_queue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.unstick_notification_queue() RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_unstuck integer; +BEGIN + UPDATE public.notification_queue + SET status = 'pendente', + attempts = attempts + 1, + last_error = 'Timeout: preso em processando por >10min', + next_retry_at = now() + interval '2 minutes' + WHERE status = 'processando' + AND updated_at < now() - interval '10 minutes'; + + GET DIAGNOSTICS v_unstuck = ROW_COUNT; + RETURN v_unstuck; +END; +$$; + + +-- +-- Name: update_payment_settings_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.update_payment_settings_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: update_professional_pricing_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.update_professional_pricing_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: user_has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.user_has_feature(_user_id uuid, _feature text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.v_user_entitlements e + where e.user_id = _user_id + and e.feature_key = _feature + and e.allowed = true + ); +$$; + + +-- +-- Name: validate_support_session(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.validate_support_session(p_token text) RETURNS json + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_session support_sessions; +BEGIN + IF p_token IS NULL OR length(trim(p_token)) < 32 THEN + RETURN json_build_object('valid', false, 'tenant_id', null); + END IF; + + SELECT * INTO v_session + FROM public.support_sessions + WHERE token = p_token + AND expires_at > now() + LIMIT 1; + + IF NOT FOUND THEN + RETURN json_build_object('valid', false, 'tenant_id', null); + END IF; + + RETURN json_build_object( + 'valid', true, + 'tenant_id', v_session.tenant_id + ); +END; +$$; + + +-- +-- Name: whoami(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.whoami() RETURNS TABLE(uid uuid, role text) + LANGUAGE sql STABLE + AS $$ + select auth.uid() as uid, auth.role() as role; +$$; + + +-- +-- Name: apply_rls(jsonb, integer); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer DEFAULT (1024 * 1024)) RETURNS SETOF realtime.wal_rls + LANGUAGE plpgsql + AS $$ +declare +-- Regclass of the table e.g. public.notes +entity_ regclass = (quote_ident(wal ->> 'schema') || '.' || quote_ident(wal ->> 'table'))::regclass; + +-- I, U, D, T: insert, update ... +action realtime.action = ( + case wal ->> 'action' + when 'I' then 'INSERT' + when 'U' then 'UPDATE' + when 'D' then 'DELETE' + else 'ERROR' + end +); + +-- Is row level security enabled for the table +is_rls_enabled bool = relrowsecurity from pg_class where oid = entity_; + +subscriptions realtime.subscription[] = array_agg(subs) + from + realtime.subscription subs + where + subs.entity = entity_; + +-- Subscription vars +roles regrole[] = array_agg(distinct us.claims_role::text) + from + unnest(subscriptions) us; + +working_role regrole; +claimed_role regrole; +claims jsonb; + +subscription_id uuid; +subscription_has_access bool; +visible_to_subscription_ids uuid[] = '{}'; + +-- structured info for wal's columns +columns realtime.wal_column[]; +-- previous identity values for update/delete +old_columns realtime.wal_column[]; + +error_record_exceeds_max_size boolean = octet_length(wal::text) > max_record_bytes; + +-- Primary jsonb output for record +output jsonb; + +begin +perform set_config('role', null, true); + +columns = + array_agg( + ( + x->>'name', + x->>'type', + x->>'typeoid', + realtime.cast( + (x->'value') #>> '{}', + coalesce( + (x->>'typeoid')::regtype, -- null when wal2json version <= 2.4 + (x->>'type')::regtype + ) + ), + (pks ->> 'name') is not null, + true + )::realtime.wal_column + ) + from + jsonb_array_elements(wal -> 'columns') x + left join jsonb_array_elements(wal -> 'pk') pks + on (x ->> 'name') = (pks ->> 'name'); + +old_columns = + array_agg( + ( + x->>'name', + x->>'type', + x->>'typeoid', + realtime.cast( + (x->'value') #>> '{}', + coalesce( + (x->>'typeoid')::regtype, -- null when wal2json version <= 2.4 + (x->>'type')::regtype + ) + ), + (pks ->> 'name') is not null, + true + )::realtime.wal_column + ) + from + jsonb_array_elements(wal -> 'identity') x + left join jsonb_array_elements(wal -> 'pk') pks + on (x ->> 'name') = (pks ->> 'name'); + +for working_role in select * from unnest(roles) loop + + -- Update `is_selectable` for columns and old_columns + columns = + array_agg( + ( + c.name, + c.type_name, + c.type_oid, + c.value, + c.is_pkey, + pg_catalog.has_column_privilege(working_role, entity_, c.name, 'SELECT') + )::realtime.wal_column + ) + from + unnest(columns) c; + + old_columns = + array_agg( + ( + c.name, + c.type_name, + c.type_oid, + c.value, + c.is_pkey, + pg_catalog.has_column_privilege(working_role, entity_, c.name, 'SELECT') + )::realtime.wal_column + ) + from + unnest(old_columns) c; + + if action <> 'DELETE' and count(1) = 0 from unnest(columns) c where c.is_pkey then + return next ( + jsonb_build_object( + 'schema', wal ->> 'schema', + 'table', wal ->> 'table', + 'type', action + ), + is_rls_enabled, + -- subscriptions is already filtered by entity + (select array_agg(s.subscription_id) from unnest(subscriptions) as s where claims_role = working_role), + array['Error 400: Bad Request, no primary key'] + )::realtime.wal_rls; + + -- The claims role does not have SELECT permission to the primary key of entity + elsif action <> 'DELETE' and sum(c.is_selectable::int) <> count(1) from unnest(columns) c where c.is_pkey then + return next ( + jsonb_build_object( + 'schema', wal ->> 'schema', + 'table', wal ->> 'table', + 'type', action + ), + is_rls_enabled, + (select array_agg(s.subscription_id) from unnest(subscriptions) as s where claims_role = working_role), + array['Error 401: Unauthorized'] + )::realtime.wal_rls; + + else + output = jsonb_build_object( + 'schema', wal ->> 'schema', + 'table', wal ->> 'table', + 'type', action, + 'commit_timestamp', to_char( + ((wal ->> 'timestamp')::timestamptz at time zone 'utc'), + 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"' + ), + 'columns', ( + select + jsonb_agg( + jsonb_build_object( + 'name', pa.attname, + 'type', pt.typname + ) + order by pa.attnum asc + ) + from + pg_attribute pa + join pg_type pt + on pa.atttypid = pt.oid + where + attrelid = entity_ + and attnum > 0 + and pg_catalog.has_column_privilege(working_role, entity_, pa.attname, 'SELECT') + ) + ) + -- Add "record" key for insert and update + || case + when action in ('INSERT', 'UPDATE') then + jsonb_build_object( + 'record', + ( + select + jsonb_object_agg( + -- if unchanged toast, get column name and value from old record + coalesce((c).name, (oc).name), + case + when (c).name is null then (oc).value + else (c).value + end + ) + from + unnest(columns) c + full outer join unnest(old_columns) oc + on (c).name = (oc).name + where + coalesce((c).is_selectable, (oc).is_selectable) + and ( not error_record_exceeds_max_size or (octet_length((c).value::text) <= 64)) + ) + ) + else '{}'::jsonb + end + -- Add "old_record" key for update and delete + || case + when action = 'UPDATE' then + jsonb_build_object( + 'old_record', + ( + select jsonb_object_agg((c).name, (c).value) + from unnest(old_columns) c + where + (c).is_selectable + and ( not error_record_exceeds_max_size or (octet_length((c).value::text) <= 64)) + ) + ) + when action = 'DELETE' then + jsonb_build_object( + 'old_record', + ( + select jsonb_object_agg((c).name, (c).value) + from unnest(old_columns) c + where + (c).is_selectable + and ( not error_record_exceeds_max_size or (octet_length((c).value::text) <= 64)) + and ( not is_rls_enabled or (c).is_pkey ) -- if RLS enabled, we can't secure deletes so filter to pkey + ) + ) + else '{}'::jsonb + end; + + -- Create the prepared statement + if is_rls_enabled and action <> 'DELETE' then + if (select 1 from pg_prepared_statements where name = 'walrus_rls_stmt' limit 1) > 0 then + deallocate walrus_rls_stmt; + end if; + execute realtime.build_prepared_statement_sql('walrus_rls_stmt', entity_, columns); + end if; + + visible_to_subscription_ids = '{}'; + + for subscription_id, claims in ( + select + subs.subscription_id, + subs.claims + from + unnest(subscriptions) subs + where + subs.entity = entity_ + and subs.claims_role = working_role + and ( + realtime.is_visible_through_filters(columns, subs.filters) + or ( + action = 'DELETE' + and realtime.is_visible_through_filters(old_columns, subs.filters) + ) + ) + ) loop + + if not is_rls_enabled or action = 'DELETE' then + visible_to_subscription_ids = visible_to_subscription_ids || subscription_id; + else + -- Check if RLS allows the role to see the record + perform + -- Trim leading and trailing quotes from working_role because set_config + -- doesn't recognize the role as valid if they are included + set_config('role', trim(both '"' from working_role::text), true), + set_config('request.jwt.claims', claims::text, true); + + execute 'execute walrus_rls_stmt' into subscription_has_access; + + if subscription_has_access then + visible_to_subscription_ids = visible_to_subscription_ids || subscription_id; + end if; + end if; + end loop; + + perform set_config('role', null, true); + + return next ( + output, + is_rls_enabled, + visible_to_subscription_ids, + case + when error_record_exceeds_max_size then array['Error 413: Payload Too Large'] + else '{}' + end + )::realtime.wal_rls; + + end if; +end loop; + +perform set_config('role', null, true); +end; +$$; + + +-- +-- Name: broadcast_changes(text, text, text, text, text, record, record, text); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.broadcast_changes(topic_name text, event_name text, operation text, table_name text, table_schema text, new record, old record, level text DEFAULT 'ROW'::text) RETURNS void + LANGUAGE plpgsql + AS $$ +DECLARE + -- Declare a variable to hold the JSONB representation of the row + row_data jsonb := '{}'::jsonb; +BEGIN + IF level = 'STATEMENT' THEN + RAISE EXCEPTION 'function can only be triggered for each row, not for each statement'; + END IF; + -- Check the operation type and handle accordingly + IF operation = 'INSERT' OR operation = 'UPDATE' OR operation = 'DELETE' THEN + row_data := jsonb_build_object('old_record', OLD, 'record', NEW, 'operation', operation, 'table', table_name, 'schema', table_schema); + PERFORM realtime.send (row_data, event_name, topic_name); + ELSE + RAISE EXCEPTION 'Unexpected operation type: %', operation; + END IF; +EXCEPTION + WHEN OTHERS THEN + RAISE EXCEPTION 'Failed to process the row: %', SQLERRM; +END; + +$$; + + +-- +-- Name: build_prepared_statement_sql(text, regclass, realtime.wal_column[]); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) RETURNS text + LANGUAGE sql + AS $$ + /* + Builds a sql string that, if executed, creates a prepared statement to + tests retrive a row from *entity* by its primary key columns. + Example + select realtime.build_prepared_statement_sql('public.notes', '{"id"}'::text[], '{"bigint"}'::text[]) + */ + select + 'prepare ' || prepared_statement_name || ' as + select + exists( + select + 1 + from + ' || entity || ' + where + ' || string_agg(quote_ident(pkc.name) || '=' || quote_nullable(pkc.value #>> '{}') , ' and ') || ' + )' + from + unnest(columns) pkc + where + pkc.is_pkey + group by + entity + $$; + + +-- +-- Name: cast(text, regtype); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime."cast"(val text, type_ regtype) RETURNS jsonb + LANGUAGE plpgsql IMMUTABLE + AS $$ + declare + res jsonb; + begin + execute format('select to_jsonb(%L::'|| type_::text || ')', val) into res; + return res; + end + $$; + + +-- +-- Name: check_equality_op(realtime.equality_op, regtype, text, text); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) RETURNS boolean + LANGUAGE plpgsql IMMUTABLE + AS $$ + /* + Casts *val_1* and *val_2* as type *type_* and check the *op* condition for truthiness + */ + declare + op_symbol text = ( + case + when op = 'eq' then '=' + when op = 'neq' then '!=' + when op = 'lt' then '<' + when op = 'lte' then '<=' + when op = 'gt' then '>' + when op = 'gte' then '>=' + when op = 'in' then '= any' + else 'UNKNOWN OP' + end + ); + res boolean; + begin + execute format( + 'select %L::'|| type_::text || ' ' || op_symbol + || ' ( %L::' + || ( + case + when op = 'in' then type_::text || '[]' + else type_::text end + ) + || ')', val_1, val_2) into res; + return res; + end; + $$; + + +-- +-- Name: is_visible_through_filters(realtime.wal_column[], realtime.user_defined_filter[]); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) RETURNS boolean + LANGUAGE sql IMMUTABLE + AS $_$ + /* + Should the record be visible (true) or filtered out (false) after *filters* are applied + */ + select + -- Default to allowed when no filters present + $2 is null -- no filters. this should not happen because subscriptions has a default + or array_length($2, 1) is null -- array length of an empty array is null + or bool_and( + coalesce( + realtime.check_equality_op( + op:=f.op, + type_:=coalesce( + col.type_oid::regtype, -- null when wal2json version <= 2.4 + col.type_name::regtype + ), + -- cast jsonb to text + val_1:=col.value #>> '{}', + val_2:=f.value + ), + false -- if null, filter does not match + ) + ) + from + unnest(filters) f + join unnest(columns) col + on f.column_name = col.name; + $_$; + + +-- +-- Name: list_changes(name, name, integer, integer); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) RETURNS SETOF realtime.wal_rls + LANGUAGE sql + SET log_min_messages TO 'fatal' + AS $$ + with pub as ( + select + concat_ws( + ',', + case when bool_or(pubinsert) then 'insert' else null end, + case when bool_or(pubupdate) then 'update' else null end, + case when bool_or(pubdelete) then 'delete' else null end + ) as w2j_actions, + coalesce( + string_agg( + realtime.quote_wal2json(format('%I.%I', schemaname, tablename)::regclass), + ',' + ) filter (where ppt.tablename is not null and ppt.tablename not like '% %'), + '' + ) w2j_add_tables + from + pg_publication pp + left join pg_publication_tables ppt + on pp.pubname = ppt.pubname + where + pp.pubname = publication + group by + pp.pubname + limit 1 + ), + w2j as ( + select + x.*, pub.w2j_add_tables + from + pub, + pg_logical_slot_get_changes( + slot_name, null, max_changes, + 'include-pk', 'true', + 'include-transaction', 'false', + 'include-timestamp', 'true', + 'include-type-oids', 'true', + 'format-version', '2', + 'actions', pub.w2j_actions, + 'add-tables', pub.w2j_add_tables + ) x + ) + select + xyz.wal, + xyz.is_rls_enabled, + xyz.subscription_ids, + xyz.errors + from + w2j, + realtime.apply_rls( + wal := w2j.data::jsonb, + max_record_bytes := max_record_bytes + ) xyz(wal, is_rls_enabled, subscription_ids, errors) + where + w2j.w2j_add_tables <> '' + and xyz.subscription_ids[1] is not null + $$; + + +-- +-- Name: quote_wal2json(regclass); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.quote_wal2json(entity regclass) RETURNS text + LANGUAGE sql IMMUTABLE STRICT + AS $$ + select + ( + select string_agg('' || ch,'') + from unnest(string_to_array(nsp.nspname::text, null)) with ordinality x(ch, idx) + where + not (x.idx = 1 and x.ch = '"') + and not ( + x.idx = array_length(string_to_array(nsp.nspname::text, null), 1) + and x.ch = '"' + ) + ) + || '.' + || ( + select string_agg('' || ch,'') + from unnest(string_to_array(pc.relname::text, null)) with ordinality x(ch, idx) + where + not (x.idx = 1 and x.ch = '"') + and not ( + x.idx = array_length(string_to_array(nsp.nspname::text, null), 1) + and x.ch = '"' + ) + ) + from + pg_class pc + join pg_namespace nsp + on pc.relnamespace = nsp.oid + where + pc.oid = entity + $$; + + +-- +-- Name: send(jsonb, text, text, boolean); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.send(payload jsonb, event text, topic text, private boolean DEFAULT true) RETURNS void + LANGUAGE plpgsql + AS $$ +DECLARE + generated_id uuid; + final_payload jsonb; +BEGIN + BEGIN + -- Generate a new UUID for the id + generated_id := gen_random_uuid(); + + -- Check if payload has an 'id' key, if not, add the generated UUID + IF payload ? 'id' THEN + final_payload := payload; + ELSE + final_payload := jsonb_set(payload, '{id}', to_jsonb(generated_id)); + END IF; + + -- Set the topic configuration + EXECUTE format('SET LOCAL realtime.topic TO %L', topic); + + -- Attempt to insert the message + INSERT INTO realtime.messages (id, payload, event, topic, private, extension) + VALUES (generated_id, final_payload, event, topic, private, 'broadcast'); + EXCEPTION + WHEN OTHERS THEN + -- Capture and notify the error + RAISE WARNING 'ErrorSendingBroadcastMessage: %', SQLERRM; + END; +END; +$$; + + +-- +-- Name: subscription_check_filters(); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.subscription_check_filters() RETURNS trigger + LANGUAGE plpgsql + AS $$ + /* + Validates that the user defined filters for a subscription: + - refer to valid columns that the claimed role may access + - values are coercable to the correct column type + */ + declare + col_names text[] = coalesce( + array_agg(c.column_name order by c.ordinal_position), + '{}'::text[] + ) + from + information_schema.columns c + where + format('%I.%I', c.table_schema, c.table_name)::regclass = new.entity + and pg_catalog.has_column_privilege( + (new.claims ->> 'role'), + format('%I.%I', c.table_schema, c.table_name)::regclass, + c.column_name, + 'SELECT' + ); + filter realtime.user_defined_filter; + col_type regtype; + + in_val jsonb; + begin + for filter in select * from unnest(new.filters) loop + -- Filtered column is valid + if not filter.column_name = any(col_names) then + raise exception 'invalid column for filter %', filter.column_name; + end if; + + -- Type is sanitized and safe for string interpolation + col_type = ( + select atttypid::regtype + from pg_catalog.pg_attribute + where attrelid = new.entity + and attname = filter.column_name + ); + if col_type is null then + raise exception 'failed to lookup type for column %', filter.column_name; + end if; + + -- Set maximum number of entries for in filter + if filter.op = 'in'::realtime.equality_op then + in_val = realtime.cast(filter.value, (col_type::text || '[]')::regtype); + if coalesce(jsonb_array_length(in_val), 0) > 100 then + raise exception 'too many values for `in` filter. Maximum 100'; + end if; + else + -- raises an exception if value is not coercable to type + perform realtime.cast(filter.value, col_type); + end if; + + end loop; + + -- Apply consistent order to filters so the unique constraint on + -- (subscription_id, entity, filters) can't be tricked by a different filter order + new.filters = coalesce( + array_agg(f order by f.column_name, f.op, f.value), + '{}' + ) from unnest(new.filters) f; + + return new; + end; + $$; + + +-- +-- Name: to_regrole(text); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.to_regrole(role_name text) RETURNS regrole + LANGUAGE sql IMMUTABLE + AS $$ select role_name::regrole $$; + + +-- +-- Name: topic(); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.topic() RETURNS text + LANGUAGE sql STABLE + AS $$ +select nullif(current_setting('realtime.topic', true), '')::text; +$$; + + +-- +-- Name: can_insert_object(text, text, uuid, jsonb); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.can_insert_object(bucketid text, name text, owner uuid, metadata jsonb) RETURNS void + LANGUAGE plpgsql + AS $$ +BEGIN + INSERT INTO "storage"."objects" ("bucket_id", "name", "owner", "metadata") VALUES (bucketid, name, owner, metadata); + -- hack to rollback the successful insert + RAISE sqlstate 'PT200' using + message = 'ROLLBACK', + detail = 'rollback successful insert'; +END +$$; + + +-- +-- Name: enforce_bucket_name_length(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.enforce_bucket_name_length() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if length(new.name) > 100 then + raise exception 'bucket name "%" is too long (% characters). Max is 100.', new.name, length(new.name); + end if; + return new; +end; +$$; + + +-- +-- Name: extension(text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.extension(name text) RETURNS text + LANGUAGE plpgsql + AS $$ +DECLARE +_parts text[]; +_filename text; +BEGIN + select string_to_array(name, '/') into _parts; + select _parts[array_length(_parts,1)] into _filename; + -- @todo return the last part instead of 2 + return reverse(split_part(reverse(_filename), '.', 1)); +END +$$; + + +-- +-- Name: filename(text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.filename(name text) RETURNS text + LANGUAGE plpgsql + AS $$ +DECLARE +_parts text[]; +BEGIN + select string_to_array(name, '/') into _parts; + return _parts[array_length(_parts,1)]; +END +$$; + + +-- +-- Name: foldername(text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.foldername(name text) RETURNS text[] + LANGUAGE plpgsql + AS $$ +DECLARE +_parts text[]; +BEGIN + select string_to_array(name, '/') into _parts; + return _parts[1:array_length(_parts,1)-1]; +END +$$; + + +-- +-- Name: get_common_prefix(text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.get_common_prefix(p_key text, p_prefix text, p_delimiter text) RETURNS text + LANGUAGE sql IMMUTABLE + AS $$ +SELECT CASE + WHEN position(p_delimiter IN substring(p_key FROM length(p_prefix) + 1)) > 0 + THEN left(p_key, length(p_prefix) + position(p_delimiter IN substring(p_key FROM length(p_prefix) + 1))) + ELSE NULL +END; +$$; + + +-- +-- Name: get_size_by_bucket(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.get_size_by_bucket() RETURNS TABLE(size bigint, bucket_id text) + LANGUAGE plpgsql + AS $$ +BEGIN + return query + select sum((metadata->>'size')::int) as size, obj.bucket_id + from "storage".objects as obj + group by obj.bucket_id; +END +$$; + + +-- +-- Name: list_multipart_uploads_with_delimiter(text, text, text, integer, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.list_multipart_uploads_with_delimiter(bucket_id text, prefix_param text, delimiter_param text, max_keys integer DEFAULT 100, next_key_token text DEFAULT ''::text, next_upload_token text DEFAULT ''::text) RETURNS TABLE(key text, id text, created_at timestamp with time zone) + LANGUAGE plpgsql + AS $_$ +BEGIN + RETURN QUERY EXECUTE + 'SELECT DISTINCT ON(key COLLATE "C") * from ( + SELECT + CASE + WHEN position($2 IN substring(key from length($1) + 1)) > 0 THEN + substring(key from 1 for length($1) + position($2 IN substring(key from length($1) + 1))) + ELSE + key + END AS key, id, created_at + FROM + storage.s3_multipart_uploads + WHERE + bucket_id = $5 AND + key ILIKE $1 || ''%'' AND + CASE + WHEN $4 != '''' AND $6 = '''' THEN + CASE + WHEN position($2 IN substring(key from length($1) + 1)) > 0 THEN + substring(key from 1 for length($1) + position($2 IN substring(key from length($1) + 1))) COLLATE "C" > $4 + ELSE + key COLLATE "C" > $4 + END + ELSE + true + END AND + CASE + WHEN $6 != '''' THEN + id COLLATE "C" > $6 + ELSE + true + END + ORDER BY + key COLLATE "C" ASC, created_at ASC) as e order by key COLLATE "C" LIMIT $3' + USING prefix_param, delimiter_param, max_keys, next_key_token, bucket_id, next_upload_token; +END; +$_$; + + +-- +-- Name: list_objects_with_delimiter(text, text, text, integer, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.list_objects_with_delimiter(_bucket_id text, prefix_param text, delimiter_param text, max_keys integer DEFAULT 100, start_after text DEFAULT ''::text, next_token text DEFAULT ''::text, sort_order text DEFAULT 'asc'::text) RETURNS TABLE(name text, id uuid, metadata jsonb, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone) + LANGUAGE plpgsql STABLE + AS $_$ +DECLARE + v_peek_name TEXT; + v_current RECORD; + v_common_prefix TEXT; + + -- Configuration + v_is_asc BOOLEAN; + v_prefix TEXT; + v_start TEXT; + v_upper_bound TEXT; + v_file_batch_size INT; + + -- Seek state + v_next_seek TEXT; + v_count INT := 0; + + -- Dynamic SQL for batch query only + v_batch_query TEXT; + +BEGIN + -- ======================================================================== + -- INITIALIZATION + -- ======================================================================== + v_is_asc := lower(coalesce(sort_order, 'asc')) = 'asc'; + v_prefix := coalesce(prefix_param, ''); + v_start := CASE WHEN coalesce(next_token, '') <> '' THEN next_token ELSE coalesce(start_after, '') END; + v_file_batch_size := LEAST(GREATEST(max_keys * 2, 100), 1000); + + -- Calculate upper bound for prefix filtering (bytewise, using COLLATE "C") + IF v_prefix = '' THEN + v_upper_bound := NULL; + ELSIF right(v_prefix, 1) = delimiter_param THEN + v_upper_bound := left(v_prefix, -1) || chr(ascii(delimiter_param) + 1); + ELSE + v_upper_bound := left(v_prefix, -1) || chr(ascii(right(v_prefix, 1)) + 1); + END IF; + + -- Build batch query (dynamic SQL - called infrequently, amortized over many rows) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" >= $2 ' || + 'AND o.name COLLATE "C" < $3 ORDER BY o.name COLLATE "C" ASC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" >= $2 ' || + 'ORDER BY o.name COLLATE "C" ASC LIMIT $4'; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" < $2 ' || + 'AND o.name COLLATE "C" >= $3 ORDER BY o.name COLLATE "C" DESC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" < $2 ' || + 'ORDER BY o.name COLLATE "C" DESC LIMIT $4'; + END IF; + END IF; + + -- ======================================================================== + -- SEEK INITIALIZATION: Determine starting position + -- ======================================================================== + IF v_start = '' THEN + IF v_is_asc THEN + v_next_seek := v_prefix; + ELSE + -- DESC without cursor: find the last item in range + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_next_seek FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_prefix AND o.name COLLATE "C" < v_upper_bound + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix <> '' THEN + SELECT o.name INTO v_next_seek FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_prefix + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_next_seek FROM storage.objects o + WHERE o.bucket_id = _bucket_id + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + END IF; + + IF v_next_seek IS NOT NULL THEN + v_next_seek := v_next_seek || delimiter_param; + ELSE + RETURN; + END IF; + END IF; + ELSE + -- Cursor provided: determine if it refers to a folder or leaf + IF EXISTS ( + SELECT 1 FROM storage.objects o + WHERE o.bucket_id = _bucket_id + AND o.name COLLATE "C" LIKE v_start || delimiter_param || '%' + LIMIT 1 + ) THEN + -- Cursor refers to a folder + IF v_is_asc THEN + v_next_seek := v_start || chr(ascii(delimiter_param) + 1); + ELSE + v_next_seek := v_start || delimiter_param; + END IF; + ELSE + -- Cursor refers to a leaf object + IF v_is_asc THEN + v_next_seek := v_start || delimiter_param; + ELSE + v_next_seek := v_start; + END IF; + END IF; + END IF; + + -- ======================================================================== + -- MAIN LOOP: Hybrid peek-then-batch algorithm + -- Uses STATIC SQL for peek (hot path) and DYNAMIC SQL for batch + -- ======================================================================== + LOOP + EXIT WHEN v_count >= max_keys; + + -- STEP 1: PEEK using STATIC SQL (plan cached, very fast) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_next_seek AND o.name COLLATE "C" < v_upper_bound + ORDER BY o.name COLLATE "C" ASC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_next_seek + ORDER BY o.name COLLATE "C" ASC LIMIT 1; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" < v_next_seek AND o.name COLLATE "C" >= v_prefix + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix <> '' THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" < v_next_seek AND o.name COLLATE "C" >= v_prefix + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" < v_next_seek + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + END IF; + END IF; + + EXIT WHEN v_peek_name IS NULL; + + -- STEP 2: Check if this is a FOLDER or FILE + v_common_prefix := storage.get_common_prefix(v_peek_name, v_prefix, delimiter_param); + + IF v_common_prefix IS NOT NULL THEN + -- FOLDER: Emit and skip to next folder (no heap access needed) + name := rtrim(v_common_prefix, delimiter_param); + id := NULL; + updated_at := NULL; + created_at := NULL; + last_accessed_at := NULL; + metadata := NULL; + RETURN NEXT; + v_count := v_count + 1; + + -- Advance seek past the folder range + IF v_is_asc THEN + v_next_seek := left(v_common_prefix, -1) || chr(ascii(delimiter_param) + 1); + ELSE + v_next_seek := v_common_prefix; + END IF; + ELSE + -- FILE: Batch fetch using DYNAMIC SQL (overhead amortized over many rows) + -- For ASC: upper_bound is the exclusive upper limit (< condition) + -- For DESC: prefix is the inclusive lower limit (>= condition) + FOR v_current IN EXECUTE v_batch_query USING _bucket_id, v_next_seek, + CASE WHEN v_is_asc THEN COALESCE(v_upper_bound, v_prefix) ELSE v_prefix END, v_file_batch_size + LOOP + v_common_prefix := storage.get_common_prefix(v_current.name, v_prefix, delimiter_param); + + IF v_common_prefix IS NOT NULL THEN + -- Hit a folder: exit batch, let peek handle it + v_next_seek := v_current.name; + EXIT; + END IF; + + -- Emit file + name := v_current.name; + id := v_current.id; + updated_at := v_current.updated_at; + created_at := v_current.created_at; + last_accessed_at := v_current.last_accessed_at; + metadata := v_current.metadata; + RETURN NEXT; + v_count := v_count + 1; + + -- Advance seek past this file + IF v_is_asc THEN + v_next_seek := v_current.name || delimiter_param; + ELSE + v_next_seek := v_current.name; + END IF; + + EXIT WHEN v_count >= max_keys; + END LOOP; + END IF; + END LOOP; +END; +$_$; + + +-- +-- Name: operation(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.operation() RETURNS text + LANGUAGE plpgsql STABLE + AS $$ +BEGIN + RETURN current_setting('storage.operation', true); +END; +$$; + + +-- +-- Name: protect_delete(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.protect_delete() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + -- Check if storage.allow_delete_query is set to 'true' + IF COALESCE(current_setting('storage.allow_delete_query', true), 'false') != 'true' THEN + RAISE EXCEPTION 'Direct deletion from storage tables is not allowed. Use the Storage API instead.' + USING HINT = 'This prevents accidental data loss from orphaned objects.', + ERRCODE = '42501'; + END IF; + RETURN NULL; +END; +$$; + + +-- +-- Name: search(text, text, integer, integer, integer, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.search(prefix text, bucketname text, limits integer DEFAULT 100, levels integer DEFAULT 1, offsets integer DEFAULT 0, search text DEFAULT ''::text, sortcolumn text DEFAULT 'name'::text, sortorder text DEFAULT 'asc'::text) RETURNS TABLE(name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb) + LANGUAGE plpgsql STABLE + AS $_$ +DECLARE + v_peek_name TEXT; + v_current RECORD; + v_common_prefix TEXT; + v_delimiter CONSTANT TEXT := '/'; + + -- Configuration + v_limit INT; + v_prefix TEXT; + v_prefix_lower TEXT; + v_is_asc BOOLEAN; + v_order_by TEXT; + v_sort_order TEXT; + v_upper_bound TEXT; + v_file_batch_size INT; + + -- Dynamic SQL for batch query only + v_batch_query TEXT; + + -- Seek state + v_next_seek TEXT; + v_count INT := 0; + v_skipped INT := 0; +BEGIN + -- ======================================================================== + -- INITIALIZATION + -- ======================================================================== + v_limit := LEAST(coalesce(limits, 100), 1500); + v_prefix := coalesce(prefix, '') || coalesce(search, ''); + v_prefix_lower := lower(v_prefix); + v_is_asc := lower(coalesce(sortorder, 'asc')) = 'asc'; + v_file_batch_size := LEAST(GREATEST(v_limit * 2, 100), 1000); + + -- Validate sort column + CASE lower(coalesce(sortcolumn, 'name')) + WHEN 'name' THEN v_order_by := 'name'; + WHEN 'updated_at' THEN v_order_by := 'updated_at'; + WHEN 'created_at' THEN v_order_by := 'created_at'; + WHEN 'last_accessed_at' THEN v_order_by := 'last_accessed_at'; + ELSE v_order_by := 'name'; + END CASE; + + v_sort_order := CASE WHEN v_is_asc THEN 'asc' ELSE 'desc' END; + + -- ======================================================================== + -- NON-NAME SORTING: Use path_tokens approach (unchanged) + -- ======================================================================== + IF v_order_by != 'name' THEN + RETURN QUERY EXECUTE format( + $sql$ + WITH folders AS ( + SELECT path_tokens[$1] AS folder + FROM storage.objects + WHERE objects.name ILIKE $2 || '%%' + AND bucket_id = $3 + AND array_length(objects.path_tokens, 1) <> $1 + GROUP BY folder + ORDER BY folder %s + ) + (SELECT folder AS "name", + NULL::uuid AS id, + NULL::timestamptz AS updated_at, + NULL::timestamptz AS created_at, + NULL::timestamptz AS last_accessed_at, + NULL::jsonb AS metadata FROM folders) + UNION ALL + (SELECT path_tokens[$1] AS "name", + id, updated_at, created_at, last_accessed_at, metadata + FROM storage.objects + WHERE objects.name ILIKE $2 || '%%' + AND bucket_id = $3 + AND array_length(objects.path_tokens, 1) = $1 + ORDER BY %I %s) + LIMIT $4 OFFSET $5 + $sql$, v_sort_order, v_order_by, v_sort_order + ) USING levels, v_prefix, bucketname, v_limit, offsets; + RETURN; + END IF; + + -- ======================================================================== + -- NAME SORTING: Hybrid skip-scan with batch optimization + -- ======================================================================== + + -- Calculate upper bound for prefix filtering + IF v_prefix_lower = '' THEN + v_upper_bound := NULL; + ELSIF right(v_prefix_lower, 1) = v_delimiter THEN + v_upper_bound := left(v_prefix_lower, -1) || chr(ascii(v_delimiter) + 1); + ELSE + v_upper_bound := left(v_prefix_lower, -1) || chr(ascii(right(v_prefix_lower, 1)) + 1); + END IF; + + -- Build batch query (dynamic SQL - called infrequently, amortized over many rows) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" >= $2 ' || + 'AND lower(o.name) COLLATE "C" < $3 ORDER BY lower(o.name) COLLATE "C" ASC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" >= $2 ' || + 'ORDER BY lower(o.name) COLLATE "C" ASC LIMIT $4'; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" < $2 ' || + 'AND lower(o.name) COLLATE "C" >= $3 ORDER BY lower(o.name) COLLATE "C" DESC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" < $2 ' || + 'ORDER BY lower(o.name) COLLATE "C" DESC LIMIT $4'; + END IF; + END IF; + + -- Initialize seek position + IF v_is_asc THEN + v_next_seek := v_prefix_lower; + ELSE + -- DESC: find the last item in range first (static SQL) + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_prefix_lower AND lower(o.name) COLLATE "C" < v_upper_bound + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix_lower <> '' THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_prefix_lower + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + END IF; + + IF v_peek_name IS NOT NULL THEN + v_next_seek := lower(v_peek_name) || v_delimiter; + ELSE + RETURN; + END IF; + END IF; + + -- ======================================================================== + -- MAIN LOOP: Hybrid peek-then-batch algorithm + -- Uses STATIC SQL for peek (hot path) and DYNAMIC SQL for batch + -- ======================================================================== + LOOP + EXIT WHEN v_count >= v_limit; + + -- STEP 1: PEEK using STATIC SQL (plan cached, very fast) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_next_seek AND lower(o.name) COLLATE "C" < v_upper_bound + ORDER BY lower(o.name) COLLATE "C" ASC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_next_seek + ORDER BY lower(o.name) COLLATE "C" ASC LIMIT 1; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" < v_next_seek AND lower(o.name) COLLATE "C" >= v_prefix_lower + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix_lower <> '' THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" < v_next_seek AND lower(o.name) COLLATE "C" >= v_prefix_lower + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" < v_next_seek + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + END IF; + END IF; + + EXIT WHEN v_peek_name IS NULL; + + -- STEP 2: Check if this is a FOLDER or FILE + v_common_prefix := storage.get_common_prefix(lower(v_peek_name), v_prefix_lower, v_delimiter); + + IF v_common_prefix IS NOT NULL THEN + -- FOLDER: Handle offset, emit if needed, skip to next folder + IF v_skipped < offsets THEN + v_skipped := v_skipped + 1; + ELSE + name := split_part(rtrim(v_common_prefix, v_delimiter), v_delimiter, levels); + id := NULL; + updated_at := NULL; + created_at := NULL; + last_accessed_at := NULL; + metadata := NULL; + RETURN NEXT; + v_count := v_count + 1; + END IF; + + -- Advance seek past the folder range + IF v_is_asc THEN + v_next_seek := lower(left(v_common_prefix, -1)) || chr(ascii(v_delimiter) + 1); + ELSE + v_next_seek := lower(v_common_prefix); + END IF; + ELSE + -- FILE: Batch fetch using DYNAMIC SQL (overhead amortized over many rows) + -- For ASC: upper_bound is the exclusive upper limit (< condition) + -- For DESC: prefix_lower is the inclusive lower limit (>= condition) + FOR v_current IN EXECUTE v_batch_query + USING bucketname, v_next_seek, + CASE WHEN v_is_asc THEN COALESCE(v_upper_bound, v_prefix_lower) ELSE v_prefix_lower END, v_file_batch_size + LOOP + v_common_prefix := storage.get_common_prefix(lower(v_current.name), v_prefix_lower, v_delimiter); + + IF v_common_prefix IS NOT NULL THEN + -- Hit a folder: exit batch, let peek handle it + v_next_seek := lower(v_current.name); + EXIT; + END IF; + + -- Handle offset skipping + IF v_skipped < offsets THEN + v_skipped := v_skipped + 1; + ELSE + -- Emit file + name := split_part(v_current.name, v_delimiter, levels); + id := v_current.id; + updated_at := v_current.updated_at; + created_at := v_current.created_at; + last_accessed_at := v_current.last_accessed_at; + metadata := v_current.metadata; + RETURN NEXT; + v_count := v_count + 1; + END IF; + + -- Advance seek past this file + IF v_is_asc THEN + v_next_seek := lower(v_current.name) || v_delimiter; + ELSE + v_next_seek := lower(v_current.name); + END IF; + + EXIT WHEN v_count >= v_limit; + END LOOP; + END IF; + END LOOP; +END; +$_$; + + +-- +-- Name: search_by_timestamp(text, text, integer, integer, text, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.search_by_timestamp(p_prefix text, p_bucket_id text, p_limit integer, p_level integer, p_start_after text, p_sort_order text, p_sort_column text, p_sort_column_after text) RETURNS TABLE(key text, name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb) + LANGUAGE plpgsql STABLE + AS $_$ +DECLARE + v_cursor_op text; + v_query text; + v_prefix text; +BEGIN + v_prefix := coalesce(p_prefix, ''); + + IF p_sort_order = 'asc' THEN + v_cursor_op := '>'; + ELSE + v_cursor_op := '<'; + END IF; + + v_query := format($sql$ + WITH raw_objects AS ( + SELECT + o.name AS obj_name, + o.id AS obj_id, + o.updated_at AS obj_updated_at, + o.created_at AS obj_created_at, + o.last_accessed_at AS obj_last_accessed_at, + o.metadata AS obj_metadata, + storage.get_common_prefix(o.name, $1, '/') AS common_prefix + FROM storage.objects o + WHERE o.bucket_id = $2 + AND o.name COLLATE "C" LIKE $1 || '%%' + ), + -- Aggregate common prefixes (folders) + -- Both created_at and updated_at use MIN(obj_created_at) to match the old prefixes table behavior + aggregated_prefixes AS ( + SELECT + rtrim(common_prefix, '/') AS name, + NULL::uuid AS id, + MIN(obj_created_at) AS updated_at, + MIN(obj_created_at) AS created_at, + NULL::timestamptz AS last_accessed_at, + NULL::jsonb AS metadata, + TRUE AS is_prefix + FROM raw_objects + WHERE common_prefix IS NOT NULL + GROUP BY common_prefix + ), + leaf_objects AS ( + SELECT + obj_name AS name, + obj_id AS id, + obj_updated_at AS updated_at, + obj_created_at AS created_at, + obj_last_accessed_at AS last_accessed_at, + obj_metadata AS metadata, + FALSE AS is_prefix + FROM raw_objects + WHERE common_prefix IS NULL + ), + combined AS ( + SELECT * FROM aggregated_prefixes + UNION ALL + SELECT * FROM leaf_objects + ), + filtered AS ( + SELECT * + FROM combined + WHERE ( + $5 = '' + OR ROW( + date_trunc('milliseconds', %I), + name COLLATE "C" + ) %s ROW( + COALESCE(NULLIF($6, '')::timestamptz, 'epoch'::timestamptz), + $5 + ) + ) + ) + SELECT + split_part(name, '/', $3) AS key, + name, + id, + updated_at, + created_at, + last_accessed_at, + metadata + FROM filtered + ORDER BY + COALESCE(date_trunc('milliseconds', %I), 'epoch'::timestamptz) %s, + name COLLATE "C" %s + LIMIT $4 + $sql$, + p_sort_column, + v_cursor_op, + p_sort_column, + p_sort_order, + p_sort_order + ); + + RETURN QUERY EXECUTE v_query + USING v_prefix, p_bucket_id, p_level, p_limit, p_start_after, p_sort_column_after; +END; +$_$; + + +-- +-- Name: search_v2(text, text, integer, integer, text, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.search_v2(prefix text, bucket_name text, limits integer DEFAULT 100, levels integer DEFAULT 1, start_after text DEFAULT ''::text, sort_order text DEFAULT 'asc'::text, sort_column text DEFAULT 'name'::text, sort_column_after text DEFAULT ''::text) RETURNS TABLE(key text, name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb) + LANGUAGE plpgsql STABLE + AS $$ +DECLARE + v_sort_col text; + v_sort_ord text; + v_limit int; +BEGIN + -- Cap limit to maximum of 1500 records + v_limit := LEAST(coalesce(limits, 100), 1500); + + -- Validate and normalize sort_order + v_sort_ord := lower(coalesce(sort_order, 'asc')); + IF v_sort_ord NOT IN ('asc', 'desc') THEN + v_sort_ord := 'asc'; + END IF; + + -- Validate and normalize sort_column + v_sort_col := lower(coalesce(sort_column, 'name')); + IF v_sort_col NOT IN ('name', 'updated_at', 'created_at') THEN + v_sort_col := 'name'; + END IF; + + -- Route to appropriate implementation + IF v_sort_col = 'name' THEN + -- Use list_objects_with_delimiter for name sorting (most efficient: O(k * log n)) + RETURN QUERY + SELECT + split_part(l.name, '/', levels) AS key, + l.name AS name, + l.id, + l.updated_at, + l.created_at, + l.last_accessed_at, + l.metadata + FROM storage.list_objects_with_delimiter( + bucket_name, + coalesce(prefix, ''), + '/', + v_limit, + start_after, + '', + v_sort_ord + ) l; + ELSE + -- Use aggregation approach for timestamp sorting + -- Not efficient for large datasets but supports correct pagination + RETURN QUERY SELECT * FROM storage.search_by_timestamp( + prefix, bucket_name, v_limit, levels, start_after, + v_sort_ord, v_sort_col, sort_column_after + ); + END IF; +END; +$$; + + +-- +-- Name: update_updated_at_column(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.update_updated_at_column() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: http_request(); Type: FUNCTION; Schema: supabase_functions; Owner: - +-- + +CREATE FUNCTION supabase_functions.http_request() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'supabase_functions' + AS $$ + DECLARE + request_id bigint; + payload jsonb; + url text := TG_ARGV[0]::text; + method text := TG_ARGV[1]::text; + headers jsonb DEFAULT '{}'::jsonb; + params jsonb DEFAULT '{}'::jsonb; + timeout_ms integer DEFAULT 1000; + BEGIN + IF url IS NULL OR url = 'null' THEN + RAISE EXCEPTION 'url argument is missing'; + END IF; + + IF method IS NULL OR method = 'null' THEN + RAISE EXCEPTION 'method argument is missing'; + END IF; + + IF TG_ARGV[2] IS NULL OR TG_ARGV[2] = 'null' THEN + headers = '{"Content-Type": "application/json"}'::jsonb; + ELSE + headers = TG_ARGV[2]::jsonb; + END IF; + + IF TG_ARGV[3] IS NULL OR TG_ARGV[3] = 'null' THEN + params = '{}'::jsonb; + ELSE + params = TG_ARGV[3]::jsonb; + END IF; + + IF TG_ARGV[4] IS NULL OR TG_ARGV[4] = 'null' THEN + timeout_ms = 1000; + ELSE + timeout_ms = TG_ARGV[4]::integer; + END IF; + + CASE + WHEN method = 'GET' THEN + SELECT http_get INTO request_id FROM net.http_get( + url, + params, + headers, + timeout_ms + ); + WHEN method = 'POST' THEN + payload = jsonb_build_object( + 'old_record', OLD, + 'record', NEW, + 'type', TG_OP, + 'table', TG_TABLE_NAME, + 'schema', TG_TABLE_SCHEMA + ); + + SELECT http_post INTO request_id FROM net.http_post( + url, + payload, + params, + headers, + timeout_ms + ); + ELSE + RAISE EXCEPTION 'method argument % is invalid', method; + END CASE; + + INSERT INTO supabase_functions.hooks + (hook_table_id, hook_name, request_id) + VALUES + (TG_RELID, TG_NAME, request_id); + + RETURN NEW; + END +$$; + + +-- +-- Name: extensions; Type: TABLE; Schema: _realtime; Owner: - +-- + +CREATE TABLE _realtime.extensions ( + id uuid NOT NULL, + type text, + settings jsonb, + tenant_external_id text, + inserted_at timestamp(0) without time zone NOT NULL, + updated_at timestamp(0) without time zone NOT NULL +); + + +-- +-- Name: schema_migrations; Type: TABLE; Schema: _realtime; Owner: - +-- + +CREATE TABLE _realtime.schema_migrations ( + version bigint NOT NULL, + inserted_at timestamp(0) without time zone +); + + +-- +-- Name: tenants; Type: TABLE; Schema: _realtime; Owner: - +-- + +CREATE TABLE _realtime.tenants ( + id uuid NOT NULL, + name text, + external_id text, + jwt_secret text, + max_concurrent_users integer DEFAULT 200 NOT NULL, + inserted_at timestamp(0) without time zone NOT NULL, + updated_at timestamp(0) without time zone NOT NULL, + max_events_per_second integer DEFAULT 100 NOT NULL, + postgres_cdc_default text DEFAULT 'postgres_cdc_rls'::text, + max_bytes_per_second integer DEFAULT 100000 NOT NULL, + max_channels_per_client integer DEFAULT 100 NOT NULL, + max_joins_per_second integer DEFAULT 500 NOT NULL, + suspend boolean DEFAULT false, + jwt_jwks jsonb, + notify_private_alpha boolean DEFAULT false, + private_only boolean DEFAULT false NOT NULL, + migrations_ran integer DEFAULT 0, + broadcast_adapter character varying(255) DEFAULT 'gen_rpc'::character varying, + max_presence_events_per_second integer DEFAULT 1000, + max_payload_size_in_kb integer DEFAULT 3000, + CONSTRAINT jwt_secret_or_jwt_jwks_required CHECK (((jwt_secret IS NOT NULL) OR (jwt_jwks IS NOT NULL))) +); + + +-- +-- Name: audit_log_entries; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.audit_log_entries ( + instance_id uuid, + id uuid NOT NULL, + payload json, + created_at timestamp with time zone, + ip_address character varying(64) DEFAULT ''::character varying NOT NULL +); + + +-- +-- Name: TABLE audit_log_entries; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.audit_log_entries IS 'Auth: Audit trail for user actions.'; + + +-- +-- Name: flow_state; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.flow_state ( + id uuid NOT NULL, + user_id uuid, + auth_code text, + code_challenge_method auth.code_challenge_method, + code_challenge text, + provider_type text NOT NULL, + provider_access_token text, + provider_refresh_token text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + authentication_method text NOT NULL, + auth_code_issued_at timestamp with time zone, + invite_token text, + referrer text, + oauth_client_state_id uuid, + linking_target_id uuid, + email_optional boolean DEFAULT false NOT NULL +); + + +-- +-- Name: TABLE flow_state; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.flow_state IS 'Stores metadata for all OAuth/SSO login flows'; + + +-- +-- Name: identities; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.identities ( + provider_id text NOT NULL, + user_id uuid NOT NULL, + identity_data jsonb NOT NULL, + provider text NOT NULL, + last_sign_in_at timestamp with time zone, + created_at timestamp with time zone, + updated_at timestamp with time zone, + email text GENERATED ALWAYS AS (lower((identity_data ->> 'email'::text))) STORED, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: TABLE identities; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.identities IS 'Auth: Stores identities associated to a user.'; + + +-- +-- Name: COLUMN identities.email; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.identities.email IS 'Auth: Email is a generated column that references the optional email property in the identity_data'; + + +-- +-- Name: instances; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.instances ( + id uuid NOT NULL, + uuid uuid, + raw_base_config text, + created_at timestamp with time zone, + updated_at timestamp with time zone +); + + +-- +-- Name: TABLE instances; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.instances IS 'Auth: Manages users across multiple sites.'; + + +-- +-- Name: mfa_amr_claims; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.mfa_amr_claims ( + session_id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + authentication_method text NOT NULL, + id uuid NOT NULL +); + + +-- +-- Name: TABLE mfa_amr_claims; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.mfa_amr_claims IS 'auth: stores authenticator method reference claims for multi factor authentication'; + + +-- +-- Name: mfa_challenges; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.mfa_challenges ( + id uuid NOT NULL, + factor_id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + verified_at timestamp with time zone, + ip_address inet NOT NULL, + otp_code text, + web_authn_session_data jsonb +); + + +-- +-- Name: TABLE mfa_challenges; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.mfa_challenges IS 'auth: stores metadata about challenge requests made'; + + +-- +-- Name: mfa_factors; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.mfa_factors ( + id uuid NOT NULL, + user_id uuid NOT NULL, + friendly_name text, + factor_type auth.factor_type NOT NULL, + status auth.factor_status NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + secret text, + phone text, + last_challenged_at timestamp with time zone, + web_authn_credential jsonb, + web_authn_aaguid uuid, + last_webauthn_challenge_data jsonb +); + + +-- +-- Name: TABLE mfa_factors; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.mfa_factors IS 'auth: stores metadata about factors'; + + +-- +-- Name: COLUMN mfa_factors.last_webauthn_challenge_data; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.mfa_factors.last_webauthn_challenge_data IS 'Stores the latest WebAuthn challenge data including attestation/assertion for customer verification'; + + +-- +-- Name: oauth_authorizations; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_authorizations ( + id uuid NOT NULL, + authorization_id text NOT NULL, + client_id uuid NOT NULL, + user_id uuid, + redirect_uri text NOT NULL, + scope text NOT NULL, + state text, + resource text, + code_challenge text, + code_challenge_method auth.code_challenge_method, + response_type auth.oauth_response_type DEFAULT 'code'::auth.oauth_response_type NOT NULL, + status auth.oauth_authorization_status DEFAULT 'pending'::auth.oauth_authorization_status NOT NULL, + authorization_code text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + expires_at timestamp with time zone DEFAULT (now() + '00:03:00'::interval) NOT NULL, + approved_at timestamp with time zone, + nonce text, + CONSTRAINT oauth_authorizations_authorization_code_length CHECK ((char_length(authorization_code) <= 255)), + CONSTRAINT oauth_authorizations_code_challenge_length CHECK ((char_length(code_challenge) <= 128)), + CONSTRAINT oauth_authorizations_expires_at_future CHECK ((expires_at > created_at)), + CONSTRAINT oauth_authorizations_nonce_length CHECK ((char_length(nonce) <= 255)), + CONSTRAINT oauth_authorizations_redirect_uri_length CHECK ((char_length(redirect_uri) <= 2048)), + CONSTRAINT oauth_authorizations_resource_length CHECK ((char_length(resource) <= 2048)), + CONSTRAINT oauth_authorizations_scope_length CHECK ((char_length(scope) <= 4096)), + CONSTRAINT oauth_authorizations_state_length CHECK ((char_length(state) <= 4096)) +); + + +-- +-- Name: oauth_client_states; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_client_states ( + id uuid NOT NULL, + provider_type text NOT NULL, + code_verifier text, + created_at timestamp with time zone NOT NULL +); + + +-- +-- Name: TABLE oauth_client_states; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.oauth_client_states IS 'Stores OAuth states for third-party provider authentication flows where Supabase acts as the OAuth client.'; + + +-- +-- Name: oauth_clients; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_clients ( + id uuid NOT NULL, + client_secret_hash text, + registration_type auth.oauth_registration_type NOT NULL, + redirect_uris text NOT NULL, + grant_types text NOT NULL, + client_name text, + client_uri text, + logo_uri text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + client_type auth.oauth_client_type DEFAULT 'confidential'::auth.oauth_client_type NOT NULL, + token_endpoint_auth_method text NOT NULL, + CONSTRAINT oauth_clients_client_name_length CHECK ((char_length(client_name) <= 1024)), + CONSTRAINT oauth_clients_client_uri_length CHECK ((char_length(client_uri) <= 2048)), + CONSTRAINT oauth_clients_logo_uri_length CHECK ((char_length(logo_uri) <= 2048)), + CONSTRAINT oauth_clients_token_endpoint_auth_method_check CHECK ((token_endpoint_auth_method = ANY (ARRAY['client_secret_basic'::text, 'client_secret_post'::text, 'none'::text]))) +); + + +-- +-- Name: oauth_consents; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_consents ( + id uuid NOT NULL, + user_id uuid NOT NULL, + client_id uuid NOT NULL, + scopes text NOT NULL, + granted_at timestamp with time zone DEFAULT now() NOT NULL, + revoked_at timestamp with time zone, + CONSTRAINT oauth_consents_revoked_after_granted CHECK (((revoked_at IS NULL) OR (revoked_at >= granted_at))), + CONSTRAINT oauth_consents_scopes_length CHECK ((char_length(scopes) <= 2048)), + CONSTRAINT oauth_consents_scopes_not_empty CHECK ((char_length(TRIM(BOTH FROM scopes)) > 0)) +); + + +-- +-- Name: one_time_tokens; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.one_time_tokens ( + id uuid NOT NULL, + user_id uuid NOT NULL, + token_type auth.one_time_token_type NOT NULL, + token_hash text NOT NULL, + relates_to text NOT NULL, + created_at timestamp without time zone DEFAULT now() NOT NULL, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + CONSTRAINT one_time_tokens_token_hash_check CHECK ((char_length(token_hash) > 0)) +); + + +-- +-- Name: refresh_tokens; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.refresh_tokens ( + instance_id uuid, + id bigint NOT NULL, + token character varying(255), + user_id character varying(255), + revoked boolean, + created_at timestamp with time zone, + updated_at timestamp with time zone, + parent character varying(255), + session_id uuid +); + + +-- +-- Name: TABLE refresh_tokens; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.refresh_tokens IS 'Auth: Store of tokens used to refresh JWT tokens once they expire.'; + + +-- +-- Name: refresh_tokens_id_seq; Type: SEQUENCE; Schema: auth; Owner: - +-- + +CREATE SEQUENCE auth.refresh_tokens_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: refresh_tokens_id_seq; Type: SEQUENCE OWNED BY; Schema: auth; Owner: - +-- + +ALTER SEQUENCE auth.refresh_tokens_id_seq OWNED BY auth.refresh_tokens.id; + + +-- +-- Name: saml_providers; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.saml_providers ( + id uuid NOT NULL, + sso_provider_id uuid NOT NULL, + entity_id text NOT NULL, + metadata_xml text NOT NULL, + metadata_url text, + attribute_mapping jsonb, + created_at timestamp with time zone, + updated_at timestamp with time zone, + name_id_format text, + CONSTRAINT "entity_id not empty" CHECK ((char_length(entity_id) > 0)), + CONSTRAINT "metadata_url not empty" CHECK (((metadata_url = NULL::text) OR (char_length(metadata_url) > 0))), + CONSTRAINT "metadata_xml not empty" CHECK ((char_length(metadata_xml) > 0)) +); + + +-- +-- Name: TABLE saml_providers; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.saml_providers IS 'Auth: Manages SAML Identity Provider connections.'; + + +-- +-- Name: saml_relay_states; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.saml_relay_states ( + id uuid NOT NULL, + sso_provider_id uuid NOT NULL, + request_id text NOT NULL, + for_email text, + redirect_to text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + flow_state_id uuid, + CONSTRAINT "request_id not empty" CHECK ((char_length(request_id) > 0)) +); + + +-- +-- Name: TABLE saml_relay_states; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.saml_relay_states IS 'Auth: Contains SAML Relay State information for each Service Provider initiated login.'; + + +-- +-- Name: schema_migrations; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.schema_migrations ( + version character varying(255) NOT NULL +); + + +-- +-- Name: TABLE schema_migrations; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.schema_migrations IS 'Auth: Manages updates to the auth system.'; + + +-- +-- Name: sessions; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.sessions ( + id uuid NOT NULL, + user_id uuid NOT NULL, + created_at timestamp with time zone, + updated_at timestamp with time zone, + factor_id uuid, + aal auth.aal_level, + not_after timestamp with time zone, + refreshed_at timestamp without time zone, + user_agent text, + ip inet, + tag text, + oauth_client_id uuid, + refresh_token_hmac_key text, + refresh_token_counter bigint, + scopes text, + CONSTRAINT sessions_scopes_length CHECK ((char_length(scopes) <= 4096)) +); + + +-- +-- Name: TABLE sessions; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.sessions IS 'Auth: Stores session data associated to a user.'; + + +-- +-- Name: COLUMN sessions.not_after; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sessions.not_after IS 'Auth: Not after is a nullable column that contains a timestamp after which the session should be regarded as expired.'; + + +-- +-- Name: COLUMN sessions.refresh_token_hmac_key; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sessions.refresh_token_hmac_key IS 'Holds a HMAC-SHA256 key used to sign refresh tokens for this session.'; + + +-- +-- Name: COLUMN sessions.refresh_token_counter; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sessions.refresh_token_counter IS 'Holds the ID (counter) of the last issued refresh token.'; + + +-- +-- Name: sso_domains; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.sso_domains ( + id uuid NOT NULL, + sso_provider_id uuid NOT NULL, + domain text NOT NULL, + created_at timestamp with time zone, + updated_at timestamp with time zone, + CONSTRAINT "domain not empty" CHECK ((char_length(domain) > 0)) +); + + +-- +-- Name: TABLE sso_domains; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.sso_domains IS 'Auth: Manages SSO email address domain mapping to an SSO Identity Provider.'; + + +-- +-- Name: sso_providers; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.sso_providers ( + id uuid NOT NULL, + resource_id text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + disabled boolean, + CONSTRAINT "resource_id not empty" CHECK (((resource_id = NULL::text) OR (char_length(resource_id) > 0))) +); + + +-- +-- Name: TABLE sso_providers; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.sso_providers IS 'Auth: Manages SSO identity provider information; see saml_providers for SAML.'; + + +-- +-- Name: COLUMN sso_providers.resource_id; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sso_providers.resource_id IS 'Auth: Uniquely identifies a SSO provider according to a user-chosen resource ID (case insensitive), useful in infrastructure as code.'; + + +-- +-- Name: users; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.users ( + instance_id uuid, + id uuid NOT NULL, + aud character varying(255), + role character varying(255), + email character varying(255), + encrypted_password character varying(255), + email_confirmed_at timestamp with time zone, + invited_at timestamp with time zone, + confirmation_token character varying(255), + confirmation_sent_at timestamp with time zone, + recovery_token character varying(255), + recovery_sent_at timestamp with time zone, + email_change_token_new character varying(255), + email_change character varying(255), + email_change_sent_at timestamp with time zone, + last_sign_in_at timestamp with time zone, + raw_app_meta_data jsonb, + raw_user_meta_data jsonb, + is_super_admin boolean, + created_at timestamp with time zone, + updated_at timestamp with time zone, + phone text DEFAULT NULL::character varying, + phone_confirmed_at timestamp with time zone, + phone_change text DEFAULT ''::character varying, + phone_change_token character varying(255) DEFAULT ''::character varying, + phone_change_sent_at timestamp with time zone, + confirmed_at timestamp with time zone GENERATED ALWAYS AS (LEAST(email_confirmed_at, phone_confirmed_at)) STORED, + email_change_token_current character varying(255) DEFAULT ''::character varying, + email_change_confirm_status smallint DEFAULT 0, + banned_until timestamp with time zone, + reauthentication_token character varying(255) DEFAULT ''::character varying, + reauthentication_sent_at timestamp with time zone, + is_sso_user boolean DEFAULT false NOT NULL, + deleted_at timestamp with time zone, + is_anonymous boolean DEFAULT false NOT NULL, + CONSTRAINT users_email_change_confirm_status_check CHECK (((email_change_confirm_status >= 0) AND (email_change_confirm_status <= 2))) +); + + +-- +-- Name: TABLE users; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.users IS 'Auth: Stores user login data within a secure schema.'; + + +-- +-- Name: COLUMN users.is_sso_user; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.users.is_sso_user IS 'Auth: Set this column to true when the account comes from SSO. These accounts can have duplicate emails.'; + + +-- +-- Name: _db_migrations; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public._db_migrations ( + id integer NOT NULL, + filename text NOT NULL, + hash text NOT NULL, + category text DEFAULT 'migration'::text NOT NULL, + applied_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: _db_migrations_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public._db_migrations_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: _db_migrations_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public._db_migrations_id_seq OWNED BY public._db_migrations.id; + + +-- +-- Name: addon_credits; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.addon_credits ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid, + addon_type text NOT NULL, + balance integer DEFAULT 0 NOT NULL, + total_purchased integer DEFAULT 0 NOT NULL, + total_consumed integer DEFAULT 0 NOT NULL, + low_balance_threshold integer DEFAULT 10, + low_balance_notified boolean DEFAULT false, + daily_limit integer, + hourly_limit integer, + daily_used integer DEFAULT 0, + hourly_used integer DEFAULT 0, + daily_reset_at timestamp with time zone, + hourly_reset_at timestamp with time zone, + from_number_override text, + expires_at timestamp with time zone, + is_active boolean DEFAULT true, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: TABLE addon_credits; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.addon_credits IS 'Saldo de cr??ditos de add-ons por tenant. Um registro por (tenant_id, addon_type).'; + + +-- +-- Name: addon_products; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.addon_products ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + slug text NOT NULL, + name text NOT NULL, + description text, + addon_type text NOT NULL, + icon text DEFAULT 'pi pi-box'::text, + credits_amount integer DEFAULT 0, + price_cents integer DEFAULT 0 NOT NULL, + currency text DEFAULT 'BRL'::text, + is_active boolean DEFAULT true, + is_visible boolean DEFAULT true, + sort_order integer DEFAULT 0, + metadata jsonb DEFAULT '{}'::jsonb, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + deleted_at timestamp with time zone +); + + +-- +-- Name: TABLE addon_products; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.addon_products IS 'Cat??logo de recursos extras vendidos pela plataforma (SMS, servidor, dom??nio, etc.)'; + + +-- +-- Name: addon_transactions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.addon_transactions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid, + addon_type text NOT NULL, + type text NOT NULL, + amount integer NOT NULL, + balance_before integer DEFAULT 0 NOT NULL, + balance_after integer DEFAULT 0 NOT NULL, + product_id uuid, + queue_id uuid, + description text, + admin_user_id uuid, + payment_method text, + payment_reference text, + price_cents integer, + currency text DEFAULT 'BRL'::text, + created_at timestamp with time zone DEFAULT now(), + metadata jsonb DEFAULT '{}'::jsonb +); + + +-- +-- Name: TABLE addon_transactions; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.addon_transactions IS 'Hist??rico de todas as transa????es de cr??ditos: compras, consumo, ajustes, reembolsos.'; + + +-- +-- Name: agenda_bloqueios; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_bloqueios ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + tipo text NOT NULL, + titulo text NOT NULL, + data_inicio date NOT NULL, + data_fim date, + hora_inicio time without time zone, + hora_fim time without time zone, + recorrente boolean DEFAULT false NOT NULL, + dia_semana smallint, + observacao text, + origem text DEFAULT 'manual'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT agenda_bloqueios_tipo_check CHECK ((tipo = ANY (ARRAY['feriado_nacional'::text, 'feriado_municipal'::text, 'bloqueio'::text]))) +); + + +-- +-- Name: agenda_configuracoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_configuracoes ( + owner_id uuid NOT NULL, + duracao_padrao_minutos integer DEFAULT 50 NOT NULL, + intervalo_padrao_minutos integer DEFAULT 0 NOT NULL, + timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL, + usar_horario_admin_custom boolean DEFAULT false NOT NULL, + admin_inicio_visualizacao time without time zone, + admin_fim_visualizacao time without time zone, + admin_slot_visual_minutos integer DEFAULT 30 NOT NULL, + online_ativo boolean DEFAULT false NOT NULL, + online_min_antecedencia_horas integer DEFAULT 24 NOT NULL, + online_max_dias_futuro integer DEFAULT 60 NOT NULL, + online_cancelar_ate_horas integer DEFAULT 12 NOT NULL, + online_reagendar_ate_horas integer DEFAULT 12 NOT NULL, + online_limite_agendamentos_futuros integer DEFAULT 1 NOT NULL, + online_modo text DEFAULT 'automatico'::text NOT NULL, + online_buffer_antes_min integer DEFAULT 0 NOT NULL, + online_buffer_depois_min integer DEFAULT 0 NOT NULL, + online_modalidade text DEFAULT 'ambos'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + usar_granularidade_custom boolean DEFAULT false NOT NULL, + granularidade_min integer, + setup_concluido boolean DEFAULT false NOT NULL, + setup_concluido_em timestamp with time zone, + agenda_view_mode text DEFAULT 'full_24h'::text NOT NULL, + agenda_custom_start time without time zone, + agenda_custom_end time without time zone, + session_duration_min integer DEFAULT 50 NOT NULL, + session_break_min integer DEFAULT 10 NOT NULL, + pausas_semanais jsonb DEFAULT '[]'::jsonb NOT NULL, + setup_clinica_concluido boolean DEFAULT false NOT NULL, + setup_clinica_concluido_em timestamp with time zone, + tenant_id uuid, + jornada_igual_todos boolean DEFAULT true, + slot_mode text DEFAULT 'fixed'::text NOT NULL, + atendimento_mode text DEFAULT 'particular'::text, + CONSTRAINT agenda_configuracoes_admin_slot_visual_minutos_check CHECK ((admin_slot_visual_minutos = ANY (ARRAY[5, 10, 15, 20, 30, 60]))), + CONSTRAINT agenda_configuracoes_atendimento_mode_check CHECK (((atendimento_mode IS NULL) OR (atendimento_mode = ANY (ARRAY['particular'::text, 'convenio'::text, 'ambos'::text])))), + CONSTRAINT agenda_configuracoes_check CHECK (((usar_horario_admin_custom = false) OR ((admin_inicio_visualizacao IS NOT NULL) AND (admin_fim_visualizacao IS NOT NULL) AND (admin_fim_visualizacao > admin_inicio_visualizacao)))), + CONSTRAINT agenda_configuracoes_duracao_padrao_minutos_check CHECK (((duracao_padrao_minutos >= 10) AND (duracao_padrao_minutos <= 240))), + CONSTRAINT agenda_configuracoes_granularidade_min_check CHECK (((granularidade_min IS NULL) OR (granularidade_min = ANY (ARRAY[5, 10, 15, 20, 30, 45, 50, 60])))), + CONSTRAINT agenda_configuracoes_intervalo_padrao_minutos_check CHECK (((intervalo_padrao_minutos >= 0) AND (intervalo_padrao_minutos <= 120))), + CONSTRAINT agenda_configuracoes_online_buffer_antes_min_check CHECK (((online_buffer_antes_min >= 0) AND (online_buffer_antes_min <= 120))), + CONSTRAINT agenda_configuracoes_online_buffer_depois_min_check CHECK (((online_buffer_depois_min >= 0) AND (online_buffer_depois_min <= 120))), + CONSTRAINT agenda_configuracoes_online_cancelar_ate_horas_check CHECK (((online_cancelar_ate_horas >= 0) AND (online_cancelar_ate_horas <= 720))), + CONSTRAINT agenda_configuracoes_online_limite_agendamentos_futuros_check CHECK (((online_limite_agendamentos_futuros >= 1) AND (online_limite_agendamentos_futuros <= 10))), + CONSTRAINT agenda_configuracoes_online_max_dias_futuro_check CHECK (((online_max_dias_futuro >= 1) AND (online_max_dias_futuro <= 365))), + CONSTRAINT agenda_configuracoes_online_min_antecedencia_horas_check CHECK (((online_min_antecedencia_horas >= 0) AND (online_min_antecedencia_horas <= 720))), + CONSTRAINT agenda_configuracoes_online_modalidade_check CHECK ((online_modalidade = ANY (ARRAY['online'::text, 'presencial'::text, 'ambos'::text]))), + CONSTRAINT agenda_configuracoes_online_modo_check CHECK ((online_modo = ANY (ARRAY['automatico'::text, 'aprovacao'::text]))), + CONSTRAINT agenda_configuracoes_online_reagendar_ate_horas_check CHECK (((online_reagendar_ate_horas >= 0) AND (online_reagendar_ate_horas <= 720))), + CONSTRAINT agenda_configuracoes_slot_mode_chk CHECK ((slot_mode = ANY (ARRAY['fixed'::text, 'dynamic'::text]))), + CONSTRAINT session_break_min_chk CHECK (((session_break_min >= 0) AND (session_break_min <= 60))), + CONSTRAINT session_duration_min_chk CHECK (((session_duration_min >= 10) AND (session_duration_min <= 240))) +); + + +-- +-- Name: COLUMN agenda_configuracoes.atendimento_mode; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agenda_configuracoes.atendimento_mode IS 'Modo de atendimento: particular | convenio | ambos'; + + +-- +-- Name: agenda_eventos; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_eventos ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tipo public.tipo_evento_agenda DEFAULT 'sessao'::public.tipo_evento_agenda NOT NULL, + status public.status_evento_agenda DEFAULT 'agendado'::public.status_evento_agenda NOT NULL, + titulo text, + observacoes text, + inicio_em timestamp with time zone NOT NULL, + fim_em timestamp with time zone NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + terapeuta_id uuid, + tenant_id uuid NOT NULL, + visibility_scope text DEFAULT 'public'::text NOT NULL, + mirror_of_event_id uuid, + mirror_source text, + patient_id uuid, + determined_commitment_id uuid, + link_online text, + titulo_custom text, + extra_fields jsonb, + recurrence_id uuid, + recurrence_date date, + modalidade text DEFAULT 'presencial'::text, + price numeric(10,2), + billing_contract_id uuid, + billed boolean DEFAULT false NOT NULL, + services_customized boolean DEFAULT false NOT NULL, + insurance_plan_id uuid, + insurance_guide_number text, + insurance_value numeric(10,2), + insurance_plan_service_id uuid, + CONSTRAINT agenda_eventos_check CHECK ((fim_em > inicio_em)) +); + + +-- +-- Name: COLUMN agenda_eventos.price; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agenda_eventos.price IS 'Valor da sess??o em BRL. Preenchido automaticamente pela tabela professional_pricing do profissional.'; + + +-- +-- Name: agenda_excecoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_excecoes ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + data date NOT NULL, + hora_inicio time without time zone, + hora_fim time without time zone, + tipo public.tipo_excecao_agenda NOT NULL, + motivo text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + status public.status_excecao_agenda DEFAULT 'ativo'::public.status_excecao_agenda NOT NULL, + fonte text DEFAULT 'manual'::text NOT NULL, + aplicavel_online boolean DEFAULT true NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_excecoes_check CHECK ((((hora_inicio IS NULL) AND (hora_fim IS NULL)) OR ((hora_inicio IS NOT NULL) AND (hora_fim IS NOT NULL) AND (hora_fim > hora_inicio)))), + CONSTRAINT agenda_excecoes_fonte_check CHECK ((fonte = ANY (ARRAY['manual'::text, 'feriado_google'::text, 'sistema'::text]))) +); + + +-- +-- Name: agenda_online_slots; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_online_slots ( + id bigint NOT NULL, + owner_id uuid NOT NULL, + weekday integer NOT NULL, + "time" time without time zone NOT NULL, + enabled boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_online_slots_weekday_check CHECK ((weekday = ANY (ARRAY[0, 1, 2, 3, 4, 5, 6]))) +); + + +-- +-- Name: agenda_online_slots_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.agenda_online_slots_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: agenda_online_slots_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.agenda_online_slots_id_seq OWNED BY public.agenda_online_slots.id; + + +-- +-- Name: agenda_regras_semanais; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_regras_semanais ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + dia_semana smallint NOT NULL, + hora_inicio time without time zone NOT NULL, + hora_fim time without time zone NOT NULL, + modalidade text DEFAULT 'ambos'::text NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_regras_semanais_check CHECK ((hora_fim > hora_inicio)), + CONSTRAINT agenda_regras_semanais_dia_semana_check CHECK (((dia_semana >= 0) AND (dia_semana <= 6))), + CONSTRAINT agenda_regras_semanais_modalidade_check CHECK (((modalidade = ANY (ARRAY['online'::text, 'presencial'::text, 'ambos'::text])) OR (modalidade IS NULL))) +); + + +-- +-- Name: agenda_slots_bloqueados_semanais; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_slots_bloqueados_semanais ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + dia_semana smallint NOT NULL, + hora_inicio time without time zone NOT NULL, + motivo text, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_slots_bloqueados_semanais_dia_semana_check CHECK (((dia_semana >= 0) AND (dia_semana <= 6))) +); + + +-- +-- Name: agenda_slots_regras; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_slots_regras ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + dia_semana smallint NOT NULL, + passo_minutos integer NOT NULL, + offset_minutos integer DEFAULT 0 NOT NULL, + buffer_antes_min integer DEFAULT 0 NOT NULL, + buffer_depois_min integer DEFAULT 0 NOT NULL, + min_antecedencia_horas integer DEFAULT 0 NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_slots_regras_buffer_antes_min_check CHECK (((buffer_antes_min >= 0) AND (buffer_antes_min <= 240))), + CONSTRAINT agenda_slots_regras_buffer_depois_min_check CHECK (((buffer_depois_min >= 0) AND (buffer_depois_min <= 240))), + CONSTRAINT agenda_slots_regras_dia_semana_check CHECK (((dia_semana >= 0) AND (dia_semana <= 6))), + CONSTRAINT agenda_slots_regras_min_antecedencia_horas_check CHECK (((min_antecedencia_horas >= 0) AND (min_antecedencia_horas <= 720))), + CONSTRAINT agenda_slots_regras_offset_minutos_check CHECK (((offset_minutos >= 0) AND (offset_minutos <= 55))), + CONSTRAINT agenda_slots_regras_passo_minutos_check CHECK (((passo_minutos >= 5) AND (passo_minutos <= 240))) +); + + +-- +-- Name: agendador_configuracoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agendador_configuracoes ( + owner_id uuid NOT NULL, + tenant_id uuid, + ativo boolean DEFAULT false NOT NULL, + link_slug text, + imagem_fundo_url text, + imagem_header_url text, + logomarca_url text, + cor_primaria text DEFAULT '#4b6bff'::text, + nome_exibicao text, + endereco text, + botao_como_chegar_ativo boolean DEFAULT true NOT NULL, + maps_url text, + modo_aprovacao text DEFAULT 'aprovacao'::text NOT NULL, + modalidade text DEFAULT 'presencial'::text NOT NULL, + tipos_habilitados jsonb DEFAULT '["primeira", "retorno"]'::jsonb NOT NULL, + duracao_sessao_min integer DEFAULT 50 NOT NULL, + antecedencia_minima_horas integer DEFAULT 24 NOT NULL, + prazo_resposta_horas integer DEFAULT 2 NOT NULL, + reserva_horas integer DEFAULT 2 NOT NULL, + pagamento_obrigatorio boolean DEFAULT false NOT NULL, + pix_chave text, + pix_countdown_minutos integer DEFAULT 20 NOT NULL, + triagem_motivo boolean DEFAULT true NOT NULL, + triagem_como_conheceu boolean DEFAULT false NOT NULL, + verificacao_email boolean DEFAULT false NOT NULL, + exigir_aceite_lgpd boolean DEFAULT true NOT NULL, + mensagem_boas_vindas text, + texto_como_se_preparar text, + texto_termos_lgpd text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + pagamento_modo text DEFAULT 'sem_pagamento'::text NOT NULL, + pagamento_metodos_visiveis text[] DEFAULT '{}'::text[] NOT NULL, + CONSTRAINT agendador_configuracoes_antecedencia_check CHECK (((antecedencia_minima_horas >= 0) AND (antecedencia_minima_horas <= 720))), + CONSTRAINT agendador_configuracoes_duracao_check CHECK (((duracao_sessao_min >= 10) AND (duracao_sessao_min <= 240))), + CONSTRAINT agendador_configuracoes_modalidade_check CHECK ((modalidade = ANY (ARRAY['presencial'::text, 'online'::text, 'ambos'::text]))), + CONSTRAINT agendador_configuracoes_modo_check CHECK ((modo_aprovacao = ANY (ARRAY['automatico'::text, 'aprovacao'::text]))), + CONSTRAINT agendador_configuracoes_pix_countdown_check CHECK (((pix_countdown_minutos >= 5) AND (pix_countdown_minutos <= 120))), + CONSTRAINT agendador_configuracoes_prazo_check CHECK (((prazo_resposta_horas >= 1) AND (prazo_resposta_horas <= 72))), + CONSTRAINT agendador_configuracoes_reserva_check CHECK (((reserva_horas >= 1) AND (reserva_horas <= 48))) +); + + +-- +-- Name: COLUMN agendador_configuracoes.pagamento_modo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agendador_configuracoes.pagamento_modo IS 'sem_pagamento | pagar_na_hora | pix_antecipado'; + + +-- +-- Name: COLUMN agendador_configuracoes.pagamento_metodos_visiveis; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agendador_configuracoes.pagamento_metodos_visiveis IS 'M??todos exibidos ao paciente quando pagamento_modo = pagar_na_hora. Ex: {pix, deposito, dinheiro, cartao, convenio}'; + + +-- +-- Name: agendador_solicitacoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agendador_solicitacoes ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + paciente_nome text NOT NULL, + paciente_sobrenome text, + paciente_email text NOT NULL, + paciente_celular text, + paciente_cpf text, + tipo text NOT NULL, + modalidade text NOT NULL, + data_solicitada date NOT NULL, + hora_solicitada time without time zone NOT NULL, + reservado_ate timestamp with time zone, + motivo text, + como_conheceu text, + pix_status text DEFAULT 'pendente'::text, + pix_pago_em timestamp with time zone, + status text DEFAULT 'pendente'::text NOT NULL, + recusado_motivo text, + autorizado_em timestamp with time zone, + autorizado_por uuid, + user_id uuid, + patient_id uuid, + evento_id uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT agendador_sol_modalidade_check CHECK ((modalidade = ANY (ARRAY['presencial'::text, 'online'::text]))), + CONSTRAINT agendador_sol_pix_check CHECK (((pix_status IS NULL) OR (pix_status = ANY (ARRAY['pendente'::text, 'pago'::text, 'expirado'::text])))), + CONSTRAINT agendador_sol_status_check CHECK ((status = ANY (ARRAY['pendente'::text, 'autorizado'::text, 'recusado'::text, 'expirado'::text, 'convertido'::text]))), + CONSTRAINT agendador_sol_tipo_check CHECK ((tipo = ANY (ARRAY['primeira'::text, 'retorno'::text, 'reagendar'::text]))) +); + + +-- +-- Name: billing_contracts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.billing_contracts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + patient_id uuid NOT NULL, + type text NOT NULL, + total_sessions integer, + sessions_used integer DEFAULT 0, + package_price numeric(10,2), + amount numeric(10,2), + billing_interval text, + active_from timestamp with time zone DEFAULT now(), + active_to timestamp with time zone, + status text DEFAULT 'active'::text NOT NULL, + created_at timestamp with time zone DEFAULT now(), + CONSTRAINT billing_contracts_interval_chk CHECK (((billing_interval IS NULL) OR (billing_interval = ANY (ARRAY['monthly'::text, 'weekly'::text])))), + CONSTRAINT billing_contracts_sess_used_chk CHECK (((sessions_used IS NULL) OR (sessions_used >= 0))), + CONSTRAINT billing_contracts_status_chk CHECK ((status = ANY (ARRAY['active'::text, 'completed'::text, 'cancelled'::text]))), + CONSTRAINT billing_contracts_total_sess_chk CHECK (((total_sessions IS NULL) OR (total_sessions > 0))), + CONSTRAINT billing_contracts_type_chk CHECK ((type = ANY (ARRAY['per_session'::text, 'package'::text, 'subscription'::text]))) +); + + +-- +-- Name: commitment_services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.commitment_services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + commitment_id uuid NOT NULL, + service_id uuid NOT NULL, + quantity integer DEFAULT 1 NOT NULL, + unit_price numeric(10,2) NOT NULL, + discount_pct numeric(5,2) DEFAULT 0, + discount_flat numeric(10,2) DEFAULT 0, + final_price numeric(10,2) NOT NULL, + created_at timestamp with time zone DEFAULT now(), + CONSTRAINT commitment_services_disc_flat_chk CHECK ((discount_flat >= (0)::numeric)), + CONSTRAINT commitment_services_disc_pct_chk CHECK (((discount_pct >= (0)::numeric) AND (discount_pct <= (100)::numeric))), + CONSTRAINT commitment_services_final_price_chk CHECK ((final_price >= (0)::numeric)), + CONSTRAINT commitment_services_quantity_chk CHECK ((quantity > 0)) +); + + +-- +-- Name: commitment_time_logs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.commitment_time_logs ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + commitment_id uuid NOT NULL, + calendar_event_id uuid, + source public.commitment_log_source DEFAULT 'manual'::public.commitment_log_source NOT NULL, + started_at timestamp with time zone NOT NULL, + ended_at timestamp with time zone NOT NULL, + minutes integer NOT NULL, + created_by uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: company_profiles; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.company_profiles ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + nome_fantasia text, + razao_social text, + tipo_empresa text, + cnpj text, + ie text, + im text, + cep text, + logradouro text, + numero text, + complemento text, + bairro text, + cidade text, + estado text, + email text, + telefone text, + site text, + logo_url text, + redes_sociais jsonb DEFAULT '[]'::jsonb NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: current_tenant_id; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.current_tenant_id AS + SELECT current_setting('request.jwt.claim.tenant_id'::text, true) AS current_setting; + + +-- +-- Name: determined_commitment_fields; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.determined_commitment_fields ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + commitment_id uuid NOT NULL, + key text NOT NULL, + label text NOT NULL, + field_type public.determined_field_type DEFAULT 'text'::public.determined_field_type NOT NULL, + required boolean DEFAULT false NOT NULL, + sort_order integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: determined_commitments; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.determined_commitments ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + created_by uuid, + is_native boolean DEFAULT false NOT NULL, + native_key text, + is_locked boolean DEFAULT false NOT NULL, + active boolean DEFAULT true NOT NULL, + name text NOT NULL, + description text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + bg_color text, + text_color text +); + + +-- +-- Name: dev_user_credentials; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.dev_user_credentials ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid, + email text NOT NULL, + password_dev text NOT NULL, + kind text DEFAULT 'custom'::text NOT NULL, + note text, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: email_layout_config; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.email_layout_config ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + header_config jsonb DEFAULT '{"layout": null, "content": "", "enabled": false}'::jsonb NOT NULL, + footer_config jsonb DEFAULT '{"layout": null, "content": "", "enabled": false}'::jsonb NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: email_templates_global; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.email_templates_global ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + domain text NOT NULL, + channel text DEFAULT 'email'::text NOT NULL, + subject text NOT NULL, + body_html text NOT NULL, + body_text text, + version integer DEFAULT 1 NOT NULL, + is_active boolean DEFAULT true NOT NULL, + variables jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: email_templates_tenant; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.email_templates_tenant ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid, + template_key text NOT NULL, + subject text, + body_html text, + body_text text, + enabled boolean DEFAULT true NOT NULL, + synced_version integer, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: entitlements_invalidation; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.entitlements_invalidation ( + owner_id uuid NOT NULL, + changed_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.features ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + description text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + descricao text DEFAULT ''::text NOT NULL, + name text DEFAULT ''::text NOT NULL +); + + +-- +-- Name: COLUMN features.descricao; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.features.descricao IS 'Descri????o humana da feature (exibi????o no admin e documenta????o).'; + + +-- +-- Name: feriados; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.feriados ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid, + owner_id uuid, + tipo text DEFAULT 'municipal'::text NOT NULL, + nome text NOT NULL, + data date NOT NULL, + cidade text, + estado text, + observacao text, + bloqueia_sessoes boolean DEFAULT false NOT NULL, + criado_em timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT feriados_tipo_check CHECK ((tipo = ANY (ARRAY['municipal'::text, 'personalizado'::text]))) +); + + +-- +-- Name: financial_categories; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.financial_categories ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid NOT NULL, + name text NOT NULL, + type public.financial_record_type DEFAULT 'receita'::public.financial_record_type NOT NULL, + color text DEFAULT '#6366f1'::text, + icon text DEFAULT 'pi pi-tag'::text, + sort_order integer DEFAULT 0, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: financial_exceptions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.financial_exceptions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid, + tenant_id uuid NOT NULL, + exception_type text NOT NULL, + charge_mode text NOT NULL, + charge_value numeric(10,2), + charge_pct numeric(5,2), + min_hours_notice integer, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + CONSTRAINT financial_exceptions_charge_chk CHECK ((charge_mode = ANY (ARRAY['none'::text, 'full'::text, 'fixed_fee'::text, 'percentage'::text]))), + CONSTRAINT financial_exceptions_type_chk CHECK ((exception_type = ANY (ARRAY['patient_no_show'::text, 'patient_cancellation'::text, 'professional_cancellation'::text]))) +); + + +-- +-- Name: global_notices; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.global_notices ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + title text, + message text DEFAULT ''::text NOT NULL, + variant text DEFAULT 'info'::text NOT NULL, + roles text[] DEFAULT '{}'::text[] NOT NULL, + contexts text[] DEFAULT '{}'::text[] NOT NULL, + starts_at timestamp with time zone, + ends_at timestamp with time zone, + is_active boolean DEFAULT true NOT NULL, + priority integer DEFAULT 0 NOT NULL, + dismissible boolean DEFAULT true NOT NULL, + persist_dismiss boolean DEFAULT true NOT NULL, + dismiss_scope text DEFAULT 'device'::text NOT NULL, + show_once boolean DEFAULT false NOT NULL, + max_views integer, + cooldown_minutes integer, + version integer DEFAULT 1 NOT NULL, + action_type text DEFAULT 'none'::text NOT NULL, + action_label text, + action_url text, + action_route text, + views_count integer DEFAULT 0 NOT NULL, + clicks_count integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + created_by uuid, + content_align text DEFAULT 'left'::text NOT NULL, + link_target text DEFAULT '_blank'::text NOT NULL, + CONSTRAINT global_notices_action_type_check CHECK ((action_type = ANY (ARRAY['none'::text, 'internal'::text, 'external'::text]))), + CONSTRAINT global_notices_content_align_check CHECK ((content_align = ANY (ARRAY['left'::text, 'center'::text, 'right'::text, 'justify'::text]))), + CONSTRAINT global_notices_dismiss_scope_check CHECK ((dismiss_scope = ANY (ARRAY['session'::text, 'device'::text, 'user'::text]))), + CONSTRAINT global_notices_link_target_check CHECK ((link_target = ANY (ARRAY['_blank'::text, '_self'::text, '_parent'::text, '_top'::text]))), + CONSTRAINT global_notices_variant_check CHECK ((variant = ANY (ARRAY['info'::text, 'success'::text, 'warning'::text, 'error'::text]))) +); + + +-- +-- Name: insurance_plan_services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.insurance_plan_services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + insurance_plan_id uuid NOT NULL, + name text NOT NULL, + value numeric(10,2) NOT NULL, + active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: insurance_plans; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.insurance_plans ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + name text NOT NULL, + notes text, + default_value numeric(10,2), + active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: login_carousel_slides; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.login_carousel_slides ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + title text NOT NULL, + body text NOT NULL, + icon text DEFAULT 'pi-star'::text NOT NULL, + ordem integer DEFAULT 0 NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: module_features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.module_features ( + module_id uuid NOT NULL, + feature_id uuid NOT NULL, + enabled boolean DEFAULT true NOT NULL, + limits jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: modules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.modules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + name text NOT NULL, + description text, + is_active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: notice_dismissals; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notice_dismissals ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + notice_id uuid NOT NULL, + user_id uuid NOT NULL, + version integer DEFAULT 1 NOT NULL, + dismissed_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: notification_channels; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_channels ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + channel text NOT NULL, + provider text NOT NULL, + is_active boolean DEFAULT false NOT NULL, + display_name text, + sender_address text, + credentials jsonb DEFAULT '{}'::jsonb NOT NULL, + connection_status text DEFAULT 'disconnected'::text, + last_health_check timestamp with time zone, + metadata jsonb DEFAULT '{}'::jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + twilio_subaccount_sid text, + twilio_phone_number text, + twilio_phone_sid text, + webhook_url text, + cost_per_message_usd numeric(8,6) DEFAULT 0, + price_per_message_brl numeric(8,4) DEFAULT 0, + provisioned_at timestamp with time zone, + CONSTRAINT notification_channels_channel_check CHECK ((channel = ANY (ARRAY['whatsapp'::text, 'email'::text, 'sms'::text]))), + CONSTRAINT notification_channels_connection_status_check CHECK ((connection_status = ANY (ARRAY['connected'::text, 'disconnected'::text, 'connecting'::text, 'qr_pending'::text, 'error'::text]))), + CONSTRAINT notification_channels_provider_check CHECK ((provider = ANY (ARRAY['evolution_api'::text, 'meta_official'::text, 'twilio'::text, 'zenvia'::text, 'sendgrid'::text, 'resend'::text, 'smtp'::text, 'zapi'::text]))) +); + + +-- +-- Name: COLUMN notification_channels.twilio_subaccount_sid; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.twilio_subaccount_sid IS 'SID da subconta Twilio criada para este tenant'; + + +-- +-- Name: COLUMN notification_channels.twilio_phone_number; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.twilio_phone_number IS 'Número WhatsApp provisionado (E.164, ex: +5511999990000)'; + + +-- +-- Name: COLUMN notification_channels.twilio_phone_sid; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.twilio_phone_sid IS 'SID do número de telefone na subconta Twilio'; + + +-- +-- Name: COLUMN notification_channels.webhook_url; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.webhook_url IS 'URL do webhook configurada na Twilio para receber callbacks de status'; + + +-- +-- Name: COLUMN notification_channels.cost_per_message_usd; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.cost_per_message_usd IS 'Custo real Twilio por mensagem WhatsApp (USD)'; + + +-- +-- Name: COLUMN notification_channels.price_per_message_brl; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.price_per_message_brl IS 'Valor cobrado do tenant por mensagem (BRL, inclui margem SaaS)'; + + +-- +-- Name: COLUMN notification_channels.provisioned_at; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.provisioned_at IS 'Timestamp do provisionamento da subconta'; + + +-- +-- Name: notification_logs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_logs ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + queue_id uuid, + agenda_evento_id uuid, + patient_id uuid NOT NULL, + channel text NOT NULL, + template_key text NOT NULL, + schedule_key text, + recipient_address text NOT NULL, + resolved_message text, + resolved_vars jsonb, + status text NOT NULL, + provider text, + provider_message_id text, + provider_status text, + provider_response jsonb, + sent_at timestamp with time zone, + delivered_at timestamp with time zone, + read_at timestamp with time zone, + failed_at timestamp with time zone, + failure_reason text, + estimated_cost_brl numeric(8,4) DEFAULT 0, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT notification_logs_status_check CHECK ((status = ANY (ARRAY['sent'::text, 'delivered'::text, 'read'::text, 'failed'::text, 'bounced'::text, 'opted_out'::text]))) +); + + +-- +-- Name: notification_preferences; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_preferences ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + patient_id uuid NOT NULL, + whatsapp_opt_in boolean DEFAULT true NOT NULL, + email_opt_in boolean DEFAULT true NOT NULL, + sms_opt_in boolean DEFAULT false NOT NULL, + preferred_time_start time without time zone DEFAULT '08:00:00'::time without time zone, + preferred_time_end time without time zone DEFAULT '20:00:00'::time without time zone, + lgpd_consent_given boolean DEFAULT false NOT NULL, + lgpd_consent_date timestamp with time zone, + lgpd_consent_version text, + lgpd_consent_ip inet, + lgpd_opt_out_date timestamp with time zone, + lgpd_opt_out_reason text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone +); + + +-- +-- Name: notification_queue; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_queue ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + agenda_evento_id uuid, + patient_id uuid NOT NULL, + channel text NOT NULL, + template_key text NOT NULL, + schedule_key text NOT NULL, + resolved_vars jsonb DEFAULT '{}'::jsonb NOT NULL, + recipient_address text NOT NULL, + status text DEFAULT 'pendente'::text NOT NULL, + scheduled_at timestamp with time zone NOT NULL, + sent_at timestamp with time zone, + next_retry_at timestamp with time zone, + attempts integer DEFAULT 0 NOT NULL, + max_attempts integer DEFAULT 5 NOT NULL, + last_error text, + idempotency_key text NOT NULL, + provider_message_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT notification_queue_channel_check CHECK ((channel = ANY (ARRAY['whatsapp'::text, 'email'::text, 'sms'::text]))), + CONSTRAINT notification_queue_status_check CHECK ((status = ANY (ARRAY['pendente'::text, 'processando'::text, 'enviado'::text, 'falhou'::text, 'cancelado'::text, 'ignorado'::text]))) +); + + +-- +-- Name: notification_schedules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_schedules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + schedule_key text NOT NULL, + event_type text NOT NULL, + trigger_type text NOT NULL, + offset_minutes integer DEFAULT 0, + whatsapp_enabled boolean DEFAULT true NOT NULL, + email_enabled boolean DEFAULT true NOT NULL, + sms_enabled boolean DEFAULT false NOT NULL, + allowed_time_start time without time zone DEFAULT '08:00:00'::time without time zone, + allowed_time_end time without time zone DEFAULT '20:00:00'::time without time zone, + skip_weekends boolean DEFAULT false, + skip_holidays boolean DEFAULT false, + is_active boolean DEFAULT true NOT NULL, + sort_order integer DEFAULT 0, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + CONSTRAINT notification_schedules_event_type_check CHECK ((event_type = ANY (ARRAY['lembrete_sessao'::text, 'confirmacao_sessao'::text, 'cancelamento_sessao'::text, 'reagendamento'::text, 'cobranca_pendente'::text, 'boas_vindas_paciente'::text]))), + CONSTRAINT notification_schedules_trigger_type_check CHECK ((trigger_type = ANY (ARRAY['before_event'::text, 'after_event'::text, 'immediate'::text]))) +); + + +-- +-- Name: notification_templates; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_templates ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid, + owner_id uuid, + key text NOT NULL, + domain text NOT NULL, + channel text NOT NULL, + event_type text NOT NULL, + body_text text NOT NULL, + meta_template_name text, + meta_template_namespace text, + meta_components jsonb, + meta_status text DEFAULT 'draft'::text, + variables jsonb DEFAULT '[]'::jsonb, + version integer DEFAULT 1 NOT NULL, + is_active boolean DEFAULT true NOT NULL, + is_default boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + CONSTRAINT notification_templates_channel_check CHECK ((channel = ANY (ARRAY['whatsapp'::text, 'sms'::text]))), + CONSTRAINT notification_templates_domain_check CHECK ((domain = ANY (ARRAY['session'::text, 'intake'::text, 'billing'::text, 'system'::text]))), + CONSTRAINT notification_templates_event_type_check CHECK ((event_type = ANY (ARRAY['lembrete_sessao'::text, 'confirmacao_sessao'::text, 'cancelamento_sessao'::text, 'reagendamento'::text, 'cobranca_pendente'::text, 'boas_vindas_paciente'::text, 'intake_recebido'::text, 'intake_aprovado'::text, 'intake_rejeitado'::text]))), + CONSTRAINT notification_templates_meta_status_check CHECK ((meta_status = ANY (ARRAY['draft'::text, 'pending_approval'::text, 'approved'::text, 'rejected'::text]))) +); + + +-- +-- Name: notifications; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notifications ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + type text NOT NULL, + ref_id uuid, + ref_table text, + payload jsonb DEFAULT '{}'::jsonb NOT NULL, + read_at timestamp with time zone, + archived boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT notifications_type_check CHECK ((type = ANY (ARRAY['new_scheduling'::text, 'new_patient'::text, 'recurrence_alert'::text, 'session_status'::text]))) +); + + +-- +-- Name: plan_features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_features ( + plan_id uuid NOT NULL, + feature_id uuid NOT NULL, + enabled boolean DEFAULT true NOT NULL, + limits jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_modules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_modules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + module_id uuid NOT NULL, + status text DEFAULT 'active'::text NOT NULL, + settings jsonb, + provider text DEFAULT 'manual'::text NOT NULL, + provider_item_id text, + installed_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: owner_feature_entitlements; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.owner_feature_entitlements AS + WITH base AS ( + SELECT s.user_id AS owner_id, + f.key AS feature_key, + pf.limits, + 'plan'::text AS source + FROM ((public.subscriptions s + JOIN public.plan_features pf ON (((pf.plan_id = s.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))) + WHERE ((s.status = 'active'::text) AND (s.user_id IS NOT NULL)) + UNION ALL + SELECT tm.owner_id, + f.key AS feature_key, + mf.limits, + 'module'::text AS source + FROM (((public.tenant_modules tm + JOIN public.modules m ON (((m.id = tm.module_id) AND (m.is_active = true)))) + JOIN public.module_features mf ON (((mf.module_id = m.id) AND (mf.enabled = true)))) + JOIN public.features f ON ((f.id = mf.feature_id))) + WHERE ((tm.status = 'active'::text) AND (tm.owner_id IS NOT NULL)) + ) + SELECT owner_id, + feature_key, + array_agg(DISTINCT source) AS sources, + jsonb_agg(limits) FILTER (WHERE (limits IS NOT NULL)) AS limits_list + FROM base + GROUP BY owner_id, feature_key; + + +-- +-- Name: owner_users; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.owner_users ( + owner_id uuid NOT NULL, + user_id uuid NOT NULL, + role text DEFAULT 'admin'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: patient_discounts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_discounts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + patient_id uuid NOT NULL, + discount_pct numeric(5,2) DEFAULT 0, + discount_flat numeric(10,2) DEFAULT 0, + reason text, + active boolean DEFAULT true NOT NULL, + active_from timestamp with time zone DEFAULT now(), + active_to timestamp with time zone, + created_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: patient_group_patient; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_group_patient ( + patient_group_id uuid NOT NULL, + patient_id uuid NOT NULL, + created_at timestamp with time zone DEFAULT now(), + tenant_id uuid NOT NULL +); + + +-- +-- Name: patient_groups; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_groups ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + nome text NOT NULL, + descricao text, + cor text, + is_active boolean DEFAULT true NOT NULL, + is_system boolean DEFAULT false NOT NULL, + owner_id uuid DEFAULT auth.uid() NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + therapist_id uuid, + tenant_id uuid NOT NULL +); + + +-- +-- Name: patient_intake_requests; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_intake_requests ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + token text NOT NULL, + consent boolean DEFAULT false NOT NULL, + status text DEFAULT 'new'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + converted_patient_id uuid, + rejected_reason text, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + cpf text, + rg text, + cep text, + nome_completo text, + email_principal text, + telefone text, + pais text, + cidade text, + estado text, + endereco text, + numero text, + bairro text, + complemento text, + data_nascimento date, + naturalidade text, + genero text, + estado_civil text, + onde_nos_conheceu text, + encaminhado_por text, + observacoes text, + notas_internas text, + email_alternativo text, + telefone_alternativo text, + profissao text, + escolaridade text, + nacionalidade text, + avatar_url text, + tenant_id uuid, + CONSTRAINT chk_intakes_status CHECK ((status = ANY (ARRAY['new'::text, 'converted'::text, 'rejected'::text]))) +); + + +-- +-- Name: patient_invites; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_invites ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + token text NOT NULL, + active boolean DEFAULT true NOT NULL, + expires_at timestamp with time zone, + max_uses integer, + uses integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid +); + + +-- +-- Name: patient_patient_tag; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_patient_tag ( + owner_id uuid NOT NULL, + patient_id uuid NOT NULL, + tag_id uuid NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL +); + + +-- +-- Name: patient_tags; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_tags ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + nome text NOT NULL, + cor text, + is_padrao boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone, + tenant_id uuid NOT NULL +); + + +-- +-- Name: patients; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patients ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + nome_completo text NOT NULL, + email_principal text, + telefone text, + created_at timestamp with time zone DEFAULT now(), + owner_id uuid, + avatar_url text, + status text DEFAULT 'Ativo'::text, + last_attended_at timestamp with time zone, + is_native boolean DEFAULT false, + naturalidade text, + data_nascimento date, + rg text, + cpf text, + identification_color text, + genero text, + estado_civil text, + email_alternativo text, + pais text DEFAULT 'Brasil'::text, + cep text, + cidade text, + estado text, + endereco text, + numero text, + bairro text, + complemento text, + escolaridade text, + profissao text, + nome_parente text, + grau_parentesco text, + telefone_alternativo text, + onde_nos_conheceu text, + encaminhado_por text, + nome_responsavel text, + telefone_responsavel text, + cpf_responsavel text, + observacao_responsavel text, + cobranca_no_responsavel boolean DEFAULT false, + observacoes text, + notas_internas text, + updated_at timestamp with time zone DEFAULT now(), + telefone_parente text, + tenant_id uuid NOT NULL, + responsible_member_id uuid NOT NULL, + user_id uuid, + patient_scope text DEFAULT 'clinic'::text NOT NULL, + therapist_member_id uuid, + CONSTRAINT cpf_responsavel_format_check CHECK (((cpf_responsavel IS NULL) OR (cpf_responsavel ~ '^\d{11}$'::text))), + CONSTRAINT patients_cpf_format_check CHECK (((cpf IS NULL) OR (cpf ~ '^\d{11}$'::text))), + CONSTRAINT patients_patient_scope_check CHECK ((patient_scope = ANY (ARRAY['clinic'::text, 'therapist'::text]))), + CONSTRAINT patients_status_check CHECK ((status = ANY (ARRAY['Ativo'::text, 'Inativo'::text, 'Alta'::text, 'Encaminhado'::text, 'Arquivado'::text]))), + CONSTRAINT patients_therapist_scope_consistency CHECK ((((patient_scope = 'clinic'::text) AND (therapist_member_id IS NULL)) OR ((patient_scope = 'therapist'::text) AND (therapist_member_id IS NOT NULL)))) +); + + +-- +-- Name: COLUMN patients.avatar_url; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.avatar_url IS 'URL p??blica da imagem de avatar armazenada no Supabase Storage'; + + +-- +-- Name: payment_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.payment_settings ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + pix_ativo boolean DEFAULT false NOT NULL, + pix_tipo text DEFAULT 'cpf'::text NOT NULL, + pix_chave text DEFAULT ''::text NOT NULL, + pix_nome_titular text DEFAULT ''::text NOT NULL, + deposito_ativo boolean DEFAULT false NOT NULL, + deposito_banco text DEFAULT ''::text NOT NULL, + deposito_agencia text DEFAULT ''::text NOT NULL, + deposito_conta text DEFAULT ''::text NOT NULL, + deposito_tipo_conta text DEFAULT 'corrente'::text NOT NULL, + deposito_titular text DEFAULT ''::text NOT NULL, + deposito_cpf_cnpj text DEFAULT ''::text NOT NULL, + dinheiro_ativo boolean DEFAULT false NOT NULL, + cartao_ativo boolean DEFAULT false NOT NULL, + cartao_instrucao text DEFAULT ''::text NOT NULL, + convenio_ativo boolean DEFAULT false NOT NULL, + convenio_lista text DEFAULT ''::text NOT NULL, + observacoes_pagamento text DEFAULT ''::text NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: plan_prices; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_prices ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + plan_id uuid NOT NULL, + currency text DEFAULT 'BRL'::text NOT NULL, + "interval" text NOT NULL, + amount_cents integer NOT NULL, + is_active boolean DEFAULT true NOT NULL, + active_from timestamp with time zone DEFAULT now() NOT NULL, + active_to timestamp with time zone, + source text DEFAULT 'manual'::text NOT NULL, + provider text, + provider_price_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT plan_prices_amount_cents_check CHECK ((amount_cents >= 0)), + CONSTRAINT plan_prices_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text]))) +); + + +-- +-- Name: TABLE plan_prices; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.plan_prices IS 'Hist??rico de pre??os por plano (fonte: manual/gateway).'; + + +-- +-- Name: plan_public; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_public ( + plan_id uuid NOT NULL, + public_name text DEFAULT ''::text NOT NULL, + public_description text DEFAULT ''::text NOT NULL, + badge text, + is_featured boolean DEFAULT false NOT NULL, + is_visible boolean DEFAULT true NOT NULL, + sort_order integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: TABLE plan_public; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.plan_public IS 'Configura????o de vitrine (p??gina p??blica) dos planos.'; + + +-- +-- Name: plan_public_bullets; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_public_bullets ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + plan_id uuid NOT NULL, + text text NOT NULL, + sort_order integer DEFAULT 0 NOT NULL, + highlight boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: plans; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plans ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + name text NOT NULL, + description text, + is_active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + price_cents integer DEFAULT 0 NOT NULL, + currency text DEFAULT 'BRL'::text NOT NULL, + billing_interval text DEFAULT 'month'::text NOT NULL, + target text, + max_supervisees integer, + CONSTRAINT plans_target_check CHECK ((target = ANY (ARRAY['patient'::text, 'therapist'::text, 'clinic'::text, 'supervisor'::text]))) +); + + +-- +-- Name: COLUMN plans.name; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.plans.name IS 'Nome interno do plano (admin). A key ?? t??cnica/imut??vel.'; + + +-- +-- Name: COLUMN plans.target; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.plans.target IS 'P??blico-alvo do plano: patient, therapist ou clinic.'; + + +-- +-- Name: COLUMN plans.max_supervisees; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.plans.max_supervisees IS 'Limite de terapeutas que podem ser supervisionados. Apenas para planos target=supervisor. NULL = sem limite.'; + + +-- +-- Name: professional_pricing; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.professional_pricing ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + determined_commitment_id uuid, + price numeric(10,2) NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: TABLE professional_pricing; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.professional_pricing IS 'DEPRECATED: substitu??da por public.services. Manter at?? pr??xima release de limpeza.'; + + +-- +-- Name: profiles; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.profiles ( + id uuid NOT NULL, + role text DEFAULT 'tenant_member'::text NOT NULL, + full_name text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + avatar_url text, + phone text, + bio text, + language text DEFAULT 'pt-BR'::text NOT NULL, + timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL, + notify_system_email boolean DEFAULT true NOT NULL, + notify_reminders boolean DEFAULT true NOT NULL, + notify_news boolean DEFAULT false NOT NULL, + account_type text DEFAULT 'free'::text NOT NULL, + platform_roles text[] DEFAULT '{}'::text[] NOT NULL, + nickname text, + work_description text, + work_description_other text, + site_url text, + social_instagram text, + social_youtube text, + social_facebook text, + social_x text, + social_custom jsonb DEFAULT '[]'::jsonb NOT NULL, + CONSTRAINT profiles_account_type_check CHECK ((account_type = ANY (ARRAY['free'::text, 'patient'::text, 'therapist'::text, 'clinic'::text]))), + CONSTRAINT profiles_role_check CHECK ((role = ANY (ARRAY['saas_admin'::text, 'tenant_member'::text, 'portal_user'::text, 'patient'::text]))) +); + + +-- +-- Name: COLUMN profiles.phone; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.phone IS 'WhatsApp / telefone no formato (99) 99999-9999'; + + +-- +-- Name: COLUMN profiles.bio; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.bio IS 'Breve apresenta????o p??blica (m??x 300 chars no front)'; + + +-- +-- Name: COLUMN profiles.account_type; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.account_type IS 'Tipo de conta: free=sem perfil ainda, patient=paciente (imut??vel), therapist=terapeuta (imut??vel), clinic=cl??nica (imut??vel).'; + + +-- +-- Name: COLUMN profiles.platform_roles; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.platform_roles IS 'Papéis globais de plataforma, independentes de tenant. Ex: editor de microlearning. Atribuído pelo saas_admin.'; + + +-- +-- Name: COLUMN profiles.nickname; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.nickname IS 'Apelido preferido para comunica????o interna (Ag??ncia PSI)'; + + +-- +-- Name: COLUMN profiles.work_description; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.work_description IS 'Categoria de trabalho selecionada no perfil p??blico'; + + +-- +-- Name: COLUMN profiles.work_description_other; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.work_description_other IS 'Descri????o livre quando work_description = ''outro'''; + + +-- +-- Name: COLUMN profiles.site_url; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.site_url IS 'Endere??o do site pessoal ou profissional'; + + +-- +-- Name: COLUMN profiles.social_instagram; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_instagram IS 'Handle ou URL do Instagram'; + + +-- +-- Name: COLUMN profiles.social_youtube; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_youtube IS 'Handle ou URL do canal no YouTube'; + + +-- +-- Name: COLUMN profiles.social_facebook; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_facebook IS 'Handle ou URL da p??gina no Facebook'; + + +-- +-- Name: COLUMN profiles.social_x; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_x IS 'Handle ou URL do perfil no X (Twitter)'; + + +-- +-- Name: COLUMN profiles.social_custom; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_custom IS 'Array JSON com redes adicionais livres: [{name, url}]'; + + +-- +-- Name: recurrence_exceptions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.recurrence_exceptions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + recurrence_id uuid NOT NULL, + tenant_id uuid NOT NULL, + original_date date NOT NULL, + type public.recurrence_exception_type NOT NULL, + new_date date, + new_start_time time without time zone, + new_end_time time without time zone, + modalidade text, + observacoes text, + titulo_custom text, + extra_fields jsonb, + reason text, + agenda_evento_id uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: recurrence_rule_services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.recurrence_rule_services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + rule_id uuid NOT NULL, + service_id uuid NOT NULL, + quantity integer DEFAULT 1 NOT NULL, + unit_price numeric(10,2) NOT NULL, + discount_pct numeric(5,2) DEFAULT 0, + discount_flat numeric(10,2) DEFAULT 0, + final_price numeric(10,2) NOT NULL, + created_at timestamp with time zone DEFAULT now(), + CONSTRAINT recurrence_rule_services_disc_flat_chk CHECK ((discount_flat >= (0)::numeric)), + CONSTRAINT recurrence_rule_services_disc_pct_chk CHECK (((discount_pct >= (0)::numeric) AND (discount_pct <= (100)::numeric))), + CONSTRAINT recurrence_rule_services_final_price_chk CHECK ((final_price >= (0)::numeric)), + CONSTRAINT recurrence_rule_services_quantity_chk CHECK ((quantity > 0)) +); + + +-- +-- Name: recurrence_rules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.recurrence_rules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + therapist_id uuid, + patient_id uuid, + determined_commitment_id uuid, + type public.recurrence_type DEFAULT 'weekly'::public.recurrence_type NOT NULL, + "interval" smallint DEFAULT 1 NOT NULL, + weekdays smallint[] DEFAULT '{}'::smallint[] NOT NULL, + start_time time without time zone NOT NULL, + end_time time without time zone NOT NULL, + timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL, + duration_min smallint DEFAULT 50 NOT NULL, + start_date date NOT NULL, + end_date date, + max_occurrences integer, + open_ended boolean DEFAULT true NOT NULL, + modalidade text DEFAULT 'presencial'::text, + titulo_custom text, + observacoes text, + extra_fields jsonb, + status text DEFAULT 'ativo'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + price numeric(10,2), + insurance_plan_id uuid, + insurance_guide_number text, + insurance_value numeric(10,2), + insurance_plan_service_id uuid, + CONSTRAINT recurrence_rules_dates_chk CHECK (((end_date IS NULL) OR (end_date >= start_date))), + CONSTRAINT recurrence_rules_interval_chk CHECK (("interval" >= 1)), + CONSTRAINT recurrence_rules_status_check CHECK ((status = ANY (ARRAY['ativo'::text, 'pausado'::text, 'cancelado'::text]))), + CONSTRAINT recurrence_rules_times_chk CHECK ((end_time > start_time)) +); + + +-- +-- Name: saas_admins; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_admins ( + user_id uuid NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: saas_doc_votos; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_doc_votos ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + doc_id uuid NOT NULL, + user_id uuid NOT NULL, + util boolean NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: saas_docs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_docs ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + titulo text NOT NULL, + conteudo text DEFAULT ''::text NOT NULL, + medias jsonb DEFAULT '[]'::jsonb NOT NULL, + tipo_acesso text DEFAULT 'usuario'::text NOT NULL, + pagina_path text NOT NULL, + docs_relacionados uuid[] DEFAULT '{}'::uuid[] NOT NULL, + ativo boolean DEFAULT true NOT NULL, + ordem integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + categoria text, + exibir_no_faq boolean DEFAULT false NOT NULL, + votos_util integer DEFAULT 0 NOT NULL, + votos_nao_util integer DEFAULT 0 NOT NULL, + CONSTRAINT saas_docs_tipo_acesso_check CHECK ((tipo_acesso = ANY (ARRAY['admin'::text, 'usuario'::text]))) +); + + +-- +-- Name: COLUMN saas_docs.categoria; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.saas_docs.categoria IS 'Agrupa docs no portal FAQ (ex: Conta, Agenda, Pagamentos)'; + + +-- +-- Name: COLUMN saas_docs.exibir_no_faq; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.saas_docs.exibir_no_faq IS 'Se true, a doc e seus itens FAQ aparecem no portal de FAQ'; + + +-- +-- Name: saas_faq; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_faq ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + pergunta text NOT NULL, + categoria text, + publico boolean DEFAULT false NOT NULL, + votos integer DEFAULT 0 NOT NULL, + titulo text, + conteudo text, + tipo_acesso text DEFAULT 'usuario'::text NOT NULL, + pagina_path text NOT NULL, + pagina_label text, + medias jsonb DEFAULT '[]'::jsonb NOT NULL, + faqs_relacionados uuid[] DEFAULT '{}'::uuid[] NOT NULL, + ativo boolean DEFAULT true NOT NULL, + ordem integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: saas_faq_itens; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_faq_itens ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + doc_id uuid NOT NULL, + pergunta text NOT NULL, + resposta text, + ordem integer DEFAULT 0 NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: TABLE saas_faq_itens; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.saas_faq_itens IS 'Pares pergunta/resposta vinculados a um documento de ajuda'; + + +-- +-- Name: services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + name text NOT NULL, + description text, + price numeric(10,2) NOT NULL, + duration_min integer, + active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: subscription_events; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_events ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + subscription_id uuid NOT NULL, + owner_id uuid NOT NULL, + event_type text NOT NULL, + old_plan_id uuid, + new_plan_id uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL, + created_by uuid, + source text DEFAULT 'admin_ui'::text, + reason text, + metadata jsonb, + owner_type text NOT NULL, + owner_ref uuid NOT NULL, + CONSTRAINT subscription_events_owner_ref_consistency_chk CHECK ((owner_id = owner_ref)), + CONSTRAINT subscription_events_owner_type_chk CHECK (((owner_type IS NULL) OR (owner_type = ANY (ARRAY['clinic'::text, 'therapist'::text])))) +); + + +-- +-- Name: subscription_intents_personal; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_intents_personal ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid NOT NULL, + created_by_user_id uuid, + email text NOT NULL, + plan_id uuid NOT NULL, + plan_key text, + "interval" text, + amount_cents integer, + currency text, + status text DEFAULT 'new'::text NOT NULL, + source text DEFAULT 'manual'::text NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + paid_at timestamp with time zone, + subscription_id uuid, + CONSTRAINT sint_personal_interval_check CHECK ((("interval" IS NULL) OR ("interval" = ANY (ARRAY['month'::text, 'year'::text])))), + CONSTRAINT sint_personal_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text]))) +); + + +-- +-- Name: subscription_intents_tenant; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_intents_tenant ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid NOT NULL, + created_by_user_id uuid, + email text NOT NULL, + plan_id uuid NOT NULL, + plan_key text, + "interval" text, + amount_cents integer, + currency text, + status text DEFAULT 'new'::text NOT NULL, + source text DEFAULT 'manual'::text NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + paid_at timestamp with time zone, + tenant_id uuid NOT NULL, + subscription_id uuid, + CONSTRAINT sint_tenant_interval_check CHECK ((("interval" IS NULL) OR ("interval" = ANY (ARRAY['month'::text, 'year'::text])))), + CONSTRAINT sint_tenant_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text]))) +); + + +-- +-- Name: subscription_intents; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.subscription_intents AS + SELECT t.id, + t.user_id, + t.created_by_user_id, + t.email, + t.plan_id, + t.plan_key, + t."interval", + t.amount_cents, + t.currency, + t.status, + t.source, + t.notes, + t.created_at, + t.paid_at, + t.tenant_id, + t.subscription_id, + 'clinic'::text AS plan_target + FROM public.subscription_intents_tenant t +UNION ALL + SELECT p.id, + p.user_id, + p.created_by_user_id, + p.email, + p.plan_id, + p.plan_key, + p."interval", + p.amount_cents, + p.currency, + p.status, + p.source, + p.notes, + p.created_at, + p.paid_at, + NULL::uuid AS tenant_id, + p.subscription_id, + 'therapist'::text AS plan_target + FROM public.subscription_intents_personal p; + + +-- +-- Name: subscription_intents_legacy; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_intents_legacy ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid, + email text, + plan_key text NOT NULL, + "interval" text NOT NULL, + amount_cents integer NOT NULL, + currency text DEFAULT 'BRL'::text NOT NULL, + status text DEFAULT 'new'::text NOT NULL, + source text DEFAULT 'landing'::text NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + paid_at timestamp with time zone, + tenant_id uuid NOT NULL, + created_by_user_id uuid, + CONSTRAINT subscription_intents_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text]))), + CONSTRAINT subscription_intents_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text]))) +); + + +-- +-- Name: support_sessions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.support_sessions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + admin_id uuid NOT NULL, + token text DEFAULT encode(extensions.gen_random_bytes(32), 'hex'::text) NOT NULL, + expires_at timestamp with time zone DEFAULT (now() + '01:00:00'::interval) NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_feature_exceptions_log; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_feature_exceptions_log ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + feature_key text NOT NULL, + enabled boolean NOT NULL, + reason text, + created_by uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_features ( + tenant_id uuid NOT NULL, + feature_key text NOT NULL, + enabled boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_invites; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_invites ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + email text NOT NULL, + role text NOT NULL, + token uuid DEFAULT gen_random_uuid() NOT NULL, + invited_by uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL, + expires_at timestamp with time zone DEFAULT (now() + '7 days'::interval) NOT NULL, + accepted_at timestamp with time zone, + accepted_by uuid, + revoked_at timestamp with time zone, + revoked_by uuid, + CONSTRAINT tenant_invites_role_check CHECK ((role = ANY (ARRAY['therapist'::text, 'secretary'::text]))) +); + + +-- +-- Name: tenants; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenants ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + name text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + kind text DEFAULT 'saas'::text NOT NULL, + CONSTRAINT tenants_kind_check CHECK ((kind = ANY (ARRAY['therapist'::text, 'clinic_coworking'::text, 'clinic_reception'::text, 'clinic_full'::text, 'clinic'::text, 'saas'::text, 'supervisor'::text]))) +); + + +-- +-- Name: COLUMN tenants.kind; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.tenants.kind IS 'Tipo do tenant. Imut??vel ap??s cria????o. therapist=terapeuta solo. clinic_coworking/clinic_reception/clinic_full=cl??nicas. clinic e saas s??o legados.'; + + +-- +-- Name: therapist_payout_records; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.therapist_payout_records ( + payout_id uuid NOT NULL, + financial_record_id uuid NOT NULL +); + + +-- +-- Name: twilio_subaccount_usage; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.twilio_subaccount_usage ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + channel_id uuid NOT NULL, + twilio_subaccount_sid text NOT NULL, + period_start date NOT NULL, + period_end date NOT NULL, + messages_sent integer DEFAULT 0 NOT NULL, + messages_delivered integer DEFAULT 0 NOT NULL, + messages_failed integer DEFAULT 0 NOT NULL, + cost_usd numeric(12,6) DEFAULT 0 NOT NULL, + cost_brl numeric(12,4) DEFAULT 0 NOT NULL, + revenue_brl numeric(12,4) DEFAULT 0 NOT NULL, + margin_brl numeric(12,4) GENERATED ALWAYS AS ((revenue_brl - cost_brl)) STORED, + usd_brl_rate numeric(8,4) DEFAULT 0, + synced_at timestamp with time zone DEFAULT now(), + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT twilio_subaccount_usage_period_check CHECK ((period_end >= period_start)) +); + + +-- +-- Name: TABLE twilio_subaccount_usage; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.twilio_subaccount_usage IS 'Consumo mensal de mensagens WhatsApp por subconta Twilio. Sincronizado via Edge Function.'; + + +-- +-- Name: user_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.user_settings ( + user_id uuid NOT NULL, + theme_mode text DEFAULT 'dark'::text NOT NULL, + preset text DEFAULT 'Aura'::text NOT NULL, + primary_color text DEFAULT 'noir'::text NOT NULL, + surface_color text DEFAULT 'slate'::text NOT NULL, + menu_mode text DEFAULT 'static'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + layout_variant text DEFAULT 'classic'::text NOT NULL, + CONSTRAINT user_settings_layout_variant_check CHECK ((layout_variant = ANY (ARRAY['classic'::text, 'rail'::text]))), + CONSTRAINT user_settings_menu_mode_check CHECK ((menu_mode = ANY (ARRAY['static'::text, 'overlay'::text]))), + CONSTRAINT user_settings_theme_mode_check CHECK ((theme_mode = ANY (ARRAY['light'::text, 'dark'::text]))) +); + + +-- +-- Name: TABLE user_settings; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.user_settings IS 'Prefer??ncias de apar??ncia e layout por usu??rio'; + + +-- +-- Name: COLUMN user_settings.user_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.user_id IS 'FK = auth.users.id ??? um registro por usu??rio'; + + +-- +-- Name: COLUMN user_settings.theme_mode; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.theme_mode IS 'light | dark'; + + +-- +-- Name: COLUMN user_settings.preset; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.preset IS 'Preset PrimeVue: Aura | Lara | Nora'; + + +-- +-- Name: COLUMN user_settings.primary_color; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.primary_color IS 'Nome da cor prim??ria (ex: blue, emerald, noir)'; + + +-- +-- Name: COLUMN user_settings.surface_color; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.surface_color IS 'Nome da surface (ex: slate, zinc, neutral)'; + + +-- +-- Name: COLUMN user_settings.menu_mode; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.menu_mode IS 'static | overlay'; + + +-- +-- Name: COLUMN user_settings.layout_variant; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.layout_variant IS 'classic (sidebar) | rail (mini rail + painel)'; + + +-- +-- Name: v_auth_users_public; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_auth_users_public AS + SELECT id AS user_id, + email, + created_at, + last_sign_in_at + FROM auth.users u; + + +-- +-- Name: v_cashflow_projection; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_cashflow_projection WITH (security_invoker='on') AS + SELECT gs.mes, + to_char(gs.mes, 'YYYY-MM'::text) AS mes_label, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric) AS receitas_projetadas, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric) AS despesas_projetadas, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = 'pending'::text))), (0)::numeric) AS receitas_pendentes, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = 'overdue'::text))), (0)::numeric) AS receitas_vencidas, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = 'pending'::text))), (0)::numeric) AS despesas_pendentes, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = 'overdue'::text))), (0)::numeric) AS despesas_vencidas, + (COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric) - COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric)) AS saldo_projetado, + count(fr.id) FILTER (WHERE (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text]))) AS count_registros + FROM (generate_series(((date_trunc('month'::text, (CURRENT_DATE)::timestamp with time zone))::date)::timestamp with time zone, (((date_trunc('month'::text, (CURRENT_DATE)::timestamp with time zone) + '5 mons'::interval))::date)::timestamp with time zone, '1 mon'::interval) gs(mes) + LEFT JOIN public.financial_records fr ON (((fr.deleted_at IS NULL) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])) AND ((date_trunc('month'::text, (fr.due_date)::timestamp with time zone))::date = gs.mes)))) + GROUP BY gs.mes + ORDER BY gs.mes; + + +-- +-- Name: VIEW v_cashflow_projection; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.v_cashflow_projection IS 'Fluxo de caixa projetado: pr??ximos 6 meses com totais de pending+overdue por due_date. Usa security_invoker=on ??? filtra automaticamente pelo auth.uid() via RLS de financial_records.'; + + +-- +-- Name: v_commitment_totals; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_commitment_totals AS + SELECT c.tenant_id, + c.id AS commitment_id, + (COALESCE(sum(l.minutes), (0)::bigint))::integer AS total_minutes + FROM (public.determined_commitments c + LEFT JOIN public.commitment_time_logs l ON ((l.commitment_id = c.id))) + GROUP BY c.tenant_id, c.id; + + +-- +-- Name: v_patient_groups_with_counts; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_patient_groups_with_counts AS + SELECT pg.id, + pg.nome, + pg.cor, + pg.owner_id, + pg.is_system, + pg.is_active, + pg.created_at, + pg.updated_at, + (COALESCE(count(pgp.patient_id), (0)::bigint))::integer AS patients_count + FROM (public.patient_groups pg + LEFT JOIN public.patient_group_patient pgp ON ((pgp.patient_group_id = pg.id))) + GROUP BY pg.id, pg.nome, pg.cor, pg.owner_id, pg.is_system, pg.is_active, pg.created_at, pg.updated_at; + + +-- +-- Name: v_plan_active_prices; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_plan_active_prices AS + SELECT plan_id, + max( + CASE + WHEN (("interval" = 'month'::text) AND is_active) THEN amount_cents + ELSE NULL::integer + END) AS monthly_cents, + max( + CASE + WHEN (("interval" = 'year'::text) AND is_active) THEN amount_cents + ELSE NULL::integer + END) AS yearly_cents, + max( + CASE + WHEN (("interval" = 'month'::text) AND is_active) THEN currency + ELSE NULL::text + END) AS monthly_currency, + max( + CASE + WHEN (("interval" = 'year'::text) AND is_active) THEN currency + ELSE NULL::text + END) AS yearly_currency + FROM public.plan_prices + GROUP BY plan_id; + + +-- +-- Name: v_public_pricing; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_public_pricing AS + SELECT p.id AS plan_id, + p.key AS plan_key, + p.name AS plan_name, + COALESCE(pp.public_name, ''::text) AS public_name, + COALESCE(pp.public_description, ''::text) AS public_description, + pp.badge, + COALESCE(pp.is_featured, false) AS is_featured, + COALESCE(pp.is_visible, true) AS is_visible, + COALESCE(pp.sort_order, 0) AS sort_order, + ap.monthly_cents, + ap.yearly_cents, + ap.monthly_currency, + ap.yearly_currency, + COALESCE(( SELECT jsonb_agg(jsonb_build_object('id', b.id, 'text', b.text, 'highlight', b.highlight, 'sort_order', b.sort_order) ORDER BY b.sort_order, b.created_at) AS jsonb_agg + FROM public.plan_public_bullets b + WHERE (b.plan_id = p.id)), '[]'::jsonb) AS bullets, + p.target AS plan_target + FROM ((public.plans p + LEFT JOIN public.plan_public pp ON ((pp.plan_id = p.id))) + LEFT JOIN public.v_plan_active_prices ap ON ((ap.plan_id = p.id))) + ORDER BY COALESCE(pp.sort_order, 0), p.key; + + +-- +-- Name: v_subscription_feature_mismatch; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_subscription_feature_mismatch AS + WITH expected AS ( + SELECT s.user_id AS owner_id, + f.key AS feature_key + FROM ((public.subscriptions s + JOIN public.plan_features pf ON (((pf.plan_id = s.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))) + WHERE ((s.status = 'active'::text) AND (s.tenant_id IS NULL) AND (s.user_id IS NOT NULL)) + ), actual AS ( + SELECT e.owner_id, + e.feature_key + FROM public.owner_feature_entitlements e + ) + SELECT COALESCE(expected.owner_id, actual.owner_id) AS owner_id, + COALESCE(expected.feature_key, actual.feature_key) AS feature_key, + CASE + WHEN ((expected.feature_key IS NOT NULL) AND (actual.feature_key IS NULL)) THEN 'missing_entitlement'::text + WHEN ((expected.feature_key IS NULL) AND (actual.feature_key IS NOT NULL)) THEN 'unexpected_entitlement'::text + ELSE NULL::text + END AS mismatch_type + FROM (expected + FULL JOIN actual ON (((expected.owner_id = actual.owner_id) AND (expected.feature_key = actual.feature_key)))) + WHERE ((expected.feature_key IS NULL) OR (actual.feature_key IS NULL)); + + +-- +-- Name: v_subscription_health; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_subscription_health AS + SELECT s.id AS subscription_id, + s.user_id AS owner_id, + s.status, + s.plan_id, + p.key AS plan_key, + s.current_period_start, + s.current_period_end, + s.updated_at, + CASE + WHEN (s.plan_id IS NULL) THEN 'missing_plan'::text + WHEN (p.id IS NULL) THEN 'invalid_plan'::text + WHEN ((s.status = 'active'::text) AND (s.current_period_end IS NOT NULL) AND (s.current_period_end < now())) THEN 'expired_but_active'::text + WHEN ((s.status = 'canceled'::text) AND (s.current_period_end > now())) THEN 'canceled_but_still_in_period'::text + ELSE 'ok'::text + END AS health_status, + CASE + WHEN (s.tenant_id IS NOT NULL) THEN 'clinic'::text + ELSE 'therapist'::text + END AS owner_type, + COALESCE(s.tenant_id, s.user_id) AS owner_ref + FROM (public.subscriptions s + LEFT JOIN public.plans p ON ((p.id = s.plan_id))); + + +-- +-- Name: v_subscription_health_v2; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_subscription_health_v2 AS + SELECT s.id AS subscription_id, + s.user_id AS owner_id, + CASE + WHEN (s.tenant_id IS NOT NULL) THEN 'clinic'::text + ELSE 'therapist'::text + END AS owner_type, + COALESCE(s.tenant_id, s.user_id) AS owner_ref, + s.status, + s.plan_id, + p.key AS plan_key, + s.current_period_start, + s.current_period_end, + s.updated_at, + CASE + WHEN (s.plan_id IS NULL) THEN 'missing_plan'::text + WHEN (p.id IS NULL) THEN 'invalid_plan'::text + WHEN ((s.status = 'active'::text) AND (s.current_period_end IS NOT NULL) AND (s.current_period_end < now())) THEN 'expired_but_active'::text + WHEN ((s.status = 'canceled'::text) AND (s.current_period_end > now())) THEN 'canceled_but_still_in_period'::text + ELSE 'ok'::text + END AS health_status + FROM (public.subscriptions s + LEFT JOIN public.plans p ON ((p.id = s.plan_id))); + + +-- +-- Name: v_tag_patient_counts; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tag_patient_counts AS + SELECT t.id, + t.owner_id, + t.nome, + t.cor, + t.is_padrao, + t.created_at, + t.updated_at, + (COALESCE(count(ppt.patient_id), (0)::bigint))::integer AS pacientes_count, + (COALESCE(count(ppt.patient_id), (0)::bigint))::integer AS patient_count + FROM (public.patient_tags t + LEFT JOIN public.patient_patient_tag ppt ON (((ppt.tag_id = t.id) AND (ppt.owner_id = t.owner_id)))) + GROUP BY t.id, t.owner_id, t.nome, t.cor, t.is_padrao, t.created_at, t.updated_at; + + +-- +-- Name: v_tenant_active_subscription; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_active_subscription AS + SELECT DISTINCT ON (tenant_id) tenant_id, + plan_id, + plan_key, + "interval", + status, + current_period_start, + current_period_end, + created_at + FROM public.subscriptions s + WHERE ((tenant_id IS NOT NULL) AND (status = 'active'::text) AND ((current_period_end IS NULL) OR (current_period_end > now()))) + ORDER BY tenant_id, created_at DESC; + + +-- +-- Name: v_tenant_entitlements; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_entitlements AS + SELECT a.tenant_id, + f.key AS feature_key, + true AS allowed + FROM ((public.v_tenant_active_subscription a + JOIN public.plan_features pf ON (((pf.plan_id = a.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))); + + +-- +-- Name: v_tenant_entitlements_full; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_entitlements_full AS + SELECT a.tenant_id, + f.key AS feature_key, + (pf.enabled = true) AS allowed, + pf.limits, + a.plan_id, + p.key AS plan_key + FROM (((public.v_tenant_active_subscription a + JOIN public.plan_features pf ON ((pf.plan_id = a.plan_id))) + JOIN public.features f ON ((f.id = pf.feature_id))) + JOIN public.plans p ON ((p.id = a.plan_id))); + + +-- +-- Name: v_tenant_entitlements_json; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_entitlements_json AS + SELECT tenant_id, + max(plan_key) AS plan_key, + jsonb_object_agg(feature_key, jsonb_build_object('allowed', allowed, 'limits', COALESCE(limits, '{}'::jsonb)) ORDER BY feature_key) AS entitlements + FROM public.v_tenant_entitlements_full + GROUP BY tenant_id; + + +-- +-- Name: v_tenant_feature_exceptions; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_feature_exceptions AS + SELECT tf.tenant_id, + a.plan_key, + tf.feature_key, + 'commercial_exception'::text AS exception_type + FROM ((public.tenant_features tf + JOIN public.v_tenant_active_subscription a ON ((a.tenant_id = tf.tenant_id))) + LEFT JOIN public.v_tenant_entitlements_full v ON (((v.tenant_id = tf.tenant_id) AND (v.feature_key = tf.feature_key)))) + WHERE ((tf.enabled = true) AND (COALESCE(v.allowed, false) = false)); + + +-- +-- Name: v_tenant_feature_mismatch; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_feature_mismatch AS + WITH plan_allowed AS ( + SELECT v.tenant_id, + v.feature_key, + v.allowed + FROM public.v_tenant_entitlements_full v + ), overrides AS ( + SELECT tf.tenant_id, + tf.feature_key, + tf.enabled + FROM public.tenant_features tf + ) + SELECT o.tenant_id, + o.feature_key, + CASE + WHEN ((o.enabled = true) AND (COALESCE(p.allowed, false) = false)) THEN 'unexpected_override'::text + ELSE NULL::text + END AS mismatch_type + FROM (overrides o + LEFT JOIN plan_allowed p ON (((p.tenant_id = o.tenant_id) AND (p.feature_key = o.feature_key)))) + WHERE ((o.enabled = true) AND (COALESCE(p.allowed, false) = false)); + + +-- +-- Name: v_tenant_members_with_profiles; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_members_with_profiles AS + SELECT tm.id AS tenant_member_id, + tm.tenant_id, + tm.user_id, + tm.role, + tm.status, + tm.created_at, + p.full_name, + au.email + FROM ((public.tenant_members tm + LEFT JOIN public.profiles p ON ((p.id = tm.user_id))) + LEFT JOIN auth.users au ON ((au.id = tm.user_id))); + + +-- +-- Name: v_tenant_people; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_people AS + SELECT 'member'::text AS type, + m.tenant_id, + m.user_id, + u.email, + m.role, + m.status, + NULL::uuid AS invite_token, + NULL::timestamp with time zone AS expires_at + FROM (public.tenant_members m + JOIN auth.users u ON ((u.id = m.user_id))) +UNION ALL + SELECT 'invite'::text AS type, + i.tenant_id, + NULL::uuid AS user_id, + i.email, + i.role, + 'invited'::text AS status, + i.token AS invite_token, + i.expires_at + FROM public.tenant_invites i + WHERE ((i.accepted_at IS NULL) AND (i.revoked_at IS NULL)); + + +-- +-- Name: v_tenant_staff; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_staff AS + SELECT ('m_'::text || (tm.id)::text) AS row_id, + tm.tenant_id, + tm.user_id, + tm.role, + tm.status, + tm.created_at, + p.full_name, + au.email, + NULL::uuid AS invite_token + FROM ((public.tenant_members tm + LEFT JOIN public.profiles p ON ((p.id = tm.user_id))) + LEFT JOIN auth.users au ON ((au.id = tm.user_id))) +UNION ALL + SELECT ('i_'::text || (ti.id)::text) AS row_id, + ti.tenant_id, + NULL::uuid AS user_id, + ti.role, + 'invited'::text AS status, + ti.created_at, + NULL::text AS full_name, + ti.email, + ti.token AS invite_token + FROM public.tenant_invites ti + WHERE ((ti.accepted_at IS NULL) AND (ti.revoked_at IS NULL) AND (ti.expires_at > now())); + + +-- +-- Name: v_twilio_whatsapp_overview; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_twilio_whatsapp_overview AS + SELECT nc.id AS channel_id, + nc.tenant_id, + nc.owner_id, + nc.is_active, + nc.connection_status, + nc.display_name, + nc.twilio_subaccount_sid, + nc.twilio_phone_number, + nc.twilio_phone_sid, + nc.cost_per_message_usd, + nc.price_per_message_brl, + nc.provisioned_at, + nc.created_at, + nc.updated_at, + COALESCE(u.messages_sent, 0) AS current_month_sent, + COALESCE(u.messages_delivered, 0) AS current_month_delivered, + COALESCE(u.messages_failed, 0) AS current_month_failed, + COALESCE(u.cost_usd, (0)::numeric) AS current_month_cost_usd, + COALESCE(u.cost_brl, (0)::numeric) AS current_month_cost_brl, + COALESCE(u.revenue_brl, (0)::numeric) AS current_month_revenue_brl, + COALESCE(u.margin_brl, (0)::numeric) AS current_month_margin_brl + FROM (public.notification_channels nc + LEFT JOIN public.twilio_subaccount_usage u ON (((u.channel_id = nc.id) AND (u.period_start = (date_trunc('month'::text, (CURRENT_DATE)::timestamp with time zone))::date)))) + WHERE ((nc.channel = 'whatsapp'::text) AND (nc.provider = 'twilio'::text) AND (nc.deleted_at IS NULL)); + + +-- +-- Name: VIEW v_twilio_whatsapp_overview; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.v_twilio_whatsapp_overview IS 'Visão consolidada de subcontas Twilio WhatsApp com uso do mês corrente.'; + + +-- +-- Name: v_user_active_subscription; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_user_active_subscription AS + SELECT DISTINCT ON (user_id) user_id, + plan_id, + plan_key, + "interval", + status, + current_period_start, + current_period_end, + created_at + FROM public.subscriptions s + WHERE ((tenant_id IS NULL) AND (user_id IS NOT NULL) AND (status = 'active'::text) AND ((current_period_end IS NULL) OR (current_period_end > now()))) + ORDER BY user_id, created_at DESC; + + +-- +-- Name: v_user_entitlements; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_user_entitlements AS + SELECT a.user_id, + f.key AS feature_key, + true AS allowed + FROM ((public.v_user_active_subscription a + JOIN public.plan_features pf ON (((pf.plan_id = a.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))); + + +-- +-- Name: messages; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +) +PARTITION BY RANGE (inserted_at); + + +-- +-- Name: messages_2026_03_24; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_24 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_25; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_25 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_26; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_26 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_27; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_27 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_28; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_28 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_29; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_29 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_30; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_30 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: schema_migrations; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.schema_migrations ( + version bigint NOT NULL, + inserted_at timestamp(0) without time zone +); + + +-- +-- Name: subscription; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.subscription ( + id bigint NOT NULL, + subscription_id uuid NOT NULL, + entity regclass NOT NULL, + filters realtime.user_defined_filter[] DEFAULT '{}'::realtime.user_defined_filter[] NOT NULL, + claims jsonb NOT NULL, + claims_role regrole GENERATED ALWAYS AS (realtime.to_regrole((claims ->> 'role'::text))) STORED NOT NULL, + created_at timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL +); + + +-- +-- Name: subscription_id_seq; Type: SEQUENCE; Schema: realtime; Owner: - +-- + +ALTER TABLE realtime.subscription ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY ( + SEQUENCE NAME realtime.subscription_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: buckets; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.buckets ( + id text NOT NULL, + name text NOT NULL, + owner uuid, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + public boolean DEFAULT false, + avif_autodetection boolean DEFAULT false, + file_size_limit bigint, + allowed_mime_types text[], + owner_id text, + type storage.buckettype DEFAULT 'STANDARD'::storage.buckettype NOT NULL +); + + +-- +-- Name: COLUMN buckets.owner; Type: COMMENT; Schema: storage; Owner: - +-- + +COMMENT ON COLUMN storage.buckets.owner IS 'Field is deprecated, use owner_id instead'; + + +-- +-- Name: buckets_analytics; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.buckets_analytics ( + name text NOT NULL, + type storage.buckettype DEFAULT 'ANALYTICS'::storage.buckettype NOT NULL, + format text DEFAULT 'ICEBERG'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL, + deleted_at timestamp with time zone +); + + +-- +-- Name: buckets_vectors; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.buckets_vectors ( + id text NOT NULL, + type storage.buckettype DEFAULT 'VECTOR'::storage.buckettype NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: iceberg_namespaces; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.iceberg_namespaces ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + bucket_name text NOT NULL, + name text NOT NULL COLLATE pg_catalog."C", + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + metadata jsonb DEFAULT '{}'::jsonb NOT NULL, + catalog_id uuid NOT NULL +); + + +-- +-- Name: iceberg_tables; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.iceberg_tables ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + namespace_id uuid NOT NULL, + bucket_name text NOT NULL, + name text NOT NULL COLLATE pg_catalog."C", + location text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + remote_table_id text, + shard_key text, + shard_id text, + catalog_id uuid NOT NULL +); + + +-- +-- Name: migrations; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.migrations ( + id integer NOT NULL, + name character varying(100) NOT NULL, + hash character varying(40) NOT NULL, + executed_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP +); + + +-- +-- Name: objects; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.objects ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + bucket_id text, + name text, + owner uuid, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + last_accessed_at timestamp with time zone DEFAULT now(), + metadata jsonb, + path_tokens text[] GENERATED ALWAYS AS (string_to_array(name, '/'::text)) STORED, + version text, + owner_id text, + user_metadata jsonb +); + + +-- +-- Name: COLUMN objects.owner; Type: COMMENT; Schema: storage; Owner: - +-- + +COMMENT ON COLUMN storage.objects.owner IS 'Field is deprecated, use owner_id instead'; + + +-- +-- Name: s3_multipart_uploads; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.s3_multipart_uploads ( + id text NOT NULL, + in_progress_size bigint DEFAULT 0 NOT NULL, + upload_signature text NOT NULL, + bucket_id text NOT NULL, + key text NOT NULL COLLATE pg_catalog."C", + version text NOT NULL, + owner_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + user_metadata jsonb +); + + +-- +-- Name: s3_multipart_uploads_parts; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.s3_multipart_uploads_parts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + upload_id text NOT NULL, + size bigint DEFAULT 0 NOT NULL, + part_number integer NOT NULL, + bucket_id text NOT NULL, + key text NOT NULL COLLATE pg_catalog."C", + etag text NOT NULL, + owner_id text, + version text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: vector_indexes; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.vector_indexes ( + id text DEFAULT gen_random_uuid() NOT NULL, + name text NOT NULL COLLATE pg_catalog."C", + bucket_id text NOT NULL, + data_type text NOT NULL, + dimension integer NOT NULL, + distance_metric text NOT NULL, + metadata_configuration jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: hooks; Type: TABLE; Schema: supabase_functions; Owner: - +-- + +CREATE TABLE supabase_functions.hooks ( + id bigint NOT NULL, + hook_table_id integer NOT NULL, + hook_name text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + request_id bigint +); + + +-- +-- Name: TABLE hooks; Type: COMMENT; Schema: supabase_functions; Owner: - +-- + +COMMENT ON TABLE supabase_functions.hooks IS 'Supabase Functions Hooks: Audit trail for triggered hooks.'; + + +-- +-- Name: hooks_id_seq; Type: SEQUENCE; Schema: supabase_functions; Owner: - +-- + +CREATE SEQUENCE supabase_functions.hooks_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: hooks_id_seq; Type: SEQUENCE OWNED BY; Schema: supabase_functions; Owner: - +-- + +ALTER SEQUENCE supabase_functions.hooks_id_seq OWNED BY supabase_functions.hooks.id; + + +-- +-- Name: migrations; Type: TABLE; Schema: supabase_functions; Owner: - +-- + +CREATE TABLE supabase_functions.migrations ( + version text NOT NULL, + inserted_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: messages_2026_03_24; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_24 FOR VALUES FROM ('2026-03-24 00:00:00') TO ('2026-03-25 00:00:00'); + + +-- +-- Name: messages_2026_03_25; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_25 FOR VALUES FROM ('2026-03-25 00:00:00') TO ('2026-03-26 00:00:00'); + + +-- +-- Name: messages_2026_03_26; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_26 FOR VALUES FROM ('2026-03-26 00:00:00') TO ('2026-03-27 00:00:00'); + + +-- +-- Name: messages_2026_03_27; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_27 FOR VALUES FROM ('2026-03-27 00:00:00') TO ('2026-03-28 00:00:00'); + + +-- +-- Name: messages_2026_03_28; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_28 FOR VALUES FROM ('2026-03-28 00:00:00') TO ('2026-03-29 00:00:00'); + + +-- +-- Name: messages_2026_03_29; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_29 FOR VALUES FROM ('2026-03-29 00:00:00') TO ('2026-03-30 00:00:00'); + + +-- +-- Name: messages_2026_03_30; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_30 FOR VALUES FROM ('2026-03-30 00:00:00') TO ('2026-03-31 00:00:00'); + + +-- +-- Name: refresh_tokens id; Type: DEFAULT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens ALTER COLUMN id SET DEFAULT nextval('auth.refresh_tokens_id_seq'::regclass); + + +-- +-- Name: _db_migrations id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._db_migrations ALTER COLUMN id SET DEFAULT nextval('public._db_migrations_id_seq'::regclass); + + +-- +-- Name: agenda_online_slots id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots ALTER COLUMN id SET DEFAULT nextval('public.agenda_online_slots_id_seq'::regclass); + + +-- +-- Name: hooks id; Type: DEFAULT; Schema: supabase_functions; Owner: - +-- + +ALTER TABLE ONLY supabase_functions.hooks ALTER COLUMN id SET DEFAULT nextval('supabase_functions.hooks_id_seq'::regclass); + + +-- +-- Name: extensions extensions_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.extensions + ADD CONSTRAINT extensions_pkey PRIMARY KEY (id); + + +-- +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.schema_migrations + ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.tenants + ADD CONSTRAINT tenants_pkey PRIMARY KEY (id); + + +-- +-- Name: mfa_amr_claims amr_id_pk; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_amr_claims + ADD CONSTRAINT amr_id_pk PRIMARY KEY (id); + + +-- +-- Name: audit_log_entries audit_log_entries_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.audit_log_entries + ADD CONSTRAINT audit_log_entries_pkey PRIMARY KEY (id); + + +-- +-- Name: flow_state flow_state_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.flow_state + ADD CONSTRAINT flow_state_pkey PRIMARY KEY (id); + + +-- +-- Name: identities identities_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.identities + ADD CONSTRAINT identities_pkey PRIMARY KEY (id); + + +-- +-- Name: identities identities_provider_id_provider_unique; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.identities + ADD CONSTRAINT identities_provider_id_provider_unique UNIQUE (provider_id, provider); + + +-- +-- Name: instances instances_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.instances + ADD CONSTRAINT instances_pkey PRIMARY KEY (id); + + +-- +-- Name: mfa_amr_claims mfa_amr_claims_session_id_authentication_method_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_amr_claims + ADD CONSTRAINT mfa_amr_claims_session_id_authentication_method_pkey UNIQUE (session_id, authentication_method); + + +-- +-- Name: mfa_challenges mfa_challenges_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_challenges + ADD CONSTRAINT mfa_challenges_pkey PRIMARY KEY (id); + + +-- +-- Name: mfa_factors mfa_factors_last_challenged_at_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_factors + ADD CONSTRAINT mfa_factors_last_challenged_at_key UNIQUE (last_challenged_at); + + +-- +-- Name: mfa_factors mfa_factors_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_factors + ADD CONSTRAINT mfa_factors_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_authorizations oauth_authorizations_authorization_code_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_authorization_code_key UNIQUE (authorization_code); + + +-- +-- Name: oauth_authorizations oauth_authorizations_authorization_id_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_authorization_id_key UNIQUE (authorization_id); + + +-- +-- Name: oauth_authorizations oauth_authorizations_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_client_states oauth_client_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_client_states + ADD CONSTRAINT oauth_client_states_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_clients oauth_clients_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_clients + ADD CONSTRAINT oauth_clients_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_consents oauth_consents_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_consents oauth_consents_user_client_unique; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_user_client_unique UNIQUE (user_id, client_id); + + +-- +-- Name: one_time_tokens one_time_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.one_time_tokens + ADD CONSTRAINT one_time_tokens_pkey PRIMARY KEY (id); + + +-- +-- Name: refresh_tokens refresh_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens + ADD CONSTRAINT refresh_tokens_pkey PRIMARY KEY (id); + + +-- +-- Name: refresh_tokens refresh_tokens_token_unique; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens + ADD CONSTRAINT refresh_tokens_token_unique UNIQUE (token); + + +-- +-- Name: saml_providers saml_providers_entity_id_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_providers + ADD CONSTRAINT saml_providers_entity_id_key UNIQUE (entity_id); + + +-- +-- Name: saml_providers saml_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_providers + ADD CONSTRAINT saml_providers_pkey PRIMARY KEY (id); + + +-- +-- Name: saml_relay_states saml_relay_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_relay_states + ADD CONSTRAINT saml_relay_states_pkey PRIMARY KEY (id); + + +-- +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.schema_migrations + ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: sessions sessions_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sessions + ADD CONSTRAINT sessions_pkey PRIMARY KEY (id); + + +-- +-- Name: sso_domains sso_domains_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sso_domains + ADD CONSTRAINT sso_domains_pkey PRIMARY KEY (id); + + +-- +-- Name: sso_providers sso_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sso_providers + ADD CONSTRAINT sso_providers_pkey PRIMARY KEY (id); + + +-- +-- Name: users users_phone_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.users + ADD CONSTRAINT users_phone_key UNIQUE (phone); + + +-- +-- Name: users users_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.users + ADD CONSTRAINT users_pkey PRIMARY KEY (id); + + +-- +-- Name: _db_migrations _db_migrations_filename_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._db_migrations + ADD CONSTRAINT _db_migrations_filename_key UNIQUE (filename); + + +-- +-- Name: _db_migrations _db_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._db_migrations + ADD CONSTRAINT _db_migrations_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_credits addon_credits_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT addon_credits_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_products addon_products_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_products + ADD CONSTRAINT addon_products_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_products addon_products_slug_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_products + ADD CONSTRAINT addon_products_slug_key UNIQUE (slug); + + +-- +-- Name: addon_transactions addon_transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_bloqueios + ADD CONSTRAINT agenda_bloqueios_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_configuracoes + ADD CONSTRAINT agenda_configuracoes_pkey PRIMARY KEY (owner_id); + + +-- +-- Name: agenda_eventos agenda_eventos_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_eventos agenda_eventos_sem_sobreposicao; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_sem_sobreposicao EXCLUDE USING gist (owner_id WITH =, tstzrange(inicio_em, fim_em, '[)'::text) WITH &&); + + +-- +-- Name: agenda_excecoes agenda_excecoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_excecoes + ADD CONSTRAINT agenda_excecoes_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_online_slots agenda_online_slots_owner_id_weekday_time_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_owner_id_weekday_time_key UNIQUE (owner_id, weekday, "time"); + + +-- +-- Name: agenda_online_slots agenda_online_slots_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_regras_semanais + ADD CONSTRAINT agenda_regras_semanais_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_regras_semanais + ADD CONSTRAINT agenda_regras_semanais_unique UNIQUE (owner_id, dia_semana, hora_inicio, hora_fim, modalidade); + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_seman_owner_id_dia_semana_hora_inic_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais + ADD CONSTRAINT agenda_slots_bloqueados_seman_owner_id_dia_semana_hora_inic_key UNIQUE (owner_id, dia_semana, hora_inicio); + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais + ADD CONSTRAINT agenda_slots_bloqueados_semanais_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_owner_id_dia_semana_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_regras + ADD CONSTRAINT agenda_slots_regras_owner_id_dia_semana_key UNIQUE (owner_id, dia_semana); + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_regras + ADD CONSTRAINT agenda_slots_regras_pkey PRIMARY KEY (id); + + +-- +-- Name: agendador_configuracoes agendador_configuracoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_configuracoes + ADD CONSTRAINT agendador_configuracoes_pkey PRIMARY KEY (owner_id); + + +-- +-- Name: agendador_solicitacoes agendador_solicitacoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_solicitacoes + ADD CONSTRAINT agendador_solicitacoes_pkey PRIMARY KEY (id); + + +-- +-- Name: billing_contracts billing_contracts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.billing_contracts + ADD CONSTRAINT billing_contracts_pkey PRIMARY KEY (id); + + +-- +-- Name: commitment_services commitment_services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_services + ADD CONSTRAINT commitment_services_pkey PRIMARY KEY (id); + + +-- +-- Name: commitment_time_logs commitment_time_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_pkey PRIMARY KEY (id); + + +-- +-- Name: company_profiles company_profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.company_profiles + ADD CONSTRAINT company_profiles_pkey PRIMARY KEY (id); + + +-- +-- Name: company_profiles company_profiles_tenant_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.company_profiles + ADD CONSTRAINT company_profiles_tenant_id_key UNIQUE (tenant_id); + + +-- +-- Name: determined_commitment_fields determined_commitment_fields_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitment_fields + ADD CONSTRAINT determined_commitment_fields_pkey PRIMARY KEY (id); + + +-- +-- Name: determined_commitments determined_commitments_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitments + ADD CONSTRAINT determined_commitments_pkey PRIMARY KEY (id); + + +-- +-- Name: determined_commitments determined_commitments_tenant_native_key_uq; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitments + ADD CONSTRAINT determined_commitments_tenant_native_key_uq UNIQUE (tenant_id, native_key); + + +-- +-- Name: dev_user_credentials dev_user_credentials_email_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.dev_user_credentials + ADD CONSTRAINT dev_user_credentials_email_key UNIQUE (email); + + +-- +-- Name: dev_user_credentials dev_user_credentials_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.dev_user_credentials + ADD CONSTRAINT dev_user_credentials_pkey PRIMARY KEY (id); + + +-- +-- Name: email_layout_config email_layout_config_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_layout_config + ADD CONSTRAINT email_layout_config_pkey PRIMARY KEY (id); + + +-- +-- Name: email_layout_config email_layout_config_tenant_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_layout_config + ADD CONSTRAINT email_layout_config_tenant_id_key UNIQUE (tenant_id); + + +-- +-- Name: email_templates_global email_templates_global_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_global + ADD CONSTRAINT email_templates_global_key_key UNIQUE (key); + + +-- +-- Name: email_templates_global email_templates_global_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_global + ADD CONSTRAINT email_templates_global_pkey PRIMARY KEY (id); + + +-- +-- Name: email_templates_tenant email_templates_tenant_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_pkey PRIMARY KEY (id); + + +-- +-- Name: email_templates_tenant email_templates_tenant_tenant_id_owner_id_template_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_tenant_id_owner_id_template_key_key UNIQUE (tenant_id, owner_id, template_key); + + +-- +-- Name: entitlements_invalidation entitlements_invalidation_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entitlements_invalidation + ADD CONSTRAINT entitlements_invalidation_pkey PRIMARY KEY (owner_id); + + +-- +-- Name: features features_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.features + ADD CONSTRAINT features_key_key UNIQUE (key); + + +-- +-- Name: features features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.features + ADD CONSTRAINT features_pkey PRIMARY KEY (id); + + +-- +-- Name: feriados feriados_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_pkey PRIMARY KEY (id); + + +-- +-- Name: feriados feriados_tenant_id_data_nome_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_tenant_id_data_nome_key UNIQUE (tenant_id, data, nome); + + +-- +-- Name: financial_categories financial_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_categories + ADD CONSTRAINT financial_categories_pkey PRIMARY KEY (id); + + +-- +-- Name: financial_exceptions financial_exceptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_exceptions + ADD CONSTRAINT financial_exceptions_pkey PRIMARY KEY (id); + + +-- +-- Name: financial_records financial_records_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_pkey PRIMARY KEY (id); + + +-- +-- Name: global_notices global_notices_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.global_notices + ADD CONSTRAINT global_notices_pkey PRIMARY KEY (id); + + +-- +-- Name: insurance_plan_services insurance_plan_services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plan_services + ADD CONSTRAINT insurance_plan_services_pkey PRIMARY KEY (id); + + +-- +-- Name: insurance_plans insurance_plans_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plans + ADD CONSTRAINT insurance_plans_pkey PRIMARY KEY (id); + + +-- +-- Name: login_carousel_slides login_carousel_slides_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.login_carousel_slides + ADD CONSTRAINT login_carousel_slides_pkey PRIMARY KEY (id); + + +-- +-- Name: module_features module_features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.module_features + ADD CONSTRAINT module_features_pkey PRIMARY KEY (module_id, feature_id); + + +-- +-- Name: modules modules_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.modules + ADD CONSTRAINT modules_key_key UNIQUE (key); + + +-- +-- Name: modules modules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.modules + ADD CONSTRAINT modules_pkey PRIMARY KEY (id); + + +-- +-- Name: notice_dismissals notice_dismissals_notice_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_notice_id_user_id_key UNIQUE (notice_id, user_id); + + +-- +-- Name: notice_dismissals notice_dismissals_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_channels notification_channels_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_channels + ADD CONSTRAINT notification_channels_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_logs notification_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_logs + ADD CONSTRAINT notification_logs_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_preferences notification_preferences_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_preferences + ADD CONSTRAINT notification_preferences_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_queue notification_queue_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_queue + ADD CONSTRAINT notification_queue_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_schedules notification_schedules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_schedules + ADD CONSTRAINT notification_schedules_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_templates notification_templates_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_templates + ADD CONSTRAINT notification_templates_pkey PRIMARY KEY (id); + + +-- +-- Name: notifications notifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT notifications_pkey PRIMARY KEY (id); + + +-- +-- Name: owner_users owner_users_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.owner_users + ADD CONSTRAINT owner_users_pkey PRIMARY KEY (owner_id, user_id); + + +-- +-- Name: patient_discounts patient_discounts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_discounts + ADD CONSTRAINT patient_discounts_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_group_patient patient_group_patient_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_group_patient + ADD CONSTRAINT patient_group_patient_pkey PRIMARY KEY (patient_group_id, patient_id); + + +-- +-- Name: patient_groups patient_groups_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_groups + ADD CONSTRAINT patient_groups_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_intake_requests patient_intake_requests_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_intake_requests + ADD CONSTRAINT patient_intake_requests_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_invites patient_invites_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_invites + ADD CONSTRAINT patient_invites_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_invites patient_invites_token_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_invites + ADD CONSTRAINT patient_invites_token_key UNIQUE (token); + + +-- +-- Name: patient_patient_tag patient_patient_tag_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT patient_patient_tag_pkey PRIMARY KEY (patient_id, tag_id); + + +-- +-- Name: patient_tags patient_tags_owner_name_uniq; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_tags + ADD CONSTRAINT patient_tags_owner_name_uniq UNIQUE (owner_id, nome); + + +-- +-- Name: patient_tags patient_tags_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_tags + ADD CONSTRAINT patient_tags_pkey PRIMARY KEY (id); + + +-- +-- Name: patients patients_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_pkey PRIMARY KEY (id); + + +-- +-- Name: payment_settings payment_settings_owner_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_owner_id_key UNIQUE (owner_id); + + +-- +-- Name: payment_settings payment_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_pkey PRIMARY KEY (id); + + +-- +-- Name: plan_features plan_features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_features + ADD CONSTRAINT plan_features_pkey PRIMARY KEY (plan_id, feature_id); + + +-- +-- Name: plan_prices plan_prices_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_prices + ADD CONSTRAINT plan_prices_pkey PRIMARY KEY (id); + + +-- +-- Name: plan_public_bullets plan_public_bullets_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public_bullets + ADD CONSTRAINT plan_public_bullets_pkey PRIMARY KEY (id); + + +-- +-- Name: plan_public plan_public_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public + ADD CONSTRAINT plan_public_pkey PRIMARY KEY (plan_id); + + +-- +-- Name: plans plans_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plans + ADD CONSTRAINT plans_key_key UNIQUE (key); + + +-- +-- Name: plans plans_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plans + ADD CONSTRAINT plans_pkey PRIMARY KEY (id); + + +-- +-- Name: professional_pricing professional_pricing_owner_commitment_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.professional_pricing + ADD CONSTRAINT professional_pricing_owner_commitment_key UNIQUE (owner_id, determined_commitment_id); + + +-- +-- Name: professional_pricing professional_pricing_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.professional_pricing + ADD CONSTRAINT professional_pricing_pkey PRIMARY KEY (id); + + +-- +-- Name: profiles profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.profiles + ADD CONSTRAINT profiles_pkey PRIMARY KEY (id); + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_pkey PRIMARY KEY (id); + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_unique UNIQUE (recurrence_id, original_date); + + +-- +-- Name: recurrence_rule_services recurrence_rule_services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rule_services + ADD CONSTRAINT recurrence_rule_services_pkey PRIMARY KEY (id); + + +-- +-- Name: recurrence_rules recurrence_rules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rules + ADD CONSTRAINT recurrence_rules_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_admins saas_admins_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_admins + ADD CONSTRAINT saas_admins_pkey PRIMARY KEY (user_id); + + +-- +-- Name: saas_doc_votos saas_doc_votos_doc_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_doc_id_user_id_key UNIQUE (doc_id, user_id); + + +-- +-- Name: saas_doc_votos saas_doc_votos_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_docs saas_docs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_docs + ADD CONSTRAINT saas_docs_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_faq_itens saas_faq_itens_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_faq_itens + ADD CONSTRAINT saas_faq_itens_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_faq saas_faq_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_faq + ADD CONSTRAINT saas_faq_pkey PRIMARY KEY (id); + + +-- +-- Name: services services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.services + ADD CONSTRAINT services_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_events subscription_events_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_events + ADD CONSTRAINT subscription_events_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_intents_personal subscription_intents_personal_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_personal + ADD CONSTRAINT subscription_intents_personal_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_intents_legacy subscription_intents_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_legacy + ADD CONSTRAINT subscription_intents_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_intents_tenant subscription_intents_tenant_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_tenant + ADD CONSTRAINT subscription_intents_tenant_pkey PRIMARY KEY (id); + + +-- +-- Name: subscriptions subscriptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscriptions + ADD CONSTRAINT subscriptions_pkey PRIMARY KEY (id); + + +-- +-- Name: support_sessions support_sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_pkey PRIMARY KEY (id); + + +-- +-- Name: support_sessions support_sessions_token_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_token_unique UNIQUE (token); + + +-- +-- Name: tenant_feature_exceptions_log tenant_feature_exceptions_log_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_feature_exceptions_log + ADD CONSTRAINT tenant_feature_exceptions_log_pkey PRIMARY KEY (id); + + +-- +-- Name: tenant_features tenant_features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_features + ADD CONSTRAINT tenant_features_pkey PRIMARY KEY (tenant_id, feature_key); + + +-- +-- Name: tenant_invites tenant_invites_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_pkey PRIMARY KEY (id); + + +-- +-- Name: tenant_members tenant_members_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_pkey PRIMARY KEY (id); + + +-- +-- Name: tenant_members tenant_members_tenant_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_tenant_id_user_id_key UNIQUE (tenant_id, user_id); + + +-- +-- Name: tenant_modules tenant_modules_owner_id_module_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_owner_id_module_id_key UNIQUE (owner_id, module_id); + + +-- +-- Name: tenant_modules tenant_modules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_pkey PRIMARY KEY (id); + + +-- +-- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenants + ADD CONSTRAINT tenants_pkey PRIMARY KEY (id); + + +-- +-- Name: therapist_payout_records therapist_payout_records_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payout_records + ADD CONSTRAINT therapist_payout_records_pkey PRIMARY KEY (payout_id, financial_record_id); + + +-- +-- Name: therapist_payouts therapist_payouts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payouts + ADD CONSTRAINT therapist_payouts_pkey PRIMARY KEY (id); + + +-- +-- Name: twilio_subaccount_usage twilio_subaccount_usage_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.twilio_subaccount_usage + ADD CONSTRAINT twilio_subaccount_usage_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_credits uq_addon_credits_tenant_type; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT uq_addon_credits_tenant_type UNIQUE (tenant_id, addon_type); + + +-- +-- Name: notification_channels uq_channel_per_owner; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_channels + ADD CONSTRAINT uq_channel_per_owner UNIQUE NULLS NOT DISTINCT (owner_id, channel, deleted_at); + + +-- +-- Name: notification_preferences uq_notif_prefs_patient_owner; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_preferences + ADD CONSTRAINT uq_notif_prefs_patient_owner UNIQUE NULLS NOT DISTINCT (owner_id, patient_id, deleted_at); + + +-- +-- Name: notification_queue uq_notif_queue_idempotency; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_queue + ADD CONSTRAINT uq_notif_queue_idempotency UNIQUE (idempotency_key); + + +-- +-- Name: notification_schedules uq_notif_schedule_owner; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_schedules + ADD CONSTRAINT uq_notif_schedule_owner UNIQUE NULLS NOT DISTINCT (owner_id, schedule_key, deleted_at); + + +-- +-- Name: notification_templates uq_notif_template_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_templates + ADD CONSTRAINT uq_notif_template_key UNIQUE NULLS NOT DISTINCT (tenant_id, owner_id, key, deleted_at); + + +-- +-- Name: user_settings user_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_settings + ADD CONSTRAINT user_settings_pkey PRIMARY KEY (user_id); + + +-- +-- Name: messages messages_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages + ADD CONSTRAINT messages_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_24 messages_2026_03_24_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_24 + ADD CONSTRAINT messages_2026_03_24_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_25 messages_2026_03_25_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_25 + ADD CONSTRAINT messages_2026_03_25_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_26 messages_2026_03_26_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_26 + ADD CONSTRAINT messages_2026_03_26_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_27 messages_2026_03_27_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_27 + ADD CONSTRAINT messages_2026_03_27_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_28 messages_2026_03_28_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_28 + ADD CONSTRAINT messages_2026_03_28_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_29 messages_2026_03_29_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_29 + ADD CONSTRAINT messages_2026_03_29_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_30 messages_2026_03_30_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_30 + ADD CONSTRAINT messages_2026_03_30_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: subscription pk_subscription; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.subscription + ADD CONSTRAINT pk_subscription PRIMARY KEY (id); + + +-- +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.schema_migrations + ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: buckets_analytics buckets_analytics_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.buckets_analytics + ADD CONSTRAINT buckets_analytics_pkey PRIMARY KEY (id); + + +-- +-- Name: buckets buckets_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.buckets + ADD CONSTRAINT buckets_pkey PRIMARY KEY (id); + + +-- +-- Name: buckets_vectors buckets_vectors_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.buckets_vectors + ADD CONSTRAINT buckets_vectors_pkey PRIMARY KEY (id); + + +-- +-- Name: iceberg_namespaces iceberg_namespaces_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_namespaces + ADD CONSTRAINT iceberg_namespaces_pkey PRIMARY KEY (id); + + +-- +-- Name: iceberg_tables iceberg_tables_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_tables + ADD CONSTRAINT iceberg_tables_pkey PRIMARY KEY (id); + + +-- +-- Name: migrations migrations_name_key; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.migrations + ADD CONSTRAINT migrations_name_key UNIQUE (name); + + +-- +-- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.migrations + ADD CONSTRAINT migrations_pkey PRIMARY KEY (id); + + +-- +-- Name: objects objects_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.objects + ADD CONSTRAINT objects_pkey PRIMARY KEY (id); + + +-- +-- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads_parts + ADD CONSTRAINT s3_multipart_uploads_parts_pkey PRIMARY KEY (id); + + +-- +-- Name: s3_multipart_uploads s3_multipart_uploads_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads + ADD CONSTRAINT s3_multipart_uploads_pkey PRIMARY KEY (id); + + +-- +-- Name: vector_indexes vector_indexes_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.vector_indexes + ADD CONSTRAINT vector_indexes_pkey PRIMARY KEY (id); + + +-- +-- Name: hooks hooks_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: - +-- + +ALTER TABLE ONLY supabase_functions.hooks + ADD CONSTRAINT hooks_pkey PRIMARY KEY (id); + + +-- +-- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: - +-- + +ALTER TABLE ONLY supabase_functions.migrations + ADD CONSTRAINT migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: extensions_tenant_external_id_index; Type: INDEX; Schema: _realtime; Owner: - +-- + +CREATE INDEX extensions_tenant_external_id_index ON _realtime.extensions USING btree (tenant_external_id); + + +-- +-- Name: extensions_tenant_external_id_type_index; Type: INDEX; Schema: _realtime; Owner: - +-- + +CREATE UNIQUE INDEX extensions_tenant_external_id_type_index ON _realtime.extensions USING btree (tenant_external_id, type); + + +-- +-- Name: tenants_external_id_index; Type: INDEX; Schema: _realtime; Owner: - +-- + +CREATE UNIQUE INDEX tenants_external_id_index ON _realtime.tenants USING btree (external_id); + + +-- +-- Name: audit_logs_instance_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX audit_logs_instance_id_idx ON auth.audit_log_entries USING btree (instance_id); + + +-- +-- Name: confirmation_token_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX confirmation_token_idx ON auth.users USING btree (confirmation_token) WHERE ((confirmation_token)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: email_change_token_current_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX email_change_token_current_idx ON auth.users USING btree (email_change_token_current) WHERE ((email_change_token_current)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: email_change_token_new_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX email_change_token_new_idx ON auth.users USING btree (email_change_token_new) WHERE ((email_change_token_new)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: factor_id_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX factor_id_created_at_idx ON auth.mfa_factors USING btree (user_id, created_at); + + +-- +-- Name: flow_state_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX flow_state_created_at_idx ON auth.flow_state USING btree (created_at DESC); + + +-- +-- Name: identities_email_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX identities_email_idx ON auth.identities USING btree (email text_pattern_ops); + + +-- +-- Name: INDEX identities_email_idx; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON INDEX auth.identities_email_idx IS 'Auth: Ensures indexed queries on the email column'; + + +-- +-- Name: identities_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX identities_user_id_idx ON auth.identities USING btree (user_id); + + +-- +-- Name: idx_auth_code; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX idx_auth_code ON auth.flow_state USING btree (auth_code); + + +-- +-- Name: idx_oauth_client_states_created_at; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX idx_oauth_client_states_created_at ON auth.oauth_client_states USING btree (created_at); + + +-- +-- Name: idx_user_id_auth_method; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX idx_user_id_auth_method ON auth.flow_state USING btree (user_id, authentication_method); + + +-- +-- Name: mfa_challenge_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX mfa_challenge_created_at_idx ON auth.mfa_challenges USING btree (created_at DESC); + + +-- +-- Name: mfa_factors_user_friendly_name_unique; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX mfa_factors_user_friendly_name_unique ON auth.mfa_factors USING btree (friendly_name, user_id) WHERE (TRIM(BOTH FROM friendly_name) <> ''::text); + + +-- +-- Name: mfa_factors_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX mfa_factors_user_id_idx ON auth.mfa_factors USING btree (user_id); + + +-- +-- Name: oauth_auth_pending_exp_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_auth_pending_exp_idx ON auth.oauth_authorizations USING btree (expires_at) WHERE (status = 'pending'::auth.oauth_authorization_status); + + +-- +-- Name: oauth_clients_deleted_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_clients_deleted_at_idx ON auth.oauth_clients USING btree (deleted_at); + + +-- +-- Name: oauth_consents_active_client_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_consents_active_client_idx ON auth.oauth_consents USING btree (client_id) WHERE (revoked_at IS NULL); + + +-- +-- Name: oauth_consents_active_user_client_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_consents_active_user_client_idx ON auth.oauth_consents USING btree (user_id, client_id) WHERE (revoked_at IS NULL); + + +-- +-- Name: oauth_consents_user_order_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_consents_user_order_idx ON auth.oauth_consents USING btree (user_id, granted_at DESC); + + +-- +-- Name: one_time_tokens_relates_to_hash_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX one_time_tokens_relates_to_hash_idx ON auth.one_time_tokens USING hash (relates_to); + + +-- +-- Name: one_time_tokens_token_hash_hash_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX one_time_tokens_token_hash_hash_idx ON auth.one_time_tokens USING hash (token_hash); + + +-- +-- Name: one_time_tokens_user_id_token_type_key; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX one_time_tokens_user_id_token_type_key ON auth.one_time_tokens USING btree (user_id, token_type); + + +-- +-- Name: reauthentication_token_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX reauthentication_token_idx ON auth.users USING btree (reauthentication_token) WHERE ((reauthentication_token)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: recovery_token_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX recovery_token_idx ON auth.users USING btree (recovery_token) WHERE ((recovery_token)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: refresh_tokens_instance_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_instance_id_idx ON auth.refresh_tokens USING btree (instance_id); + + +-- +-- Name: refresh_tokens_instance_id_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_instance_id_user_id_idx ON auth.refresh_tokens USING btree (instance_id, user_id); + + +-- +-- Name: refresh_tokens_parent_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_parent_idx ON auth.refresh_tokens USING btree (parent); + + +-- +-- Name: refresh_tokens_session_id_revoked_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_session_id_revoked_idx ON auth.refresh_tokens USING btree (session_id, revoked); + + +-- +-- Name: refresh_tokens_updated_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_updated_at_idx ON auth.refresh_tokens USING btree (updated_at DESC); + + +-- +-- Name: saml_providers_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_providers_sso_provider_id_idx ON auth.saml_providers USING btree (sso_provider_id); + + +-- +-- Name: saml_relay_states_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_relay_states_created_at_idx ON auth.saml_relay_states USING btree (created_at DESC); + + +-- +-- Name: saml_relay_states_for_email_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_relay_states_for_email_idx ON auth.saml_relay_states USING btree (for_email); + + +-- +-- Name: saml_relay_states_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_relay_states_sso_provider_id_idx ON auth.saml_relay_states USING btree (sso_provider_id); + + +-- +-- Name: sessions_not_after_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sessions_not_after_idx ON auth.sessions USING btree (not_after DESC); + + +-- +-- Name: sessions_oauth_client_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sessions_oauth_client_id_idx ON auth.sessions USING btree (oauth_client_id); + + +-- +-- Name: sessions_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sessions_user_id_idx ON auth.sessions USING btree (user_id); + + +-- +-- Name: sso_domains_domain_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX sso_domains_domain_idx ON auth.sso_domains USING btree (lower(domain)); + + +-- +-- Name: sso_domains_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sso_domains_sso_provider_id_idx ON auth.sso_domains USING btree (sso_provider_id); + + +-- +-- Name: sso_providers_resource_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX sso_providers_resource_id_idx ON auth.sso_providers USING btree (lower(resource_id)); + + +-- +-- Name: sso_providers_resource_id_pattern_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sso_providers_resource_id_pattern_idx ON auth.sso_providers USING btree (resource_id text_pattern_ops); + + +-- +-- Name: unique_phone_factor_per_user; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX unique_phone_factor_per_user ON auth.mfa_factors USING btree (user_id, phone); + + +-- +-- Name: user_id_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX user_id_created_at_idx ON auth.sessions USING btree (user_id, created_at); + + +-- +-- Name: users_email_partial_key; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX users_email_partial_key ON auth.users USING btree (email) WHERE (is_sso_user = false); + + +-- +-- Name: INDEX users_email_partial_key; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON INDEX auth.users_email_partial_key IS 'Auth: A partial unique index that applies only when is_sso_user is false'; + + +-- +-- Name: users_instance_id_email_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX users_instance_id_email_idx ON auth.users USING btree (instance_id, lower((email)::text)); + + +-- +-- Name: users_instance_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX users_instance_id_idx ON auth.users USING btree (instance_id); + + +-- +-- Name: users_is_anonymous_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX users_is_anonymous_idx ON auth.users USING btree (is_anonymous); + + +-- +-- Name: agenda_bloqueios_owner_data_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_owner_data_idx ON public.agenda_bloqueios USING btree (owner_id, data_inicio, data_fim); + + +-- +-- Name: agenda_bloqueios_owner_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_owner_id_idx ON public.agenda_bloqueios USING btree (owner_id); + + +-- +-- Name: agenda_bloqueios_recorrente_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_recorrente_idx ON public.agenda_bloqueios USING btree (owner_id, dia_semana) WHERE (recorrente = true); + + +-- +-- Name: agenda_bloqueios_tenant_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_tenant_id_idx ON public.agenda_bloqueios USING btree (tenant_id); + + +-- +-- Name: agenda_configuracoes_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_configuracoes_tenant_idx ON public.agenda_configuracoes USING btree (tenant_id); + + +-- +-- Name: agenda_configuracoes_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_configuracoes_tenant_owner_idx ON public.agenda_configuracoes USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_eventos_billing_contract_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_billing_contract_idx ON public.agenda_eventos USING btree (billing_contract_id) WHERE (billing_contract_id IS NOT NULL); + + +-- +-- Name: agenda_eventos_insurance_plan_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_insurance_plan_idx ON public.agenda_eventos USING btree (insurance_plan_id); + + +-- +-- Name: agenda_eventos_owner_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_owner_inicio_idx ON public.agenda_eventos USING btree (owner_id, inicio_em); + + +-- +-- Name: agenda_eventos_owner_terapeuta_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_owner_terapeuta_inicio_idx ON public.agenda_eventos USING btree (owner_id, terapeuta_id, inicio_em); + + +-- +-- Name: agenda_eventos_recurrence_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_recurrence_idx ON public.agenda_eventos USING btree (recurrence_id) WHERE (recurrence_id IS NOT NULL); + + +-- +-- Name: agenda_eventos_tenant_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_tenant_inicio_idx ON public.agenda_eventos USING btree (tenant_id, inicio_em); + + +-- +-- Name: agenda_eventos_tenant_owner_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_tenant_owner_inicio_idx ON public.agenda_eventos USING btree (tenant_id, owner_id, inicio_em); + + +-- +-- Name: agenda_excecoes_owner_data_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_excecoes_owner_data_idx ON public.agenda_excecoes USING btree (owner_id, data); + + +-- +-- Name: agenda_excecoes_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_excecoes_tenant_idx ON public.agenda_excecoes USING btree (tenant_id); + + +-- +-- Name: agenda_excecoes_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_excecoes_tenant_owner_idx ON public.agenda_excecoes USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_online_slots_owner_weekday_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_online_slots_owner_weekday_idx ON public.agenda_online_slots USING btree (owner_id, weekday); + + +-- +-- Name: agenda_online_slots_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_online_slots_tenant_idx ON public.agenda_online_slots USING btree (tenant_id); + + +-- +-- Name: agenda_online_slots_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_online_slots_tenant_owner_idx ON public.agenda_online_slots USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_regras_semanais_owner_dia_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_regras_semanais_owner_dia_idx ON public.agenda_regras_semanais USING btree (owner_id, dia_semana); + + +-- +-- Name: agenda_regras_semanais_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_regras_semanais_tenant_idx ON public.agenda_regras_semanais USING btree (tenant_id); + + +-- +-- Name: agenda_regras_semanais_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_regras_semanais_tenant_owner_idx ON public.agenda_regras_semanais USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_slots_bloqueados_semanais_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_bloqueados_semanais_tenant_idx ON public.agenda_slots_bloqueados_semanais USING btree (tenant_id); + + +-- +-- Name: agenda_slots_bloqueados_semanais_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_bloqueados_semanais_tenant_owner_idx ON public.agenda_slots_bloqueados_semanais USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_slots_regras_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_regras_tenant_idx ON public.agenda_slots_regras USING btree (tenant_id); + + +-- +-- Name: agenda_slots_regras_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_regras_tenant_owner_idx ON public.agenda_slots_regras USING btree (tenant_id, owner_id); + + +-- +-- Name: agendador_cfg_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_cfg_tenant_idx ON public.agendador_configuracoes USING btree (tenant_id); + + +-- +-- Name: agendador_sol_data_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_sol_data_idx ON public.agendador_solicitacoes USING btree (data_solicitada, hora_solicitada); + + +-- +-- Name: agendador_sol_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_sol_owner_idx ON public.agendador_solicitacoes USING btree (owner_id, status); + + +-- +-- Name: agendador_sol_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_sol_tenant_idx ON public.agendador_solicitacoes USING btree (tenant_id); + + +-- +-- Name: billing_contracts_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX billing_contracts_owner_idx ON public.billing_contracts USING btree (owner_id); + + +-- +-- Name: billing_contracts_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX billing_contracts_patient_idx ON public.billing_contracts USING btree (patient_id); + + +-- +-- Name: billing_contracts_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX billing_contracts_tenant_idx ON public.billing_contracts USING btree (tenant_id); + + +-- +-- Name: commitment_services_commitment_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_services_commitment_idx ON public.commitment_services USING btree (commitment_id); + + +-- +-- Name: commitment_services_service_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_services_service_idx ON public.commitment_services USING btree (service_id); + + +-- +-- Name: commitment_time_logs_calendar_event_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_time_logs_calendar_event_idx ON public.commitment_time_logs USING btree (calendar_event_id); + + +-- +-- Name: commitment_time_logs_commitment_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_time_logs_commitment_idx ON public.commitment_time_logs USING btree (commitment_id, created_at DESC); + + +-- +-- Name: commitment_time_logs_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_time_logs_tenant_idx ON public.commitment_time_logs USING btree (tenant_id, created_at DESC); + + +-- +-- Name: determined_commitment_fields_commitment_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitment_fields_commitment_idx ON public.determined_commitment_fields USING btree (commitment_id, sort_order); + + +-- +-- Name: determined_commitment_fields_key_uniq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX determined_commitment_fields_key_uniq ON public.determined_commitment_fields USING btree (commitment_id, key); + + +-- +-- Name: determined_commitment_fields_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitment_fields_tenant_idx ON public.determined_commitment_fields USING btree (tenant_id); + + +-- +-- Name: determined_commitments_active_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitments_active_idx ON public.determined_commitments USING btree (tenant_id, active); + + +-- +-- Name: determined_commitments_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitments_tenant_idx ON public.determined_commitments USING btree (tenant_id); + + +-- +-- Name: determined_commitments_tenant_name_uniq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX determined_commitments_tenant_name_uniq ON public.determined_commitments USING btree (tenant_id, lower(name)); + + +-- +-- Name: feriados_global_unique; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX feriados_global_unique ON public.feriados USING btree (data, nome) WHERE (tenant_id IS NULL); + + +-- +-- Name: financial_exceptions_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX financial_exceptions_owner_idx ON public.financial_exceptions USING btree (owner_id); + + +-- +-- Name: financial_exceptions_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX financial_exceptions_tenant_idx ON public.financial_exceptions USING btree (tenant_id); + + +-- +-- Name: idx_addon_credits_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_credits_tenant ON public.addon_credits USING btree (tenant_id) WHERE (is_active = true); + + +-- +-- Name: idx_addon_credits_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_credits_type ON public.addon_credits USING btree (addon_type) WHERE (is_active = true); + + +-- +-- Name: idx_addon_products_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_products_active ON public.addon_products USING btree (is_active, is_visible) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_addon_products_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_products_type ON public.addon_products USING btree (addon_type) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_addon_tx_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_created ON public.addon_transactions USING btree (created_at DESC); + + +-- +-- Name: idx_addon_tx_queue; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_queue ON public.addon_transactions USING btree (queue_id) WHERE (queue_id IS NOT NULL); + + +-- +-- Name: idx_addon_tx_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_tenant ON public.addon_transactions USING btree (tenant_id, addon_type); + + +-- +-- Name: idx_addon_tx_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_type ON public.addon_transactions USING btree (type); + + +-- +-- Name: idx_agenda_eventos_determined_commitment_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_agenda_eventos_determined_commitment_id ON public.agenda_eventos USING btree (determined_commitment_id); + + +-- +-- Name: idx_agenda_excecoes_owner_data; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_agenda_excecoes_owner_data ON public.agenda_excecoes USING btree (owner_id, data); + + +-- +-- Name: idx_agenda_slots_regras_owner_dia; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_agenda_slots_regras_owner_dia ON public.agenda_slots_regras USING btree (owner_id, dia_semana); + + +-- +-- Name: idx_email_templates_global_domain; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_global_domain ON public.email_templates_global USING btree (domain) WHERE (is_active = true); + + +-- +-- Name: idx_email_templates_global_key; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_global_key ON public.email_templates_global USING btree (key) WHERE (is_active = true); + + +-- +-- Name: idx_email_templates_tenant_lookup; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_tenant_lookup ON public.email_templates_tenant USING btree (tenant_id, template_key) WHERE (enabled = true); + + +-- +-- Name: idx_email_templates_tenant_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_tenant_owner ON public.email_templates_tenant USING btree (owner_id, template_key) WHERE ((enabled = true) AND (owner_id IS NOT NULL)); + + +-- +-- Name: idx_financial_categories_user_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_categories_user_id ON public.financial_categories USING btree (user_id); + + +-- +-- Name: idx_financial_records_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_active ON public.financial_records USING btree (owner_id, paid_at DESC) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_financial_records_agenda_evento_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_agenda_evento_id ON public.financial_records USING btree (agenda_evento_id) WHERE (agenda_evento_id IS NOT NULL); + + +-- +-- Name: idx_financial_records_category_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_category_id ON public.financial_records USING btree (category_id) WHERE (category_id IS NOT NULL); + + +-- +-- Name: idx_financial_records_due_date; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_due_date ON public.financial_records USING btree (due_date); + + +-- +-- Name: idx_financial_records_installment_group; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_installment_group ON public.financial_records USING btree (installment_group) WHERE (installment_group IS NOT NULL); + + +-- +-- Name: idx_financial_records_owner_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_owner_id ON public.financial_records USING btree (owner_id); + + +-- +-- Name: idx_financial_records_paid_at; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_paid_at ON public.financial_records USING btree (paid_at DESC); + + +-- +-- Name: idx_financial_records_patient_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_patient_id ON public.financial_records USING btree (patient_id); + + +-- +-- Name: idx_financial_records_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_status ON public.financial_records USING btree (status) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_financial_records_tenant_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_tenant_active ON public.financial_records USING btree (tenant_id, paid_at DESC) WHERE ((deleted_at IS NULL) AND (tenant_id IS NOT NULL)); + + +-- +-- Name: idx_financial_records_tenant_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_tenant_id ON public.financial_records USING btree (tenant_id); + + +-- +-- Name: idx_financial_records_type_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_type_status ON public.financial_records USING btree (type, status); + + +-- +-- Name: idx_global_notices_active_priority; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_global_notices_active_priority ON public.global_notices USING btree (is_active, priority DESC, starts_at, ends_at); + + +-- +-- Name: idx_intakes_converted_patient_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_converted_patient_id ON public.patient_intake_requests USING btree (converted_patient_id); + + +-- +-- Name: idx_intakes_owner_cpf; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_owner_cpf ON public.patient_intake_requests USING btree (owner_id, cpf); + + +-- +-- Name: idx_intakes_owner_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_owner_created ON public.patient_intake_requests USING btree (owner_id, created_at DESC); + + +-- +-- Name: idx_intakes_owner_status_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_owner_status_created ON public.patient_intake_requests USING btree (owner_id, status, created_at DESC); + + +-- +-- Name: idx_intakes_status_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_status_created ON public.patient_intake_requests USING btree (status, created_at DESC); + + +-- +-- Name: idx_notice_dismissals_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notice_dismissals_user ON public.notice_dismissals USING btree (user_id, notice_id); + + +-- +-- Name: idx_notif_channels_owner_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_channels_owner_active ON public.notification_channels USING btree (owner_id, channel) WHERE ((is_active = true) AND (deleted_at IS NULL)); + + +-- +-- Name: idx_notif_channels_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_channels_tenant ON public.notification_channels USING btree (tenant_id) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_logs_owner_date; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_owner_date ON public.notification_logs USING btree (owner_id, created_at DESC); + + +-- +-- Name: idx_notif_logs_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_patient ON public.notification_logs USING btree (patient_id, created_at DESC); + + +-- +-- Name: idx_notif_logs_provider_msg; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_provider_msg ON public.notification_logs USING btree (provider_message_id) WHERE (provider_message_id IS NOT NULL); + + +-- +-- Name: idx_notif_logs_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_status ON public.notification_logs USING btree (status, created_at DESC); + + +-- +-- Name: idx_notif_logs_tenant_date; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_tenant_date ON public.notification_logs USING btree (tenant_id, created_at DESC); + + +-- +-- Name: idx_notif_prefs_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_prefs_owner ON public.notification_preferences USING btree (owner_id) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_prefs_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_prefs_patient ON public.notification_preferences USING btree (patient_id) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_prefs_whatsapp_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_prefs_whatsapp_active ON public.notification_preferences USING btree (owner_id, patient_id) WHERE ((whatsapp_opt_in = true) AND (deleted_at IS NULL)); + + +-- +-- Name: idx_notif_queue_evento; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_evento ON public.notification_queue USING btree (agenda_evento_id) WHERE (status = ANY (ARRAY['pendente'::text, 'processando'::text])); + + +-- +-- Name: idx_notif_queue_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_patient ON public.notification_queue USING btree (patient_id, channel) WHERE (status = 'pendente'::text); + + +-- +-- Name: idx_notif_queue_pending; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_pending ON public.notification_queue USING btree (scheduled_at) WHERE (status = 'pendente'::text); + + +-- +-- Name: idx_notif_queue_processing; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_processing ON public.notification_queue USING btree (status, updated_at) WHERE (status = 'processando'::text); + + +-- +-- Name: idx_notif_queue_retry; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_retry ON public.notification_queue USING btree (next_retry_at) WHERE ((status = 'pendente'::text) AND (attempts > 0)); + + +-- +-- Name: idx_notif_queue_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_tenant ON public.notification_queue USING btree (tenant_id, created_at DESC); + + +-- +-- Name: idx_notif_schedules_owner_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_schedules_owner_active ON public.notification_schedules USING btree (owner_id, event_type) WHERE ((is_active = true) AND (deleted_at IS NULL)); + + +-- +-- Name: idx_notif_templates_default; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_templates_default ON public.notification_templates USING btree (channel, event_type) WHERE ((is_default = true) AND (deleted_at IS NULL) AND (tenant_id IS NULL)); + + +-- +-- Name: idx_notif_templates_lookup; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_templates_lookup ON public.notification_templates USING btree (channel, event_type, is_active) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_templates_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_templates_tenant ON public.notification_templates USING btree (tenant_id, channel, event_type) WHERE ((deleted_at IS NULL) AND (is_active = true)); + + +-- +-- Name: idx_notification_channels_twilio_subaccount_sid; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notification_channels_twilio_subaccount_sid ON public.notification_channels USING btree (twilio_subaccount_sid) WHERE (twilio_subaccount_sid IS NOT NULL); + + +-- +-- Name: idx_patient_group_patient_group_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_group_patient_group_id ON public.patient_group_patient USING btree (patient_group_id); + + +-- +-- Name: idx_patient_groups_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_groups_owner ON public.patient_groups USING btree (owner_id); + + +-- +-- Name: idx_patient_groups_owner_system_nome; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_groups_owner_system_nome ON public.patient_groups USING btree (owner_id, is_system, nome); + + +-- +-- Name: idx_patient_tags_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_tags_owner ON public.patient_tags USING btree (owner_id); + + +-- +-- Name: idx_patients_created_at; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_created_at ON public.patients USING btree (created_at DESC); + + +-- +-- Name: idx_patients_last_attended; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_last_attended ON public.patients USING btree (last_attended_at DESC); + + +-- +-- Name: idx_patients_owner_email_principal; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_owner_email_principal ON public.patients USING btree (owner_id, email_principal); + + +-- +-- Name: idx_patients_owner_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_owner_id ON public.patients USING btree (owner_id); + + +-- +-- Name: idx_patients_owner_nome; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_owner_nome ON public.patients USING btree (owner_id, nome_completo); + + +-- +-- Name: idx_patients_responsible_member; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_responsible_member ON public.patients USING btree (responsible_member_id); + + +-- +-- Name: idx_patients_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_status ON public.patients USING btree (status); + + +-- +-- Name: idx_patients_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_tenant ON public.patients USING btree (tenant_id); + + +-- +-- Name: idx_patients_tenant_email_norm; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_tenant_email_norm ON public.patients USING btree (tenant_id, lower(TRIM(BOTH FROM email_principal))); + + +-- +-- Name: idx_pgp_group; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pgp_group ON public.patient_group_patient USING btree (patient_group_id); + + +-- +-- Name: idx_pgp_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pgp_patient ON public.patient_group_patient USING btree (patient_id); + + +-- +-- Name: idx_ppt_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_ppt_patient ON public.patient_patient_tag USING btree (patient_id); + + +-- +-- Name: idx_ppt_tag; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_ppt_tag ON public.patient_patient_tag USING btree (tag_id); + + +-- +-- Name: idx_slots_bloq_owner_dia; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_slots_bloq_owner_dia ON public.agenda_slots_bloqueados_semanais USING btree (owner_id, dia_semana); + + +-- +-- Name: idx_subscription_intents_plan_interval; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_subscription_intents_plan_interval ON public.subscription_intents_legacy USING btree (plan_key, "interval"); + + +-- +-- Name: idx_subscription_intents_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_subscription_intents_status ON public.subscription_intents_legacy USING btree (status); + + +-- +-- Name: idx_subscription_intents_user_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_subscription_intents_user_id ON public.subscription_intents_legacy USING btree (user_id); + + +-- +-- Name: idx_tenant_features_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_tenant_features_tenant ON public.tenant_features USING btree (tenant_id); + + +-- +-- Name: idx_tenant_invites_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_tenant_invites_tenant ON public.tenant_invites USING btree (tenant_id); + + +-- +-- Name: idx_tenant_invites_token; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_tenant_invites_token ON public.tenant_invites USING btree (token); + + +-- +-- Name: idx_therapist_payout_records_financial_record_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payout_records_financial_record_id ON public.therapist_payout_records USING btree (financial_record_id); + + +-- +-- Name: idx_therapist_payouts_owner_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_owner_id ON public.therapist_payouts USING btree (owner_id); + + +-- +-- Name: idx_therapist_payouts_period; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_period ON public.therapist_payouts USING btree (period_start, period_end); + + +-- +-- Name: idx_therapist_payouts_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_status ON public.therapist_payouts USING btree (status) WHERE (status = 'pending'::text); + + +-- +-- Name: idx_therapist_payouts_tenant_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_tenant_id ON public.therapist_payouts USING btree (tenant_id); + + +-- +-- Name: idx_twilio_usage_channel; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_twilio_usage_channel ON public.twilio_subaccount_usage USING btree (channel_id, period_start DESC); + + +-- +-- Name: idx_twilio_usage_tenant_period; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_twilio_usage_tenant_period ON public.twilio_subaccount_usage USING btree (tenant_id, period_start DESC); + + +-- +-- Name: idx_twilio_usage_unique_period; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX idx_twilio_usage_unique_period ON public.twilio_subaccount_usage USING btree (channel_id, period_start, period_end); + + +-- +-- Name: insurance_plans_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX insurance_plans_owner_idx ON public.insurance_plans USING btree (owner_id); + + +-- +-- Name: insurance_plans_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX insurance_plans_tenant_idx ON public.insurance_plans USING btree (tenant_id); + + +-- +-- Name: ix_plan_prices_plan; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ix_plan_prices_plan ON public.plan_prices USING btree (plan_id); + + +-- +-- Name: ix_plan_public_bullets_plan; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ix_plan_public_bullets_plan ON public.plan_public_bullets USING btree (plan_id); + + +-- +-- Name: ix_plan_public_sort; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ix_plan_public_sort ON public.plan_public USING btree (sort_order); + + +-- +-- Name: notifications_owner_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX notifications_owner_created ON public.notifications USING btree (owner_id, created_at DESC); + + +-- +-- Name: notifications_owner_unread; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX notifications_owner_unread ON public.notifications USING btree (owner_id, read_at) WHERE (read_at IS NULL); + + +-- +-- Name: patient_discounts_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_discounts_owner_idx ON public.patient_discounts USING btree (owner_id); + + +-- +-- Name: patient_discounts_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_discounts_patient_idx ON public.patient_discounts USING btree (patient_id); + + +-- +-- Name: patient_discounts_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_discounts_tenant_idx ON public.patient_discounts USING btree (tenant_id); + + +-- +-- Name: patient_group_patient_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_group_patient_tenant_idx ON public.patient_group_patient USING btree (tenant_id); + + +-- +-- Name: patient_groups_owner_nome_uniq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX patient_groups_owner_nome_uniq ON public.patient_groups USING btree (owner_id, nome); + + +-- +-- Name: patient_groups_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_groups_tenant_idx ON public.patient_groups USING btree (tenant_id); + + +-- +-- Name: patient_intake_owner_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_owner_id_idx ON public.patient_intake_requests USING btree (owner_id); + + +-- +-- Name: patient_intake_requests_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_requests_tenant_idx ON public.patient_intake_requests USING btree (tenant_id); + + +-- +-- Name: patient_intake_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_status_idx ON public.patient_intake_requests USING btree (status); + + +-- +-- Name: patient_intake_token_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_token_idx ON public.patient_intake_requests USING btree (token); + + +-- +-- Name: patient_invites_one_active_per_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX patient_invites_one_active_per_owner ON public.patient_invites USING btree (owner_id) WHERE (active = true); + + +-- +-- Name: patient_invites_owner_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_invites_owner_id_idx ON public.patient_invites USING btree (owner_id); + + +-- +-- Name: patient_invites_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_invites_tenant_idx ON public.patient_invites USING btree (tenant_id); + + +-- +-- Name: patient_invites_token_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_invites_token_idx ON public.patient_invites USING btree (token); + + +-- +-- Name: patient_patient_tag_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_patient_tag_tenant_idx ON public.patient_patient_tag USING btree (tenant_id); + + +-- +-- Name: patient_tags_owner_name_uq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX patient_tags_owner_name_uq ON public.patient_tags USING btree (owner_id, lower(nome)); + + +-- +-- Name: patient_tags_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_tags_tenant_idx ON public.patient_tags USING btree (tenant_id); + + +-- +-- Name: payment_settings_tenant_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX payment_settings_tenant_id_idx ON public.payment_settings USING btree (tenant_id); + + +-- +-- Name: ppt_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ppt_owner_idx ON public.patient_patient_tag USING btree (owner_id); + + +-- +-- Name: ppt_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ppt_patient_idx ON public.patient_patient_tag USING btree (patient_id); + + +-- +-- Name: ppt_tag_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ppt_tag_idx ON public.patient_patient_tag USING btree (tag_id); + + +-- +-- Name: professional_pricing_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX professional_pricing_tenant_idx ON public.professional_pricing USING btree (tenant_id); + + +-- +-- Name: profiles_work_description_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX profiles_work_description_idx ON public.profiles USING btree (work_description) WHERE (work_description IS NOT NULL); + + +-- +-- Name: recurrence_exceptions_rule_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_exceptions_rule_idx ON public.recurrence_exceptions USING btree (recurrence_id); + + +-- +-- Name: recurrence_exceptions_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_exceptions_tenant_idx ON public.recurrence_exceptions USING btree (tenant_id); + + +-- +-- Name: recurrence_rule_services_rule_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rule_services_rule_idx ON public.recurrence_rule_services USING btree (rule_id); + + +-- +-- Name: recurrence_rule_services_service_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rule_services_service_idx ON public.recurrence_rule_services USING btree (service_id); + + +-- +-- Name: recurrence_rules_active_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_active_idx ON public.recurrence_rules USING btree (owner_id, status) WHERE (status = 'ativo'::text); + + +-- +-- Name: recurrence_rules_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_owner_idx ON public.recurrence_rules USING btree (owner_id); + + +-- +-- Name: recurrence_rules_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_patient_idx ON public.recurrence_rules USING btree (patient_id); + + +-- +-- Name: recurrence_rules_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_tenant_idx ON public.recurrence_rules USING btree (tenant_id); + + +-- +-- Name: saas_doc_votos_doc_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_doc_votos_doc_id_idx ON public.saas_doc_votos USING btree (doc_id); + + +-- +-- Name: saas_doc_votos_user_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_doc_votos_user_id_idx ON public.saas_doc_votos USING btree (user_id); + + +-- +-- Name: saas_docs_categoria_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_docs_categoria_idx ON public.saas_docs USING btree (categoria); + + +-- +-- Name: saas_docs_exibir_no_faq_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_docs_exibir_no_faq_idx ON public.saas_docs USING btree (exibir_no_faq) WHERE (exibir_no_faq = true); + + +-- +-- Name: saas_docs_path_ativo_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_docs_path_ativo_idx ON public.saas_docs USING btree (pagina_path, ativo); + + +-- +-- Name: saas_faq_ativo_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_ativo_idx ON public.saas_faq USING btree (ativo); + + +-- +-- Name: saas_faq_categoria_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_categoria_idx ON public.saas_faq USING btree (categoria); + + +-- +-- Name: saas_faq_fts_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_fts_idx ON public.saas_faq USING gin (to_tsvector('portuguese'::regconfig, ((COALESCE(pergunta, ''::text) || ' '::text) || COALESCE(conteudo, ''::text)))); + + +-- +-- Name: saas_faq_itens_ativo_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_itens_ativo_idx ON public.saas_faq_itens USING btree (ativo); + + +-- +-- Name: saas_faq_itens_doc_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_itens_doc_id_idx ON public.saas_faq_itens USING btree (doc_id); + + +-- +-- Name: saas_faq_pagina_path_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_pagina_path_idx ON public.saas_faq USING btree (pagina_path); + + +-- +-- Name: saas_faq_publico_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_publico_idx ON public.saas_faq USING btree (publico); + + +-- +-- Name: saas_faq_votos_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_votos_idx ON public.saas_faq USING btree (votos DESC); + + +-- +-- Name: services_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX services_owner_idx ON public.services USING btree (owner_id); + + +-- +-- Name: services_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX services_tenant_idx ON public.services USING btree (tenant_id); + + +-- +-- Name: sint_personal_created_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_personal_created_idx ON public.subscription_intents_personal USING btree (created_at DESC); + + +-- +-- Name: sint_personal_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_personal_status_idx ON public.subscription_intents_personal USING btree (status); + + +-- +-- Name: sint_tenant_created_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_tenant_created_idx ON public.subscription_intents_tenant USING btree (created_at DESC); + + +-- +-- Name: sint_tenant_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_tenant_status_idx ON public.subscription_intents_tenant USING btree (status); + + +-- +-- Name: sint_tenant_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_tenant_tenant_idx ON public.subscription_intents_tenant USING btree (tenant_id); + + +-- +-- Name: subscription_events_created_at_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_created_at_idx ON public.subscription_events USING btree (created_at DESC); + + +-- +-- Name: subscription_events_owner_ref_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_owner_ref_idx ON public.subscription_events USING btree (owner_type, owner_ref); + + +-- +-- Name: subscription_events_sub_created_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_sub_created_idx ON public.subscription_events USING btree (subscription_id, created_at DESC); + + +-- +-- Name: subscription_events_subscription_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_subscription_id_idx ON public.subscription_events USING btree (subscription_id); + + +-- +-- Name: subscriptions_one_active_per_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX subscriptions_one_active_per_tenant ON public.subscriptions USING btree (tenant_id) WHERE (status = 'active'::text); + + +-- +-- Name: subscriptions_one_active_per_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX subscriptions_one_active_per_user ON public.subscriptions USING btree (user_id) WHERE (status = 'active'::text); + + +-- +-- Name: subscriptions_one_active_per_user_personal; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX subscriptions_one_active_per_user_personal ON public.subscriptions USING btree (user_id) WHERE ((tenant_id IS NULL) AND (status = 'active'::text)); + + +-- +-- Name: subscriptions_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_owner_idx ON public.subscriptions USING btree (user_id); + + +-- +-- Name: subscriptions_plan_key_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_plan_key_idx ON public.subscriptions USING btree (plan_key); + + +-- +-- Name: subscriptions_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_status_idx ON public.subscriptions USING btree (status); + + +-- +-- Name: subscriptions_tenant_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_tenant_id_idx ON public.subscriptions USING btree (tenant_id); + + +-- +-- Name: subscriptions_tenant_period_end_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_tenant_period_end_idx ON public.subscriptions USING btree (tenant_id, current_period_end); + + +-- +-- Name: subscriptions_tenant_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_tenant_status_idx ON public.subscriptions USING btree (tenant_id, status); + + +-- +-- Name: subscriptions_user_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_user_status_idx ON public.subscriptions USING btree (user_id, status, created_at DESC); + + +-- +-- Name: support_sessions_expires_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX support_sessions_expires_idx ON public.support_sessions USING btree (expires_at); + + +-- +-- Name: support_sessions_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX support_sessions_tenant_idx ON public.support_sessions USING btree (tenant_id); + + +-- +-- Name: support_sessions_token_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX support_sessions_token_idx ON public.support_sessions USING btree (token); + + +-- +-- Name: tenant_members_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX tenant_members_tenant_idx ON public.tenant_members USING btree (tenant_id); + + +-- +-- Name: tenant_members_user_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX tenant_members_user_idx ON public.tenant_members USING btree (user_id); + + +-- +-- Name: tenant_modules_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX tenant_modules_owner_idx ON public.tenant_modules USING btree (owner_id); + + +-- +-- Name: unique_member_per_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX unique_member_per_tenant ON public.tenant_members USING btree (tenant_id, user_id); + + +-- +-- Name: uq_patients_tenant_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_patients_tenant_user ON public.patients USING btree (tenant_id, user_id) WHERE (user_id IS NOT NULL); + + +-- +-- Name: uq_plan_price_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_plan_price_active ON public.plan_prices USING btree (plan_id, "interval", currency) WHERE ((is_active = true) AND (active_to IS NULL)); + + +-- +-- Name: uq_plan_prices_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_plan_prices_active ON public.plan_prices USING btree (plan_id, "interval") WHERE (is_active = true); + + +-- +-- Name: uq_subscriptions_active_by_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_subscriptions_active_by_tenant ON public.subscriptions USING btree (tenant_id) WHERE ((tenant_id IS NOT NULL) AND (status = 'active'::text)); + + +-- +-- Name: uq_subscriptions_active_personal_by_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_subscriptions_active_personal_by_user ON public.subscriptions USING btree (user_id) WHERE ((tenant_id IS NULL) AND (status = 'active'::text)); + + +-- +-- Name: uq_tenant_invites_pending; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_tenant_invites_pending ON public.tenant_invites USING btree (tenant_id, lower(email), role) WHERE ((accepted_at IS NULL) AND (revoked_at IS NULL)); + + +-- +-- Name: uq_tenant_members_tenant_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_tenant_members_tenant_user ON public.tenant_members USING btree (tenant_id, user_id); + + +-- +-- Name: ux_subscriptions_active_per_personal_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX ux_subscriptions_active_per_personal_user ON public.subscriptions USING btree (user_id) WHERE ((status = 'active'::text) AND (tenant_id IS NULL)); + + +-- +-- Name: ux_subscriptions_active_per_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX ux_subscriptions_active_per_tenant ON public.subscriptions USING btree (tenant_id) WHERE ((status = 'active'::text) AND (tenant_id IS NOT NULL)); + + +-- +-- Name: ix_realtime_subscription_entity; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX ix_realtime_subscription_entity ON realtime.subscription USING btree (entity); + + +-- +-- Name: messages_inserted_at_topic_index; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_inserted_at_topic_index ON ONLY realtime.messages USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_24_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_24_inserted_at_topic_idx ON realtime.messages_2026_03_24 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_25_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_25_inserted_at_topic_idx ON realtime.messages_2026_03_25 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_26_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_26_inserted_at_topic_idx ON realtime.messages_2026_03_26 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_27_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_27_inserted_at_topic_idx ON realtime.messages_2026_03_27 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_28_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_28_inserted_at_topic_idx ON realtime.messages_2026_03_28 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_29_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_29_inserted_at_topic_idx ON realtime.messages_2026_03_29 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_30_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_30_inserted_at_topic_idx ON realtime.messages_2026_03_30 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: subscription_subscription_id_entity_filters_key; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE UNIQUE INDEX subscription_subscription_id_entity_filters_key ON realtime.subscription USING btree (subscription_id, entity, filters); + + +-- +-- Name: bname; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX bname ON storage.buckets USING btree (name); + + +-- +-- Name: bucketid_objname; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX bucketid_objname ON storage.objects USING btree (bucket_id, name); + + +-- +-- Name: buckets_analytics_unique_name_idx; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX buckets_analytics_unique_name_idx ON storage.buckets_analytics USING btree (name) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_iceberg_namespaces_bucket_id; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX idx_iceberg_namespaces_bucket_id ON storage.iceberg_namespaces USING btree (catalog_id, name); + + +-- +-- Name: idx_iceberg_tables_location; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX idx_iceberg_tables_location ON storage.iceberg_tables USING btree (location); + + +-- +-- Name: idx_iceberg_tables_namespace_id; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX idx_iceberg_tables_namespace_id ON storage.iceberg_tables USING btree (catalog_id, namespace_id, name); + + +-- +-- Name: idx_multipart_uploads_list; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX idx_multipart_uploads_list ON storage.s3_multipart_uploads USING btree (bucket_id, key, created_at); + + +-- +-- Name: idx_objects_bucket_id_name; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX idx_objects_bucket_id_name ON storage.objects USING btree (bucket_id, name COLLATE "C"); + + +-- +-- Name: idx_objects_bucket_id_name_lower; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX idx_objects_bucket_id_name_lower ON storage.objects USING btree (bucket_id, lower(name) COLLATE "C"); + + +-- +-- Name: name_prefix_search; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX name_prefix_search ON storage.objects USING btree (name text_pattern_ops); + + +-- +-- Name: vector_indexes_name_bucket_id_idx; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX vector_indexes_name_bucket_id_idx ON storage.vector_indexes USING btree (name, bucket_id); + + +-- +-- Name: supabase_functions_hooks_h_table_id_h_name_idx; Type: INDEX; Schema: supabase_functions; Owner: - +-- + +CREATE INDEX supabase_functions_hooks_h_table_id_h_name_idx ON supabase_functions.hooks USING btree (hook_table_id, hook_name); + + +-- +-- Name: supabase_functions_hooks_request_id_idx; Type: INDEX; Schema: supabase_functions; Owner: - +-- + +CREATE INDEX supabase_functions_hooks_request_id_idx ON supabase_functions.hooks USING btree (request_id); + + +-- +-- Name: messages_2026_03_24_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_24_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_24_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_24_pkey; + + +-- +-- Name: messages_2026_03_25_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_25_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_25_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_25_pkey; + + +-- +-- Name: messages_2026_03_26_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_26_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_26_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_26_pkey; + + +-- +-- Name: messages_2026_03_27_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_27_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_27_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_27_pkey; + + +-- +-- Name: messages_2026_03_28_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_28_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_28_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_28_pkey; + + +-- +-- Name: messages_2026_03_29_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_29_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_29_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_29_pkey; + + +-- +-- Name: messages_2026_03_30_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_30_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_30_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_30_pkey; + + +-- +-- Name: users on_auth_user_created; Type: TRIGGER; Schema: auth; Owner: - +-- + +CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.handle_new_user(); + + +-- +-- Name: users trg_seed_patient_groups; Type: TRIGGER; Schema: auth; Owner: - +-- + +CREATE TRIGGER trg_seed_patient_groups AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.on_new_user_seed_patient_groups(); + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER agenda_bloqueios_updated_at BEFORE UPDATE ON public.agenda_bloqueios FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agendador_configuracoes agendador_slug_trigger; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER agendador_slug_trigger BEFORE INSERT OR UPDATE ON public.agendador_configuracoes FOR EACH ROW EXECUTE FUNCTION public.agendador_gerar_slug(); + + +-- +-- Name: tenant_members prevent_saas_membership_trigger; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER prevent_saas_membership_trigger BEFORE INSERT ON public.tenant_members FOR EACH ROW EXECUTE FUNCTION public.prevent_saas_membership(); + + +-- +-- Name: insurance_plan_services set_insurance_plan_services_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER set_insurance_plan_services_updated_at BEFORE UPDATE ON public.insurance_plan_services FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: user_settings t_user_settings_set_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER t_user_settings_set_updated_at BEFORE UPDATE ON public.user_settings FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_configuracoes tg_agenda_configuracoes_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_configuracoes_updated_at BEFORE UPDATE ON public.agenda_configuracoes FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_eventos tg_agenda_eventos_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_eventos_updated_at BEFORE UPDATE ON public.agenda_eventos FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_excecoes tg_agenda_excecoes_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_excecoes_updated_at BEFORE UPDATE ON public.agenda_excecoes FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_regras_semanais tg_agenda_regras_semanais_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_regras_semanais_updated_at BEFORE UPDATE ON public.agenda_regras_semanais FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: recurrence_rules tg_recurrence_rules_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_recurrence_rules_updated_at BEFORE UPDATE ON public.recurrence_rules FOR EACH ROW EXECUTE FUNCTION public.set_updated_at_recurrence(); + + +-- +-- Name: plan_public tr_plan_public_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tr_plan_public_updated_at BEFORE UPDATE ON public.plan_public FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: profiles trg_account_type_immutable; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_account_type_immutable BEFORE UPDATE OF account_type ON public.profiles FOR EACH ROW EXECUTE FUNCTION public.guard_account_type_immutable(); + + +-- +-- Name: agenda_configuracoes trg_agenda_cfg_sync; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_cfg_sync BEFORE INSERT OR UPDATE ON public.agenda_configuracoes FOR EACH ROW EXECUTE FUNCTION public.agenda_cfg_sync(); + + +-- +-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_del; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_eventos_busy_mirror_del AFTER DELETE ON public.agenda_eventos FOR EACH ROW WHEN (((old.mirror_of_event_id IS NULL) AND (old.tenant_id = old.owner_id))) EXECUTE FUNCTION public.sync_busy_mirror_agenda_eventos(); + + +-- +-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_ins; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_eventos_busy_mirror_ins AFTER INSERT ON public.agenda_eventos FOR EACH ROW WHEN (((new.mirror_of_event_id IS NULL) AND (new.tenant_id = new.owner_id) AND (new.visibility_scope = ANY (ARRAY['busy_only'::text, 'private'::text])))) EXECUTE FUNCTION public.sync_busy_mirror_agenda_eventos(); + + +-- +-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_upd; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_eventos_busy_mirror_upd AFTER UPDATE ON public.agenda_eventos FOR EACH ROW WHEN (((new.mirror_of_event_id IS NULL) AND (new.tenant_id = new.owner_id) AND ((new.visibility_scope IS DISTINCT FROM old.visibility_scope) OR (new.inicio_em IS DISTINCT FROM old.inicio_em) OR (new.fim_em IS DISTINCT FROM old.fim_em) OR (new.owner_id IS DISTINCT FROM old.owner_id) OR (new.tenant_id IS DISTINCT FROM old.tenant_id)))) EXECUTE FUNCTION public.sync_busy_mirror_agenda_eventos(); + + +-- +-- Name: agenda_regras_semanais trg_agenda_regras_semanais_no_overlap; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_regras_semanais_no_overlap BEFORE INSERT OR UPDATE ON public.agenda_regras_semanais FOR EACH ROW EXECUTE FUNCTION public.fn_agenda_regras_semanais_no_overlap(); + + +-- +-- Name: agenda_eventos trg_auto_financial_from_session; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_auto_financial_from_session AFTER UPDATE OF status ON public.agenda_eventos FOR EACH ROW EXECUTE FUNCTION public.auto_create_financial_record_from_session(); + + +-- +-- Name: notification_preferences trg_cancel_notifs_on_opt_out; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_cancel_notifs_on_opt_out AFTER UPDATE ON public.notification_preferences FOR EACH ROW EXECUTE FUNCTION public.cancel_notifications_on_opt_out(); + + +-- +-- Name: agenda_eventos trg_cancel_notifs_on_session_cancel; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_cancel_notifs_on_session_cancel AFTER UPDATE ON public.agenda_eventos FOR EACH ROW WHEN ((new.status IS DISTINCT FROM old.status)) EXECUTE FUNCTION public.cancel_notifications_on_session_cancel(); + + +-- +-- Name: company_profiles trg_company_profiles_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_company_profiles_updated_at BEFORE UPDATE ON public.company_profiles FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: determined_commitment_fields trg_determined_commitment_fields_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_determined_commitment_fields_updated_at BEFORE UPDATE ON public.determined_commitment_fields FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: determined_commitments trg_determined_commitments_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_determined_commitments_updated_at BEFORE UPDATE ON public.determined_commitments FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: email_layout_config trg_email_layout_config_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_email_layout_config_updated_at BEFORE UPDATE ON public.email_layout_config FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: email_templates_global trg_email_templates_global_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_email_templates_global_updated_at BEFORE UPDATE ON public.email_templates_global FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: email_templates_tenant trg_email_templates_tenant_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_email_templates_tenant_updated_at BEFORE UPDATE ON public.email_templates_tenant FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: financial_exceptions trg_financial_exceptions_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_financial_exceptions_updated_at BEFORE UPDATE ON public.financial_exceptions FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: financial_records trg_financial_records_auto_overdue; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_financial_records_auto_overdue BEFORE UPDATE ON public.financial_records FOR EACH ROW EXECUTE FUNCTION public.trg_fn_financial_records_auto_overdue(); + + +-- +-- Name: financial_records trg_financial_records_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_financial_records_updated_at BEFORE UPDATE ON public.financial_records FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: global_notices trg_global_notices_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_global_notices_updated_at BEFORE UPDATE ON public.global_notices FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: insurance_plans trg_insurance_plans_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_insurance_plans_updated_at BEFORE UPDATE ON public.insurance_plans FOR EACH ROW EXECUTE FUNCTION public.set_insurance_plans_updated_at(); + + +-- +-- Name: plans trg_no_change_core_plan_key; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_no_change_core_plan_key BEFORE UPDATE ON public.plans FOR EACH ROW EXECUTE FUNCTION public.guard_no_change_core_plan_key(); + + +-- +-- Name: plans trg_no_change_plan_target; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_no_change_plan_target BEFORE UPDATE ON public.plans FOR EACH ROW EXECUTE FUNCTION public.guard_no_change_plan_target(); + + +-- +-- Name: plans trg_no_delete_core_plans; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_no_delete_core_plans BEFORE DELETE ON public.plans FOR EACH ROW EXECUTE FUNCTION public.guard_no_delete_core_plans(); + + +-- +-- Name: notification_channels trg_notification_channels_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_channels_updated_at BEFORE UPDATE ON public.notification_channels FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_logs trg_notification_logs_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_logs_updated_at BEFORE UPDATE ON public.notification_logs FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_preferences trg_notification_preferences_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_preferences_updated_at BEFORE UPDATE ON public.notification_preferences FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_queue trg_notification_queue_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_queue_updated_at BEFORE UPDATE ON public.notification_queue FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_schedules trg_notification_schedules_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_schedules_updated_at BEFORE UPDATE ON public.notification_schedules FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_templates trg_notification_templates_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_templates_updated_at BEFORE UPDATE ON public.notification_templates FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patient_intake_requests trg_notify_on_intake; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notify_on_intake AFTER INSERT ON public.patient_intake_requests FOR EACH ROW EXECUTE FUNCTION public.notify_on_intake(); + + +-- +-- Name: agendador_solicitacoes trg_notify_on_scheduling; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notify_on_scheduling AFTER INSERT ON public.agendador_solicitacoes FOR EACH ROW EXECUTE FUNCTION public.notify_on_scheduling(); + + +-- +-- Name: agenda_eventos trg_notify_on_session_status; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notify_on_session_status AFTER UPDATE OF status ON public.agenda_eventos FOR EACH ROW EXECUTE FUNCTION public.notify_on_session_status(); + + +-- +-- Name: tenant_members trg_patient_cannot_own_tenant; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_cannot_own_tenant BEFORE INSERT OR UPDATE ON public.tenant_members FOR EACH ROW EXECUTE FUNCTION public.guard_patient_cannot_own_tenant(); + + +-- +-- Name: patient_groups trg_patient_groups_set_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_groups_set_updated_at BEFORE UPDATE ON public.patient_groups FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patient_intake_requests trg_patient_intake_requests_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_intake_requests_updated_at BEFORE UPDATE ON public.patient_intake_requests FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patient_tags trg_patient_tags_set_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_tags_set_updated_at BEFORE UPDATE ON public.patient_tags FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patients trg_patients_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patients_updated_at BEFORE UPDATE ON public.patients FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patients trg_patients_validate_members; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patients_validate_members BEFORE INSERT OR UPDATE OF tenant_id, responsible_member_id, patient_scope, therapist_member_id ON public.patients FOR EACH ROW EXECUTE FUNCTION public.patients_validate_member_consistency(); + + +-- +-- Name: payment_settings trg_payment_settings_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_payment_settings_updated_at BEFORE UPDATE ON public.payment_settings FOR EACH ROW EXECUTE FUNCTION public.update_payment_settings_updated_at(); + + +-- +-- Name: patient_groups trg_prevent_promoting_to_system; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_prevent_promoting_to_system BEFORE UPDATE ON public.patient_groups FOR EACH ROW EXECUTE FUNCTION public.prevent_promoting_to_system(); + + +-- +-- Name: patient_groups trg_prevent_system_group_changes; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_prevent_system_group_changes BEFORE DELETE OR UPDATE ON public.patient_groups FOR EACH ROW EXECUTE FUNCTION public.prevent_system_group_changes(); + + +-- +-- Name: professional_pricing trg_professional_pricing_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_professional_pricing_updated_at BEFORE UPDATE ON public.professional_pricing FOR EACH ROW EXECUTE FUNCTION public.update_professional_pricing_updated_at(); + + +-- +-- Name: profiles trg_profiles_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_profiles_updated_at BEFORE UPDATE ON public.profiles FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: services trg_services_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_services_updated_at BEFORE UPDATE ON public.services FOR EACH ROW EXECUTE FUNCTION public.set_services_updated_at(); + + +-- +-- Name: subscription_intents trg_subscription_intents_view_insert; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_subscription_intents_view_insert INSTEAD OF INSERT ON public.subscription_intents FOR EACH ROW EXECUTE FUNCTION public.subscription_intents_view_insert(); + + +-- +-- Name: subscriptions trg_subscriptions_validate_scope; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_subscriptions_validate_scope BEFORE INSERT OR UPDATE ON public.subscriptions FOR EACH ROW EXECUTE FUNCTION public.subscriptions_validate_scope(); + + +-- +-- Name: tenant_features trg_tenant_features_guard_with_plan; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_tenant_features_guard_with_plan BEFORE INSERT OR UPDATE ON public.tenant_features FOR EACH ROW EXECUTE FUNCTION public.tenant_features_guard_with_plan(); + + +-- +-- Name: tenant_features trg_tenant_features_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_tenant_features_updated_at BEFORE UPDATE ON public.tenant_features FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: tenants trg_tenant_kind_immutable; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_tenant_kind_immutable BEFORE UPDATE OF kind ON public.tenants FOR EACH ROW EXECUTE FUNCTION public.guard_tenant_kind_immutable(); + + +-- +-- Name: therapist_payouts trg_therapist_payouts_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_therapist_payouts_updated_at BEFORE UPDATE ON public.therapist_payouts FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: user_settings trg_user_settings_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_user_settings_updated_at BEFORE UPDATE ON public.user_settings FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: subscription tr_check_filters; Type: TRIGGER; Schema: realtime; Owner: - +-- + +CREATE TRIGGER tr_check_filters BEFORE INSERT OR UPDATE ON realtime.subscription FOR EACH ROW EXECUTE FUNCTION realtime.subscription_check_filters(); + + +-- +-- Name: buckets enforce_bucket_name_length_trigger; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER enforce_bucket_name_length_trigger BEFORE INSERT OR UPDATE OF name ON storage.buckets FOR EACH ROW EXECUTE FUNCTION storage.enforce_bucket_name_length(); + + +-- +-- Name: buckets protect_buckets_delete; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER protect_buckets_delete BEFORE DELETE ON storage.buckets FOR EACH STATEMENT EXECUTE FUNCTION storage.protect_delete(); + + +-- +-- Name: objects protect_objects_delete; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER protect_objects_delete BEFORE DELETE ON storage.objects FOR EACH STATEMENT EXECUTE FUNCTION storage.protect_delete(); + + +-- +-- Name: objects update_objects_updated_at; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER update_objects_updated_at BEFORE UPDATE ON storage.objects FOR EACH ROW EXECUTE FUNCTION storage.update_updated_at_column(); + + +-- +-- Name: extensions extensions_tenant_external_id_fkey; Type: FK CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.extensions + ADD CONSTRAINT extensions_tenant_external_id_fkey FOREIGN KEY (tenant_external_id) REFERENCES _realtime.tenants(external_id) ON DELETE CASCADE; + + +-- +-- Name: identities identities_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.identities + ADD CONSTRAINT identities_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: mfa_amr_claims mfa_amr_claims_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_amr_claims + ADD CONSTRAINT mfa_amr_claims_session_id_fkey FOREIGN KEY (session_id) REFERENCES auth.sessions(id) ON DELETE CASCADE; + + +-- +-- Name: mfa_challenges mfa_challenges_auth_factor_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_challenges + ADD CONSTRAINT mfa_challenges_auth_factor_id_fkey FOREIGN KEY (factor_id) REFERENCES auth.mfa_factors(id) ON DELETE CASCADE; + + +-- +-- Name: mfa_factors mfa_factors_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_factors + ADD CONSTRAINT mfa_factors_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_authorizations oauth_authorizations_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_client_id_fkey FOREIGN KEY (client_id) REFERENCES auth.oauth_clients(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_authorizations oauth_authorizations_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_consents oauth_consents_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_client_id_fkey FOREIGN KEY (client_id) REFERENCES auth.oauth_clients(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_consents oauth_consents_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: one_time_tokens one_time_tokens_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.one_time_tokens + ADD CONSTRAINT one_time_tokens_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: refresh_tokens refresh_tokens_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens + ADD CONSTRAINT refresh_tokens_session_id_fkey FOREIGN KEY (session_id) REFERENCES auth.sessions(id) ON DELETE CASCADE; + + +-- +-- Name: saml_providers saml_providers_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_providers + ADD CONSTRAINT saml_providers_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; + + +-- +-- Name: saml_relay_states saml_relay_states_flow_state_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_relay_states + ADD CONSTRAINT saml_relay_states_flow_state_id_fkey FOREIGN KEY (flow_state_id) REFERENCES auth.flow_state(id) ON DELETE CASCADE; + + +-- +-- Name: saml_relay_states saml_relay_states_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_relay_states + ADD CONSTRAINT saml_relay_states_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; + + +-- +-- Name: sessions sessions_oauth_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sessions + ADD CONSTRAINT sessions_oauth_client_id_fkey FOREIGN KEY (oauth_client_id) REFERENCES auth.oauth_clients(id) ON DELETE CASCADE; + + +-- +-- Name: sessions sessions_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sessions + ADD CONSTRAINT sessions_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: sso_domains sso_domains_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sso_domains + ADD CONSTRAINT sso_domains_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; + + +-- +-- Name: addon_credits addon_credits_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT addon_credits_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id); + + +-- +-- Name: addon_credits addon_credits_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT addon_credits_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id); + + +-- +-- Name: addon_transactions addon_transactions_admin_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_admin_user_id_fkey FOREIGN KEY (admin_user_id) REFERENCES auth.users(id); + + +-- +-- Name: addon_transactions addon_transactions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id); + + +-- +-- Name: addon_transactions addon_transactions_product_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.addon_products(id); + + +-- +-- Name: addon_transactions addon_transactions_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id); + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_bloqueios + ADD CONSTRAINT agenda_bloqueios_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_bloqueios + ADD CONSTRAINT agenda_bloqueios_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_configuracoes + ADD CONSTRAINT agenda_configuracoes_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_eventos agenda_eventos_billing_contract_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_billing_contract_id_fkey FOREIGN KEY (billing_contract_id) REFERENCES public.billing_contracts(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_determined_commitment_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_determined_commitment_fk FOREIGN KEY (determined_commitment_id) REFERENCES public.determined_commitments(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_insurance_plan_id_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_insurance_plan_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_insurance_plan_service_id_fkey FOREIGN KEY (insurance_plan_service_id) REFERENCES public.insurance_plan_services(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_recurrence_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_recurrence_id_fkey FOREIGN KEY (recurrence_id) REFERENCES public.recurrence_rules(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_terapeuta_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_terapeuta_fk FOREIGN KEY (terapeuta_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_excecoes agenda_excecoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_excecoes + ADD CONSTRAINT agenda_excecoes_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_online_slots agenda_online_slots_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_online_slots agenda_online_slots_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_regras_semanais + ADD CONSTRAINT agenda_regras_semanais_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais + ADD CONSTRAINT agenda_slots_bloqueados_semanais_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_regras + ADD CONSTRAINT agenda_slots_regras_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_configuracoes agendador_configuracoes_owner_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_configuracoes + ADD CONSTRAINT agendador_configuracoes_owner_fk FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_configuracoes agendador_configuracoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_configuracoes + ADD CONSTRAINT agendador_configuracoes_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_solicitacoes agendador_sol_owner_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_solicitacoes + ADD CONSTRAINT agendador_sol_owner_fk FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_solicitacoes agendador_sol_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_solicitacoes + ADD CONSTRAINT agendador_sol_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: billing_contracts billing_contracts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.billing_contracts + ADD CONSTRAINT billing_contracts_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: billing_contracts billing_contracts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.billing_contracts + ADD CONSTRAINT billing_contracts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: commitment_services commitment_services_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_services + ADD CONSTRAINT commitment_services_commitment_id_fkey FOREIGN KEY (commitment_id) REFERENCES public.agenda_eventos(id) ON DELETE CASCADE; + + +-- +-- Name: commitment_services commitment_services_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_services + ADD CONSTRAINT commitment_services_service_id_fkey FOREIGN KEY (service_id) REFERENCES public.services(id) ON DELETE RESTRICT; + + +-- +-- Name: commitment_time_logs commitment_time_logs_calendar_event_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_calendar_event_id_fkey FOREIGN KEY (calendar_event_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; + + +-- +-- Name: commitment_time_logs commitment_time_logs_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_commitment_id_fkey FOREIGN KEY (commitment_id) REFERENCES public.determined_commitments(id) ON DELETE CASCADE; + + +-- +-- Name: commitment_time_logs commitment_time_logs_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: company_profiles company_profiles_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.company_profiles + ADD CONSTRAINT company_profiles_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: determined_commitment_fields determined_commitment_fields_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitment_fields + ADD CONSTRAINT determined_commitment_fields_commitment_id_fkey FOREIGN KEY (commitment_id) REFERENCES public.determined_commitments(id) ON DELETE CASCADE; + + +-- +-- Name: determined_commitment_fields determined_commitment_fields_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitment_fields + ADD CONSTRAINT determined_commitment_fields_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: determined_commitments determined_commitments_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitments + ADD CONSTRAINT determined_commitments_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: email_layout_config email_layout_config_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_layout_config + ADD CONSTRAINT email_layout_config_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: email_templates_tenant email_templates_tenant_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: email_templates_tenant email_templates_tenant_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: feriados feriados_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: feriados feriados_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: financial_categories financial_categories_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_categories + ADD CONSTRAINT financial_categories_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: financial_exceptions financial_exceptions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_exceptions + ADD CONSTRAINT financial_exceptions_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: financial_records financial_records_agenda_evento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_agenda_evento_id_fkey FOREIGN KEY (agenda_evento_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_category_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_category_id_fkey FOREIGN KEY (category_id) REFERENCES public.financial_categories(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_insurance_plan_id_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_user_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: global_notices global_notices_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.global_notices + ADD CONSTRAINT global_notices_created_by_fkey FOREIGN KEY (created_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: insurance_plan_services insurance_plan_services_plan_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plan_services + ADD CONSTRAINT insurance_plan_services_plan_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE CASCADE; + + +-- +-- Name: insurance_plans insurance_plans_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plans + ADD CONSTRAINT insurance_plans_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: module_features module_features_feature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.module_features + ADD CONSTRAINT module_features_feature_id_fkey FOREIGN KEY (feature_id) REFERENCES public.features(id) ON DELETE CASCADE; + + +-- +-- Name: module_features module_features_module_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.module_features + ADD CONSTRAINT module_features_module_id_fkey FOREIGN KEY (module_id) REFERENCES public.modules(id) ON DELETE CASCADE; + + +-- +-- Name: notice_dismissals notice_dismissals_notice_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_notice_id_fkey FOREIGN KEY (notice_id) REFERENCES public.global_notices(id) ON DELETE CASCADE; + + +-- +-- Name: notice_dismissals notice_dismissals_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notification_channels notification_channels_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_channels + ADD CONSTRAINT notification_channels_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notification_logs notification_logs_queue_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_logs + ADD CONSTRAINT notification_logs_queue_id_fkey FOREIGN KEY (queue_id) REFERENCES public.notification_queue(id) ON DELETE SET NULL; + + +-- +-- Name: notification_schedules notification_schedules_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_schedules + ADD CONSTRAINT notification_schedules_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notification_templates notification_templates_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_templates + ADD CONSTRAINT notification_templates_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notifications notifications_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT notifications_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: patient_discounts patient_discounts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_discounts + ADD CONSTRAINT patient_discounts_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: patient_discounts patient_discounts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_discounts + ADD CONSTRAINT patient_discounts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_group_patient patient_group_patient_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_group_patient + ADD CONSTRAINT patient_group_patient_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_group_patient patient_group_patient_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_group_patient + ADD CONSTRAINT patient_group_patient_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_groups patient_groups_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_groups + ADD CONSTRAINT patient_groups_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_intake_requests patient_intake_requests_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_intake_requests + ADD CONSTRAINT patient_intake_requests_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_invites patient_invites_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_invites + ADD CONSTRAINT patient_invites_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag patient_patient_tag_tag_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT patient_patient_tag_tag_id_fkey FOREIGN KEY (tag_id) REFERENCES public.patient_tags(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag patient_patient_tag_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT patient_patient_tag_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_tags patient_tags_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_tags + ADD CONSTRAINT patient_tags_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patients patients_responsible_member_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_responsible_member_id_fkey FOREIGN KEY (responsible_member_id) REFERENCES public.tenant_members(id) ON DELETE RESTRICT; + + +-- +-- Name: patients patients_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patients patients_therapist_member_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_therapist_member_id_fkey FOREIGN KEY (therapist_member_id) REFERENCES public.tenant_members(id); + + +-- +-- Name: patients patients_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: payment_settings payment_settings_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: payment_settings payment_settings_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: plan_features plan_features_feature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_features + ADD CONSTRAINT plan_features_feature_id_fkey FOREIGN KEY (feature_id) REFERENCES public.features(id) ON DELETE CASCADE; + + +-- +-- Name: plan_features plan_features_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_features + ADD CONSTRAINT plan_features_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: plan_prices plan_prices_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_prices + ADD CONSTRAINT plan_prices_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: plan_public_bullets plan_public_bullets_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public_bullets + ADD CONSTRAINT plan_public_bullets_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: plan_public plan_public_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public + ADD CONSTRAINT plan_public_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag ppt_patient_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT ppt_patient_fk FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag ppt_tag_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT ppt_tag_fk FOREIGN KEY (tag_id) REFERENCES public.patient_tags(id) ON DELETE CASCADE; + + +-- +-- Name: professional_pricing professional_pricing_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.professional_pricing + ADD CONSTRAINT professional_pricing_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: profiles profiles_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.profiles + ADD CONSTRAINT profiles_id_fkey FOREIGN KEY (id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_agenda_evento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_agenda_evento_id_fkey FOREIGN KEY (agenda_evento_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_recurrence_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_recurrence_id_fkey FOREIGN KEY (recurrence_id) REFERENCES public.recurrence_rules(id) ON DELETE CASCADE; + + +-- +-- Name: recurrence_rule_services recurrence_rule_services_rule_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rule_services + ADD CONSTRAINT recurrence_rule_services_rule_id_fkey FOREIGN KEY (rule_id) REFERENCES public.recurrence_rules(id) ON DELETE CASCADE; + + +-- +-- Name: recurrence_rule_services recurrence_rule_services_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rule_services + ADD CONSTRAINT recurrence_rule_services_service_id_fkey FOREIGN KEY (service_id) REFERENCES public.services(id) ON DELETE RESTRICT; + + +-- +-- Name: recurrence_rules recurrence_rules_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rules + ADD CONSTRAINT recurrence_rules_insurance_plan_id_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + + +-- +-- Name: recurrence_rules recurrence_rules_insurance_plan_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rules + ADD CONSTRAINT recurrence_rules_insurance_plan_service_id_fkey FOREIGN KEY (insurance_plan_service_id) REFERENCES public.insurance_plan_services(id) ON DELETE SET NULL; + + +-- +-- Name: saas_admins saas_admins_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_admins + ADD CONSTRAINT saas_admins_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: saas_doc_votos saas_doc_votos_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_doc_id_fkey FOREIGN KEY (doc_id) REFERENCES public.saas_docs(id) ON DELETE CASCADE; + + +-- +-- Name: saas_doc_votos saas_doc_votos_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: saas_faq_itens saas_faq_itens_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_faq_itens + ADD CONSTRAINT saas_faq_itens_doc_id_fkey FOREIGN KEY (doc_id) REFERENCES public.saas_docs(id) ON DELETE CASCADE; + + +-- +-- Name: services services_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.services + ADD CONSTRAINT services_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: subscription_intents_personal sint_personal_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_personal + ADD CONSTRAINT sint_personal_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES public.subscriptions(id) ON DELETE SET NULL; + + +-- +-- Name: subscription_intents_tenant sint_tenant_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_tenant + ADD CONSTRAINT sint_tenant_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES public.subscriptions(id) ON DELETE SET NULL; + + +-- +-- Name: subscription_events subscription_events_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_events + ADD CONSTRAINT subscription_events_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES public.subscriptions(id) ON DELETE CASCADE; + + +-- +-- Name: subscription_intents_personal subscription_intents_personal_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_personal + ADD CONSTRAINT subscription_intents_personal_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE RESTRICT; + + +-- +-- Name: subscription_intents_tenant subscription_intents_tenant_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_tenant + ADD CONSTRAINT subscription_intents_tenant_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE RESTRICT; + + +-- +-- Name: subscription_intents_legacy subscription_intents_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_legacy + ADD CONSTRAINT subscription_intents_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: subscriptions subscriptions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscriptions + ADD CONSTRAINT subscriptions_owner_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: subscriptions subscriptions_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscriptions + ADD CONSTRAINT subscriptions_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE RESTRICT; + + +-- +-- Name: support_sessions support_sessions_admin_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_admin_fk FOREIGN KEY (admin_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: support_sessions support_sessions_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_features tenant_features_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_features + ADD CONSTRAINT tenant_features_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_invites tenant_invites_accepted_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_accepted_by_fkey FOREIGN KEY (accepted_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: tenant_invites tenant_invites_invited_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_invited_by_fkey FOREIGN KEY (invited_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: tenant_invites tenant_invites_revoked_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_revoked_by_fkey FOREIGN KEY (revoked_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: tenant_invites tenant_invites_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_members tenant_members_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_members tenant_members_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_modules tenant_modules_module_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_module_id_fkey FOREIGN KEY (module_id) REFERENCES public.modules(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_modules tenant_modules_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: therapist_payout_records therapist_payout_records_financial_record_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payout_records + ADD CONSTRAINT therapist_payout_records_financial_record_id_fkey FOREIGN KEY (financial_record_id) REFERENCES public.financial_records(id) ON DELETE RESTRICT; + + +-- +-- Name: therapist_payout_records therapist_payout_records_payout_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payout_records + ADD CONSTRAINT therapist_payout_records_payout_id_fkey FOREIGN KEY (payout_id) REFERENCES public.therapist_payouts(id) ON DELETE CASCADE; + + +-- +-- Name: therapist_payouts therapist_payouts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payouts + ADD CONSTRAINT therapist_payouts_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: therapist_payouts therapist_payouts_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payouts + ADD CONSTRAINT therapist_payouts_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: twilio_subaccount_usage twilio_subaccount_usage_channel_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.twilio_subaccount_usage + ADD CONSTRAINT twilio_subaccount_usage_channel_fk FOREIGN KEY (channel_id) REFERENCES public.notification_channels(id) ON DELETE CASCADE; + + +-- +-- Name: user_settings user_settings_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_settings + ADD CONSTRAINT user_settings_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: iceberg_namespaces iceberg_namespaces_catalog_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_namespaces + ADD CONSTRAINT iceberg_namespaces_catalog_id_fkey FOREIGN KEY (catalog_id) REFERENCES storage.buckets_analytics(id) ON DELETE CASCADE; + + +-- +-- Name: iceberg_tables iceberg_tables_catalog_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_tables + ADD CONSTRAINT iceberg_tables_catalog_id_fkey FOREIGN KEY (catalog_id) REFERENCES storage.buckets_analytics(id) ON DELETE CASCADE; + + +-- +-- Name: iceberg_tables iceberg_tables_namespace_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_tables + ADD CONSTRAINT iceberg_tables_namespace_id_fkey FOREIGN KEY (namespace_id) REFERENCES storage.iceberg_namespaces(id) ON DELETE CASCADE; + + +-- +-- Name: objects objects_bucketId_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.objects + ADD CONSTRAINT "objects_bucketId_fkey" FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); + + +-- +-- Name: s3_multipart_uploads s3_multipart_uploads_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads + ADD CONSTRAINT s3_multipart_uploads_bucket_id_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); + + +-- +-- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads_parts + ADD CONSTRAINT s3_multipart_uploads_parts_bucket_id_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); + + +-- +-- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_upload_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads_parts + ADD CONSTRAINT s3_multipart_uploads_parts_upload_id_fkey FOREIGN KEY (upload_id) REFERENCES storage.s3_multipart_uploads(id) ON DELETE CASCADE; + + +-- +-- Name: vector_indexes vector_indexes_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.vector_indexes + ADD CONSTRAINT vector_indexes_bucket_id_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets_vectors(id); + + +-- +-- Name: audit_log_entries; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.audit_log_entries ENABLE ROW LEVEL SECURITY; + +-- +-- Name: flow_state; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.flow_state ENABLE ROW LEVEL SECURITY; + +-- +-- Name: identities; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.identities ENABLE ROW LEVEL SECURITY; + +-- +-- Name: instances; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.instances ENABLE ROW LEVEL SECURITY; + +-- +-- Name: mfa_amr_claims; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.mfa_amr_claims ENABLE ROW LEVEL SECURITY; + +-- +-- Name: mfa_challenges; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.mfa_challenges ENABLE ROW LEVEL SECURITY; + +-- +-- Name: mfa_factors; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.mfa_factors ENABLE ROW LEVEL SECURITY; + +-- +-- Name: one_time_tokens; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.one_time_tokens ENABLE ROW LEVEL SECURITY; + +-- +-- Name: refresh_tokens; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.refresh_tokens ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saml_providers; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.saml_providers ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saml_relay_states; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.saml_relay_states ENABLE ROW LEVEL SECURITY; + +-- +-- Name: schema_migrations; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.schema_migrations ENABLE ROW LEVEL SECURITY; + +-- +-- Name: sessions; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.sessions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: sso_domains; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.sso_domains ENABLE ROW LEVEL SECURITY; + +-- +-- Name: sso_providers; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.sso_providers ENABLE ROW LEVEL SECURITY; + +-- +-- Name: users; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.users ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_credits; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.addon_credits ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_credits addon_credits_admin_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_credits_admin_select ON public.addon_credits FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_credits addon_credits_admin_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_credits_admin_write ON public.addon_credits TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_credits addon_credits_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_credits_select_own ON public.addon_credits FOR SELECT TO authenticated USING ((public.is_tenant_member(tenant_id) OR (owner_id = auth.uid()))); + + +-- +-- Name: addon_products; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.addon_products ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_products addon_products_admin_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_products_admin_all ON public.addon_products TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_products addon_products_select_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_products_select_authenticated ON public.addon_products FOR SELECT TO authenticated USING (((deleted_at IS NULL) AND (is_active = true) AND (is_visible = true))); + + +-- +-- Name: addon_transactions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.addon_transactions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_transactions addon_transactions_admin_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_transactions_admin_insert ON public.addon_transactions FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_transactions addon_transactions_admin_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_transactions_admin_select ON public.addon_transactions FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_transactions addon_transactions_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_transactions_select_own ON public.addon_transactions FOR SELECT TO authenticated USING ((public.is_tenant_member(tenant_id) OR (owner_id = auth.uid()))); + + +-- +-- Name: agenda_bloqueios; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_bloqueios ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_configuracoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_configuracoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_configuracoes agenda_configuracoes_clinic_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_configuracoes_clinic_read ON public.agenda_configuracoes FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_clinic_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_configuracoes_clinic_write ON public.agenda_configuracoes USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_configuracoes_owner ON public.agenda_configuracoes USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_eventos; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_eventos ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_eventos agenda_eventos_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_delete ON public.agenda_eventos FOR DELETE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.delete'::text))); + + +-- +-- Name: agenda_eventos agenda_eventos_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_insert ON public.agenda_eventos FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.create'::text))); + + +-- +-- Name: agenda_eventos agenda_eventos_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_owner_all ON public.agenda_eventos TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_eventos agenda_eventos_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_select ON public.agenda_eventos FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_eventos agenda_eventos_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_update ON public.agenda_eventos FOR UPDATE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_excecoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_excecoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_excecoes agenda_excecoes_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_excecoes_owner ON public.agenda_excecoes USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_excecoes agenda_excecoes_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_excecoes_select ON public.agenda_excecoes FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_excecoes agenda_excecoes_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_excecoes_write ON public.agenda_excecoes USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_online_slots; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_online_slots ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_online_slots agenda_online_slots_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_online_slots_owner ON public.agenda_online_slots USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_online_slots agenda_online_slots_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_online_slots_select ON public.agenda_online_slots FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_online_slots agenda_online_slots_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_online_slots_write ON public.agenda_online_slots USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_regras_semanais; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_regras_semanais ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_regras_semanais_owner ON public.agenda_regras_semanais USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_regras_semanais_select ON public.agenda_regras_semanais FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_regras_semanais_write ON public.agenda_regras_semanais USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_slots_bloqueados_semanais; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_slots_bloqueados_semanais ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_bloqueados_semanais_select ON public.agenda_slots_bloqueados_semanais FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_bloqueados_semanais_write ON public.agenda_slots_bloqueados_semanais USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_slots_regras; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_slots_regras ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_slots_regras agenda_slots_regras_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_regras_select ON public.agenda_slots_regras FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_regras_write ON public.agenda_slots_regras USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agendador_configuracoes agendador_cfg_public_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_cfg_public_read ON public.agendador_configuracoes FOR SELECT TO anon USING (((ativo = true) AND (link_slug IS NOT NULL))); + + +-- +-- Name: agendador_configuracoes agendador_cfg_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_cfg_select ON public.agendador_configuracoes FOR SELECT USING ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_configuracoes agendador_cfg_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_cfg_write ON public.agendador_configuracoes USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_configuracoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agendador_configuracoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agendador_solicitacoes agendador_sol_owner_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_owner_select ON public.agendador_solicitacoes FOR SELECT USING ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_solicitacoes agendador_sol_owner_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_owner_write ON public.agendador_solicitacoes USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_solicitacoes agendador_sol_patient_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_patient_read ON public.agendador_solicitacoes FOR SELECT TO authenticated USING (((auth.uid() = user_id) OR (auth.uid() = owner_id))); + + +-- +-- Name: agendador_solicitacoes agendador_sol_public_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_public_insert ON public.agendador_solicitacoes FOR INSERT TO anon WITH CHECK (true); + + +-- +-- Name: agendador_solicitacoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agendador_solicitacoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: billing_contracts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.billing_contracts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: billing_contracts billing_contracts: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "billing_contracts: owner full access" ON public.billing_contracts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_delete ON public.agenda_bloqueios FOR DELETE TO authenticated USING ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_insert ON public.agenda_bloqueios FOR INSERT TO authenticated WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_select_clinic; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_select_clinic ON public.agenda_bloqueios FOR SELECT TO authenticated USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE ((tenant_members.user_id = auth.uid()) AND (tenant_members.role = ANY (ARRAY['admin'::text, 'clinic_admin'::text, 'tenant_admin'::text, 'secretary'::text])))))); + + +-- +-- Name: agenda_bloqueios bloqueios_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_select_own ON public.agenda_bloqueios FOR SELECT TO authenticated USING ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_update ON public.agenda_bloqueios FOR UPDATE TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: saas_docs clinic_admin_read_all_docs; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY clinic_admin_read_all_docs ON public.saas_docs FOR SELECT TO authenticated USING (((ativo = true) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = ANY (ARRAY['clinic_admin'::text, 'tenant_admin'::text]))))))); + + +-- +-- Name: commitment_services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.commitment_services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: commitment_services commitment_services: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "commitment_services: owner full access" ON public.commitment_services USING ((EXISTS ( SELECT 1 + FROM public.services s + WHERE ((s.id = commitment_services.service_id) AND (s.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.services s + WHERE ((s.id = commitment_services.service_id) AND (s.owner_id = auth.uid()))))); + + +-- +-- Name: commitment_time_logs; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.commitment_time_logs ENABLE ROW LEVEL SECURITY; + +-- +-- Name: company_profiles; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.company_profiles ENABLE ROW LEVEL SECURITY; + +-- +-- Name: company_profiles company_profiles_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_delete ON public.company_profiles FOR DELETE USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: company_profiles company_profiles_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_insert ON public.company_profiles FOR INSERT WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: company_profiles company_profiles_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_select ON public.company_profiles FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: company_profiles company_profiles_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_update ON public.company_profiles FOR UPDATE USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: commitment_time_logs ctl_delete_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_delete_for_active_member ON public.commitment_time_logs FOR DELETE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: commitment_time_logs ctl_insert_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_insert_for_active_member ON public.commitment_time_logs FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: commitment_time_logs ctl_select_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_select_for_active_member ON public.commitment_time_logs FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: commitment_time_logs ctl_update_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_update_for_active_member ON public.commitment_time_logs FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitments dc_delete_custom_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_delete_custom_for_active_member ON public.determined_commitments FOR DELETE TO authenticated USING (((is_native = false) AND (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: determined_commitments dc_insert_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_insert_for_active_member ON public.determined_commitments FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitments dc_select_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_select_for_active_member ON public.determined_commitments FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitments dc_update_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_update_for_active_member ON public.determined_commitments FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_delete_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_delete_for_active_member ON public.determined_commitment_fields FOR DELETE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_insert_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_insert_for_active_member ON public.determined_commitment_fields FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_select_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_select_for_active_member ON public.determined_commitment_fields FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_update_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_update_for_active_member ON public.determined_commitment_fields FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: agenda_bloqueios delete own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "delete own" ON public.agenda_bloqueios FOR DELETE USING ((owner_id = auth.uid())); + + +-- +-- Name: determined_commitment_fields; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.determined_commitment_fields ENABLE ROW LEVEL SECURITY; + +-- +-- Name: determined_commitments; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.determined_commitments ENABLE ROW LEVEL SECURITY; + +-- +-- Name: dev_user_credentials dev_creds_select_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dev_creds_select_saas_admin ON public.dev_user_credentials FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles p + WHERE ((p.id = auth.uid()) AND (p.role = 'saas_admin'::text))))); + + +-- +-- Name: dev_user_credentials dev_creds_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dev_creds_write_saas_admin ON public.dev_user_credentials TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles p + WHERE ((p.id = auth.uid()) AND (p.role = 'saas_admin'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.profiles p + WHERE ((p.id = auth.uid()) AND (p.role = 'saas_admin'::text))))); + + +-- +-- Name: dev_user_credentials; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.dev_user_credentials ENABLE ROW LEVEL SECURITY; + +-- +-- Name: email_layout_config; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.email_layout_config ENABLE ROW LEVEL SECURITY; + +-- +-- Name: email_templates_global; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.email_templates_global ENABLE ROW LEVEL SECURITY; + +-- +-- Name: email_templates_tenant; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.email_templates_tenant ENABLE ROW LEVEL SECURITY; + +-- +-- Name: entitlements_invalidation ent_inv_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ent_inv_select_own ON public.entitlements_invalidation FOR SELECT USING (((owner_id = auth.uid()) OR public.is_saas_admin())); + + +-- +-- Name: entitlements_invalidation ent_inv_update_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ent_inv_update_saas ON public.entitlements_invalidation FOR UPDATE USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: entitlements_invalidation ent_inv_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ent_inv_write_saas ON public.entitlements_invalidation FOR INSERT WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: entitlements_invalidation; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.entitlements_invalidation ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_faq faq_admin_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_admin_write ON public.saas_faq TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = ANY (ARRAY['saas_admin'::text, 'tenant_admin'::text, 'clinic_admin'::text])))))); + + +-- +-- Name: saas_faq faq_auth_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_auth_read ON public.saas_faq FOR SELECT TO authenticated USING ((ativo = true)); + + +-- +-- Name: saas_faq_itens faq_itens_admin_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_itens_admin_write ON public.saas_faq_itens TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = ANY (ARRAY['saas_admin'::text, 'tenant_admin'::text, 'clinic_admin'::text])))))); + + +-- +-- Name: saas_faq_itens faq_itens_auth_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_itens_auth_read ON public.saas_faq_itens FOR SELECT TO authenticated USING (((ativo = true) AND (EXISTS ( SELECT 1 + FROM public.saas_docs d + WHERE ((d.id = saas_faq_itens.doc_id) AND (d.ativo = true)))))); + + +-- +-- Name: saas_faq faq_public_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_public_read ON public.saas_faq FOR SELECT USING (((publico = true) AND (ativo = true))); + + +-- +-- Name: features; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.features ENABLE ROW LEVEL SECURITY; + +-- +-- Name: features features_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY features_read_authenticated ON public.features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: features features_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY features_write_saas_admin ON public.features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: feriados; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.feriados ENABLE ROW LEVEL SECURITY; + +-- +-- Name: feriados feriados_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_delete ON public.feriados FOR DELETE USING ((owner_id = auth.uid())); + + +-- +-- Name: feriados feriados_global_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_global_select ON public.feriados FOR SELECT USING ((tenant_id IS NULL)); + + +-- +-- Name: feriados feriados_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_insert ON public.feriados FOR INSERT WITH CHECK ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_saas_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_saas_delete ON public.feriados FOR DELETE USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_saas_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_saas_insert ON public.feriados FOR INSERT WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_saas_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_saas_select ON public.feriados FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_select ON public.feriados FOR SELECT USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: financial_categories; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.financial_categories ENABLE ROW LEVEL SECURITY; + +-- +-- Name: financial_categories financial_categories_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_categories_self ON public.financial_categories USING ((auth.uid() = user_id)) WITH CHECK ((auth.uid() = user_id)); + + +-- +-- Name: financial_exceptions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.financial_exceptions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: financial_exceptions financial_exceptions: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "financial_exceptions: owner full access" ON public.financial_exceptions USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: financial_exceptions financial_exceptions: tenant members read clinic rules; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "financial_exceptions: tenant members read clinic rules" ON public.financial_exceptions FOR SELECT USING (((owner_id IS NULL) AND (EXISTS ( SELECT 1 + FROM public.owner_users ou + WHERE ((ou.owner_id = financial_exceptions.tenant_id) AND (ou.user_id = auth.uid())))))); + + +-- +-- Name: financial_records; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.financial_records ENABLE ROW LEVEL SECURITY; + +-- +-- Name: financial_records financial_records_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_records_self ON public.financial_records USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: financial_records financial_records_tenant_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_records_tenant_admin ON public.financial_records FOR SELECT USING (((tenant_id IS NOT NULL) AND public.is_tenant_admin(tenant_id))); + + +-- +-- Name: financial_records financial_records_tenant_member_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_records_tenant_member_read ON public.financial_records FOR SELECT USING (((tenant_id IS NOT NULL) AND (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = financial_records.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: email_templates_global global templates readable by authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "global templates readable by authenticated" ON public.email_templates_global FOR SELECT USING ((auth.role() = 'authenticated'::text)); + + +-- +-- Name: global_notices; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.global_notices ENABLE ROW LEVEL SECURITY; + +-- +-- Name: global_notices global_notices_saas_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY global_notices_saas_all ON public.global_notices TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: global_notices global_notices_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY global_notices_select ON public.global_notices FOR SELECT TO authenticated USING ((is_active = true)); + + +-- +-- Name: agenda_bloqueios insert own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "insert own" ON public.agenda_bloqueios FOR INSERT WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: insurance_plan_services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.insurance_plan_services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: insurance_plan_services insurance_plan_services_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY insurance_plan_services_owner ON public.insurance_plan_services USING ((EXISTS ( SELECT 1 + FROM public.insurance_plans ip + WHERE ((ip.id = insurance_plan_services.insurance_plan_id) AND (ip.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.insurance_plans ip + WHERE ((ip.id = insurance_plan_services.insurance_plan_id) AND (ip.owner_id = auth.uid()))))); + + +-- +-- Name: insurance_plans; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.insurance_plans ENABLE ROW LEVEL SECURITY; + +-- +-- Name: insurance_plans insurance_plans: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "insurance_plans: owner full access" ON public.insurance_plans USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: login_carousel_slides; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.login_carousel_slides ENABLE ROW LEVEL SECURITY; + +-- +-- Name: module_features; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.module_features ENABLE ROW LEVEL SECURITY; + +-- +-- Name: module_features module_features_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY module_features_read_authenticated ON public.module_features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: module_features module_features_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY module_features_write_saas_admin ON public.module_features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: modules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.modules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: modules modules_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY modules_read_authenticated ON public.modules FOR SELECT TO authenticated USING (true); + + +-- +-- Name: modules modules_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY modules_write_saas_admin ON public.modules TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: notice_dismissals; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notice_dismissals ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notice_dismissals notice_dismissals_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notice_dismissals_own ON public.notice_dismissals TO authenticated USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: notification_logs notif_logs_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_logs_owner ON public.notification_logs FOR SELECT USING ((owner_id = auth.uid())); + + +-- +-- Name: notification_preferences notif_prefs_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_prefs_owner ON public.notification_preferences USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: notification_queue notif_queue_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_queue_owner ON public.notification_queue FOR SELECT USING ((owner_id = auth.uid())); + + +-- +-- Name: notification_schedules notif_schedules_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_schedules_owner ON public.notification_schedules USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: notification_templates notif_templates_admin_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_templates_admin_all ON public.notification_templates TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: notification_templates notif_templates_read_global; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_templates_read_global ON public.notification_templates FOR SELECT TO authenticated USING (((deleted_at IS NULL) AND (((tenant_id IS NULL) AND (is_default = true)) OR (owner_id = auth.uid()) OR public.is_tenant_member(tenant_id)))); + + +-- +-- Name: notification_templates notif_templates_write_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_templates_write_owner ON public.notification_templates TO authenticated USING (((owner_id = auth.uid()) OR public.is_tenant_member(tenant_id))) WITH CHECK (((owner_id = auth.uid()) OR public.is_tenant_member(tenant_id))); + + +-- +-- Name: notification_channels; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_channels ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_channels notification_channels_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notification_channels_owner ON public.notification_channels USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: notification_logs; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_logs ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_preferences; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_preferences ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_queue; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_queue ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_schedules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_schedules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_templates; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_templates ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notifications; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notifications owner only; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "owner only" ON public.notifications USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: owner_users; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.owner_users ENABLE ROW LEVEL SECURITY; + +-- +-- Name: owner_users owner_users: user can read own links; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "owner_users: user can read own links" ON public.owner_users FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: patient_discounts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_discounts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_discounts patient_discounts: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "patient_discounts: owner full access" ON public.patient_discounts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_group_patient; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_group_patient ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_group_patient patient_group_patient_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_group_patient_owner_all ON public.patient_group_patient TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.patients p + WHERE ((p.id = patient_group_patient.patient_id) AND (p.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.patients p + WHERE ((p.id = patient_group_patient.patient_id) AND (p.owner_id = auth.uid()))))); + + +-- +-- Name: patient_group_patient patient_group_patient_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_group_patient_select ON public.patient_group_patient FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_group_patient patient_group_patient_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_group_patient_write ON public.patient_group_patient USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_groups; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_groups ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_groups patient_groups_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_groups_owner_all ON public.patient_groups TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_groups patient_groups_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_groups_select ON public.patient_groups FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_groups patient_groups_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_groups_write ON public.patient_groups USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_intake_requests; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_intake_requests ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_intake_requests patient_intake_requests_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_intake_requests_owner_all ON public.patient_intake_requests TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_intake_requests patient_intake_requests_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_intake_requests_select ON public.patient_intake_requests FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_intake_requests patient_intake_requests_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_intake_requests_write ON public.patient_intake_requests USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_invites; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_invites ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_invites patient_invites_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_invites_owner_all ON public.patient_invites TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_invites patient_invites_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_invites_select ON public.patient_invites FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_invites patient_invites_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_invites_write ON public.patient_invites USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_patient_tag; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_patient_tag ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_patient_tag patient_patient_tag_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_patient_tag_owner_all ON public.patient_patient_tag TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_patient_tag patient_patient_tag_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_patient_tag_select ON public.patient_patient_tag FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_patient_tag patient_patient_tag_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_patient_tag_write ON public.patient_patient_tag USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_tags; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_tags ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_tags patient_tags_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_tags_owner_all ON public.patient_tags TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_tags patient_tags_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_tags_select ON public.patient_tags FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_tags patient_tags_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_tags_write ON public.patient_tags USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patients; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patients ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patients patients_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_delete ON public.patients FOR DELETE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.delete'::text))); + + +-- +-- Name: patients patients_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_insert ON public.patients FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.create'::text))); + + +-- +-- Name: patients patients_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_owner_all ON public.patients TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patients patients_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_select ON public.patients FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patients patients_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_update ON public.patients FOR UPDATE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: payment_settings; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.payment_settings ENABLE ROW LEVEL SECURITY; + +-- +-- Name: payment_settings payment_settings: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "payment_settings: owner full access" ON public.payment_settings USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: plan_features; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.plan_features ENABLE ROW LEVEL SECURITY; + +-- +-- Name: plan_features plan_features_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plan_features_read_authenticated ON public.plan_features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plan_features plan_features_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plan_features_write_saas_admin ON public.plan_features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: plans; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.plans ENABLE ROW LEVEL SECURITY; + +-- +-- Name: plans plans_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plans_read_authenticated ON public.plans FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plans plans_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plans_write_saas_admin ON public.plans TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: professional_pricing; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.professional_pricing ENABLE ROW LEVEL SECURITY; + +-- +-- Name: professional_pricing professional_pricing: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "professional_pricing: owner full access" ON public.professional_pricing USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: profiles; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; + +-- +-- Name: profiles profiles_insert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_insert_own ON public.profiles FOR INSERT WITH CHECK ((id = auth.uid())); + + +-- +-- Name: profiles profiles_read_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_read_saas_admin ON public.profiles FOR SELECT USING (public.is_saas_admin()); + + +-- +-- Name: profiles profiles_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_select_own ON public.profiles FOR SELECT USING ((id = auth.uid())); + + +-- +-- Name: profiles profiles_update_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_update_own ON public.profiles FOR UPDATE USING ((id = auth.uid())) WITH CHECK ((id = auth.uid())); + + +-- +-- Name: login_carousel_slides public_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY public_read ON public.login_carousel_slides FOR SELECT USING ((ativo = true)); + + +-- +-- Name: features read features (auth); Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "read features (auth)" ON public.features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plan_features read plan_features (auth); Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "read plan_features (auth)" ON public.plan_features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plans read plans (auth); Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "read plans (auth)" ON public.plans FOR SELECT TO authenticated USING (true); + + +-- +-- Name: recurrence_exceptions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.recurrence_exceptions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: recurrence_exceptions recurrence_exceptions_tenant; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_exceptions_tenant ON public.recurrence_exceptions TO authenticated USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))) WITH CHECK ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: recurrence_rule_services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.recurrence_rule_services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: recurrence_rule_services recurrence_rule_services: clinic read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "recurrence_rule_services: clinic read" ON public.recurrence_rule_services FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND public.is_clinic_tenant(r.tenant_id) AND public.is_tenant_member(r.tenant_id) AND public.tenant_has_feature(r.tenant_id, 'agenda.view'::text))))); + + +-- +-- Name: recurrence_rule_services recurrence_rule_services: clinic write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "recurrence_rule_services: clinic write" ON public.recurrence_rule_services USING ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND public.is_clinic_tenant(r.tenant_id) AND public.is_tenant_member(r.tenant_id) AND public.tenant_has_feature(r.tenant_id, 'agenda.edit'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND public.is_clinic_tenant(r.tenant_id) AND public.is_tenant_member(r.tenant_id) AND public.tenant_has_feature(r.tenant_id, 'agenda.edit'::text))))); + + +-- +-- Name: recurrence_rule_services recurrence_rule_services: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "recurrence_rule_services: owner full access" ON public.recurrence_rule_services TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND (r.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND (r.owner_id = auth.uid()))))); + + +-- +-- Name: recurrence_rules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.recurrence_rules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: recurrence_rules recurrence_rules_clinic_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_rules_clinic_read ON public.recurrence_rules FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: recurrence_rules recurrence_rules_clinic_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_rules_clinic_write ON public.recurrence_rules USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: recurrence_rules recurrence_rules_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_rules_owner ON public.recurrence_rules TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: subscription_intents_legacy saas_admin can read subscription_intents; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "saas_admin can read subscription_intents" ON public.subscription_intents_legacy FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins a + WHERE (a.user_id = auth.uid())))); + + +-- +-- Name: subscription_intents_legacy saas_admin can update subscription_intents; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "saas_admin can update subscription_intents" ON public.subscription_intents_legacy FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins a + WHERE (a.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins a + WHERE (a.user_id = auth.uid())))); + + +-- +-- Name: login_carousel_slides saas_admin_full; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY saas_admin_full ON public.login_carousel_slides USING ((EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text))))); + + +-- +-- Name: saas_docs saas_admin_full_access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY saas_admin_full_access ON public.saas_docs TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: saas_admins; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_admins ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_admins saas_admins_select_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY saas_admins_select_self ON public.saas_admins FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: saas_doc_votos; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_doc_votos ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_docs; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_docs ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_faq; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_faq ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_faq_itens; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_faq_itens ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_bloqueios select own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "select own" ON public.agenda_bloqueios FOR SELECT USING ((owner_id = auth.uid())); + + +-- +-- Name: twilio_subaccount_usage service_role_manage_usage; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY service_role_manage_usage ON public.twilio_subaccount_usage USING ((auth.role() = 'service_role'::text)); + + +-- +-- Name: services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: services services: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "services: owner full access" ON public.services USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: subscription_events; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.subscription_events ENABLE ROW LEVEL SECURITY; + +-- +-- Name: subscription_events subscription_events_read_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_events_read_saas ON public.subscription_events FOR SELECT USING (public.is_saas_admin()); + + +-- +-- Name: subscription_events subscription_events_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_events_write_saas ON public.subscription_events FOR INSERT WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: subscription_intents_legacy subscription_intents_insert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_intents_insert_own ON public.subscription_intents_legacy FOR INSERT TO authenticated WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: subscription_intents_legacy; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.subscription_intents_legacy ENABLE ROW LEVEL SECURITY; + +-- +-- Name: subscription_intents_legacy subscription_intents_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_intents_select_own ON public.subscription_intents_legacy FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: subscriptions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.subscriptions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: subscriptions subscriptions read own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "subscriptions read own" ON public.subscriptions FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: subscriptions subscriptions: read if linked owner_users; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "subscriptions: read if linked owner_users" ON public.subscriptions FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.owner_users ou + WHERE ((ou.owner_id = subscriptions.user_id) AND (ou.user_id = auth.uid()))))); + + +-- +-- Name: subscriptions subscriptions_insert_own_personal; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_insert_own_personal ON public.subscriptions FOR INSERT TO authenticated WITH CHECK (((user_id = auth.uid()) AND (tenant_id IS NULL))); + + +-- +-- Name: subscriptions subscriptions_no_direct_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_no_direct_update ON public.subscriptions FOR UPDATE TO authenticated USING (false) WITH CHECK (false); + + +-- +-- Name: subscriptions subscriptions_read_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_read_own ON public.subscriptions FOR SELECT TO authenticated USING (((user_id = auth.uid()) OR public.is_saas_admin())); + + +-- +-- Name: subscriptions subscriptions_select_for_tenant_members; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_select_for_tenant_members ON public.subscriptions FOR SELECT TO authenticated USING (((tenant_id IS NOT NULL) AND (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = subscriptions.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: subscriptions subscriptions_select_own_personal; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_select_own_personal ON public.subscriptions FOR SELECT TO authenticated USING (((user_id = auth.uid()) AND (tenant_id IS NULL))); + + +-- +-- Name: subscriptions subscriptions_update_only_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_update_only_saas_admin ON public.subscriptions FOR UPDATE TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: support_sessions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.support_sessions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: support_sessions support_sessions_saas_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY support_sessions_saas_delete ON public.support_sessions FOR DELETE USING (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text)))))); + + +-- +-- Name: support_sessions support_sessions_saas_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY support_sessions_saas_insert ON public.support_sessions FOR INSERT WITH CHECK (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text)))))); + + +-- +-- Name: support_sessions support_sessions_saas_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY support_sessions_saas_select ON public.support_sessions FOR SELECT USING (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text)))))); + + +-- +-- Name: email_templates_tenant tenant manages own overrides; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "tenant manages own overrides" ON public.email_templates_tenant USING ((tenant_id = auth.uid())) WITH CHECK ((tenant_id = auth.uid())); + + +-- +-- Name: email_layout_config tenant owns email layout config; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "tenant owns email layout config" ON public.email_layout_config USING ((tenant_id = auth.uid())) WITH CHECK ((tenant_id = auth.uid())); + + +-- +-- Name: tenant_members; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.tenant_members ENABLE ROW LEVEL SECURITY; + +-- +-- Name: tenant_members tenant_members_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_members_write_saas ON public.tenant_members TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: tenant_modules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.tenant_modules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: tenant_modules tenant_modules_read_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_modules_read_own ON public.tenant_modules FOR SELECT TO authenticated USING (((owner_id = auth.uid()) OR public.is_saas_admin())); + + +-- +-- Name: tenant_modules tenant_modules_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_modules_write_saas ON public.tenant_modules TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: twilio_subaccount_usage tenant_select_own_usage; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_select_own_usage ON public.twilio_subaccount_usage FOR SELECT USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: tenants; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.tenants ENABLE ROW LEVEL SECURITY; + +-- +-- Name: tenants tenants_read_members; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenants_read_members ON public.tenants FOR SELECT TO authenticated USING ((public.is_saas_admin() OR (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = tenants.id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: tenants tenants_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenants_write_saas ON public.tenants TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: therapist_payout_records; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.therapist_payout_records ENABLE ROW LEVEL SECURITY; + +-- +-- Name: therapist_payout_records therapist_payout_records_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payout_records_self ON public.therapist_payout_records USING ((EXISTS ( SELECT 1 + FROM public.therapist_payouts tp + WHERE ((tp.id = therapist_payout_records.payout_id) AND (tp.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.therapist_payouts tp + WHERE ((tp.id = therapist_payout_records.payout_id) AND (tp.owner_id = auth.uid()))))); + + +-- +-- Name: therapist_payout_records therapist_payout_records_tenant_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payout_records_tenant_admin ON public.therapist_payout_records FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.therapist_payouts tp + WHERE ((tp.id = therapist_payout_records.payout_id) AND public.is_tenant_admin(tp.tenant_id))))); + + +-- +-- Name: therapist_payouts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.therapist_payouts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: therapist_payouts therapist_payouts_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payouts_self ON public.therapist_payouts USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: therapist_payouts therapist_payouts_tenant_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payouts_tenant_admin ON public.therapist_payouts FOR SELECT USING (((tenant_id IS NOT NULL) AND public.is_tenant_admin(tenant_id))); + + +-- +-- Name: tenant_members tm_select_admin_all_members; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tm_select_admin_all_members ON public.tenant_members FOR SELECT TO authenticated USING (public.is_tenant_admin(tenant_id)); + + +-- +-- Name: tenant_members tm_select_own_membership; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tm_select_own_membership ON public.tenant_members FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: twilio_subaccount_usage; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.twilio_subaccount_usage ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_bloqueios update own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "update own" ON public.agenda_bloqueios FOR UPDATE USING ((owner_id = auth.uid())); + + +-- +-- Name: user_settings; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.user_settings ENABLE ROW LEVEL SECURITY; + +-- +-- Name: user_settings user_settings_insert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY user_settings_insert_own ON public.user_settings FOR INSERT WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: user_settings user_settings_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY user_settings_select_own ON public.user_settings FOR SELECT USING ((user_id = auth.uid())); + + +-- +-- Name: user_settings user_settings_update_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY user_settings_update_own ON public.user_settings FOR UPDATE USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: saas_docs users_read_usuario_docs; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY users_read_usuario_docs ON public.saas_docs FOR SELECT TO authenticated USING (((ativo = true) AND (tipo_acesso = 'usuario'::text))); + + +-- +-- Name: saas_doc_votos votos_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY votos_select_own ON public.saas_doc_votos FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: saas_doc_votos votos_upsert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY votos_upsert_own ON public.saas_doc_votos TO authenticated USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: messages; Type: ROW SECURITY; Schema: realtime; Owner: - +-- + +ALTER TABLE realtime.messages ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects Allow authenticated updates; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "Allow authenticated updates" ON storage.objects FOR UPDATE TO authenticated USING ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); + + +-- +-- Name: objects Allow authenticated uploads; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "Allow authenticated uploads" ON storage.objects FOR INSERT TO authenticated WITH CHECK ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); + + +-- +-- Name: objects Public read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "Public read" ON storage.objects FOR SELECT USING ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); + + +-- +-- Name: objects agendador_storage_owner_delete; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_owner_delete ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects agendador_storage_owner_insert; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_owner_insert ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects agendador_storage_owner_update; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_owner_update ON storage.objects FOR UPDATE TO authenticated USING (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects agendador_storage_public_read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_public_read ON storage.objects FOR SELECT USING ((bucket_id = 'agendador'::text)); + + +-- +-- Name: objects avatars authenticated upload; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars authenticated upload" ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects avatars owner delete; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars owner delete" ON storage.objects FOR DELETE USING (((bucket_id = 'avatars'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects avatars owner update; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars owner update" ON storage.objects FOR UPDATE USING (((bucket_id = 'avatars'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects avatars public read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars public read" ON storage.objects FOR SELECT USING ((bucket_id = 'avatars'::text)); + + +-- +-- Name: objects avatars_delete_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_delete_own ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_delete_own_folder; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_delete_own_folder ON storage.objects FOR DELETE USING (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); + + +-- +-- Name: objects avatars_insert_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_insert_own ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_insert_own_folder; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_insert_own_folder ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); + + +-- +-- Name: objects avatars_read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_read ON storage.objects FOR SELECT USING ((bucket_id = 'avatars'::text)); + + +-- +-- Name: objects avatars_select_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_select_own ON storage.objects FOR SELECT TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_update_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_update_own ON storage.objects FOR UPDATE TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))) WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_update_own_folder; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_update_own_folder ON storage.objects FOR UPDATE USING (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))) WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); + + +-- +-- Name: buckets; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.buckets ENABLE ROW LEVEL SECURITY; + +-- +-- Name: buckets_analytics; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.buckets_analytics ENABLE ROW LEVEL SECURITY; + +-- +-- Name: buckets_vectors; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.buckets_vectors ENABLE ROW LEVEL SECURITY; + +-- +-- Name: iceberg_namespaces; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.iceberg_namespaces ENABLE ROW LEVEL SECURITY; + +-- +-- Name: iceberg_tables; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.iceberg_tables ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects intake_read_anon; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_read_anon ON storage.objects FOR SELECT TO anon USING (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: objects intake_read_public; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_read_public ON storage.objects FOR SELECT USING (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: objects intake_upload_anon; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_upload_anon ON storage.objects FOR INSERT TO anon WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: objects intake_upload_public; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_upload_public ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: migrations; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.migrations ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects public_read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY public_read ON storage.objects FOR SELECT USING ((bucket_id = 'saas-docs'::text)); + + +-- +-- Name: s3_multipart_uploads; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.s3_multipart_uploads ENABLE ROW LEVEL SECURITY; + +-- +-- Name: s3_multipart_uploads_parts; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.s3_multipart_uploads_parts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects saas_admin_delete; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY saas_admin_delete ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'saas-docs'::text) AND (EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid()))))); + + +-- +-- Name: objects saas_admin_upload; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY saas_admin_upload ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'saas-docs'::text) AND (EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid()))))); + + +-- +-- Name: vector_indexes; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.vector_indexes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: supabase_realtime; Type: PUBLICATION; Schema: -; Owner: - +-- + +CREATE PUBLICATION supabase_realtime WITH (publish = 'insert, update, delete, truncate'); + + +-- +-- Name: supabase_realtime_messages_publication; Type: PUBLICATION; Schema: -; Owner: - +-- + +CREATE PUBLICATION supabase_realtime_messages_publication WITH (publish = 'insert, update, delete, truncate'); + + +-- +-- Name: supabase_realtime notifications; Type: PUBLICATION TABLE; Schema: public; Owner: - +-- + +ALTER PUBLICATION supabase_realtime ADD TABLE ONLY public.notifications; + + +-- +-- Name: supabase_realtime_messages_publication messages; Type: PUBLICATION TABLE; Schema: realtime; Owner: - +-- + +ALTER PUBLICATION supabase_realtime_messages_publication ADD TABLE ONLY realtime.messages; + + +-- +-- Name: issue_graphql_placeholder; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_graphql_placeholder ON sql_drop + WHEN TAG IN ('DROP EXTENSION') + EXECUTE FUNCTION extensions.set_graphql_placeholder(); + + +-- +-- Name: issue_pg_cron_access; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_pg_cron_access ON ddl_command_end + WHEN TAG IN ('CREATE EXTENSION') + EXECUTE FUNCTION extensions.grant_pg_cron_access(); + + +-- +-- Name: issue_pg_graphql_access; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_pg_graphql_access ON ddl_command_end + WHEN TAG IN ('CREATE FUNCTION') + EXECUTE FUNCTION extensions.grant_pg_graphql_access(); + + +-- +-- Name: issue_pg_net_access; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_pg_net_access ON ddl_command_end + WHEN TAG IN ('CREATE EXTENSION') + EXECUTE FUNCTION extensions.grant_pg_net_access(); + + +-- +-- Name: pgrst_ddl_watch; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER pgrst_ddl_watch ON ddl_command_end + EXECUTE FUNCTION extensions.pgrst_ddl_watch(); + + +-- +-- Name: pgrst_drop_watch; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER pgrst_drop_watch ON sql_drop + EXECUTE FUNCTION extensions.pgrst_drop_watch(); + + +-- +-- PostgreSQL database dump complete +-- + +\unrestrict acnCnMZQsJO473l3dvqVdklBN15FPebE5Zi8Gz0mj5MpfIJdgRLhsX941fb70hT + diff --git a/database-novo/backups/2026-03-29/data.sql b/database-novo/backups/2026-03-29/data.sql new file mode 100644 index 0000000..cbe7d45 --- /dev/null +++ b/database-novo/backups/2026-03-29/data.sql @@ -0,0 +1,1799 @@ +-- +-- PostgreSQL database dump +-- + +\restrict XPa1LcYWz3K821NmGm5bTrtk26L4QuNyoejVScPd40dVb1JeGkFrWmrMc2TIigR + +-- Dumped from database version 17.6 +-- Dumped by pg_dump version 17.6 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET transaction_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Data for Name: audit_log_entries; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.audit_log_entries (instance_id, id, payload, created_at, ip_address) FROM stdin; +00000000-0000-0000-0000-000000000000 f3a9bb5c-a48b-48da-83b0-5e5f71cc1278 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-23 10:58:23.661651+00 +00000000-0000-0000-0000-000000000000 015ae6c3-f000-49f5-a05a-7e43256a5ead {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-23 10:59:13.173068+00 +00000000-0000-0000-0000-000000000000 2222f6e6-4a78-4c44-88cf-65b677a0bdb6 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-23 10:59:17.817071+00 +00000000-0000-0000-0000-000000000000 12c8d5f5-46f0-4e67-96d2-7722167c7b3c {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-23 11:30:05.361177+00 +00000000-0000-0000-0000-000000000000 4aa98446-9aa8-4442-985a-aae3fbd28cd2 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-23 11:30:08.832248+00 +00000000-0000-0000-0000-000000000000 4e5e241b-0ba7-4772-a492-5dce48836688 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 12:28:24.672784+00 +00000000-0000-0000-0000-000000000000 12c376a9-7e40-4701-b163-173b66bbcc05 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 12:28:24.674266+00 +00000000-0000-0000-0000-000000000000 54e723ec-3977-4d92-8595-ce7ab53dbd86 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 13:30:01.59908+00 +00000000-0000-0000-0000-000000000000 de3c62a2-5771-4cb3-8bc5-a0f2c5cdf7bd {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 13:30:01.600518+00 +00000000-0000-0000-0000-000000000000 970016c6-0f0b-41a0-b4eb-5da3a5064f8b {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 14:30:14.581786+00 +00000000-0000-0000-0000-000000000000 a951c68b-1c3f-46ca-82d1-aead121e268f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 14:30:14.582962+00 +00000000-0000-0000-0000-000000000000 34299538-d49f-41a9-b0cc-2abbedbae26e {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 15:28:29.566693+00 +00000000-0000-0000-0000-000000000000 276722cb-b0ee-46a3-93b9-f3d966f16c26 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 15:28:29.56777+00 +00000000-0000-0000-0000-000000000000 bdc467e8-37fa-488e-b21d-e12bb16d5225 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 16:26:59.228619+00 +00000000-0000-0000-0000-000000000000 f00324fe-263c-42fd-b885-1309db0c76d0 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 16:26:59.230039+00 +00000000-0000-0000-0000-000000000000 4e468b7b-c2c1-479f-a6f5-9d7aea99aaa7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 17:25:28.767876+00 +00000000-0000-0000-0000-000000000000 b1e6f592-6413-44bd-82ce-eb4ae4d3873b {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 17:25:28.770199+00 +00000000-0000-0000-0000-000000000000 8ab04c44-66b6-469e-b869-a69b35c1d35f {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 18:25:01.605164+00 +00000000-0000-0000-0000-000000000000 00c18ea4-a6bb-429b-9d0d-7dbddd7b3d94 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 18:25:01.615437+00 +00000000-0000-0000-0000-000000000000 53a407ef-2f1c-4bed-838a-997812acb065 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 19:25:02.095787+00 +00000000-0000-0000-0000-000000000000 2c310c64-0933-439d-81f9-74fb6ffbf68d {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 19:25:02.097455+00 +00000000-0000-0000-0000-000000000000 2720bc5f-9871-4c13-a725-b0c640a04698 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 20:23:51.534132+00 +00000000-0000-0000-0000-000000000000 19fc4616-6242-4e56-b340-48467d9be144 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 20:23:51.535311+00 +00000000-0000-0000-0000-000000000000 1eed06b7-6fa2-4d66-ac2c-3d3bcb5b0592 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 21:22:21.898206+00 +00000000-0000-0000-0000-000000000000 920acd65-8146-4b35-b6d5-5281f4ea1ed1 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 21:22:21.900821+00 +00000000-0000-0000-0000-000000000000 73a31066-566b-42a1-a281-0ec8fc71a2f5 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 22:25:02.312362+00 +00000000-0000-0000-0000-000000000000 44c5a6ee-6bd3-43f1-9a3f-7c13ef1a83b5 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 22:25:02.314918+00 +00000000-0000-0000-0000-000000000000 a28430ae-7a7d-4ce8-9ae3-c83f7b3f8f5d {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 23:24:14.39221+00 +00000000-0000-0000-0000-000000000000 b4f2883f-f222-4862-8cdf-135563aaa257 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 23:24:14.393364+00 +00000000-0000-0000-0000-000000000000 2d425bb6-1ab6-4d54-9502-1132ff7fd14f {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 10:17:05.380495+00 +00000000-0000-0000-0000-000000000000 48dac6fd-0dce-42e1-ba70-3b235a0e1161 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 10:17:05.385002+00 +00000000-0000-0000-0000-000000000000 bb85f6c0-d96a-4eb6-a8b7-5170b4ea74a7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 11:17:55.391428+00 +00000000-0000-0000-0000-000000000000 b063d335-22f8-470b-b0ed-df6f9d68a7ba {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 11:17:55.392611+00 +00000000-0000-0000-0000-000000000000 29529cd0-0680-4d1d-a5ff-e43736f70555 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 12:17:55.375751+00 +00000000-0000-0000-0000-000000000000 f1dc7c02-dd21-4882-83e1-5e83d60156f2 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 12:17:55.378137+00 +00000000-0000-0000-0000-000000000000 e0ce9d96-5ed7-4342-bcb0-13e420b91523 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 19:20:20.869716+00 +00000000-0000-0000-0000-000000000000 0a93156f-a22c-4d18-81f2-54367a3745ba {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 19:20:20.936303+00 +00000000-0000-0000-0000-000000000000 1ddab552-8e6f-4ea6-8cc1-f7d00b92abc7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 20:20:04.2956+00 +00000000-0000-0000-0000-000000000000 bf82dc60-43b8-4d3c-8cb1-01bc4fd4a341 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 20:20:04.297562+00 +00000000-0000-0000-0000-000000000000 c5a9e22f-feb2-4383-b4cd-7910f2266978 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 21:18:48.738276+00 +00000000-0000-0000-0000-000000000000 fb58f5f0-7279-41de-98aa-20fa277e3d84 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 21:18:48.74055+00 +00000000-0000-0000-0000-000000000000 0dda5ef1-f572-4379-b734-f65b9e044830 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 22:17:29.961966+00 +00000000-0000-0000-0000-000000000000 8762f6f6-d22e-4c60-b2a3-c5c8bf99d548 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 22:17:29.965545+00 +00000000-0000-0000-0000-000000000000 51cea4a0-f2cc-4e9b-8b1a-8b2c9cdf8b47 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-24 23:01:52.959606+00 +00000000-0000-0000-0000-000000000000 61e26d26-ce04-490d-802d-4f67cc379fda {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-24 23:01:56.400534+00 +00000000-0000-0000-0000-000000000000 8c73c9b9-2da1-42df-b1a6-f92b98ab9300 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-24 23:07:05.837147+00 +00000000-0000-0000-0000-000000000000 05bf6f2d-60c0-4e5a-8ec1-6f2a7403d0c2 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-24 23:07:09.755293+00 +00000000-0000-0000-0000-000000000000 8d63e967-c616-42b0-b1c5-c8f54f54beaa {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:01:49.505958+00 +00000000-0000-0000-0000-000000000000 cdde610b-ac0f-4d61-91bd-7af34f23acf0 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:01:57.651986+00 +00000000-0000-0000-0000-000000000000 ef89c4e5-e092-4c12-a3b5-608b0e79bd65 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:03:22.169295+00 +00000000-0000-0000-0000-000000000000 ba251517-d704-4aa9-9992-16a8590582e0 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:03:28.975189+00 +00000000-0000-0000-0000-000000000000 b9d81262-4370-4712-8db7-81d51e27fcc8 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:06:51.450521+00 +00000000-0000-0000-0000-000000000000 26ca7ff5-87d3-48a2-ad9f-4ac67a216da8 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:06:55.726041+00 +00000000-0000-0000-0000-000000000000 f8c943c2-6dc0-42f6-87c9-adffc9a336c5 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:07:45.021178+00 +00000000-0000-0000-0000-000000000000 bba67a2b-049e-4d1b-b6a7-286d2b9c45f7 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:07:48.235967+00 +00000000-0000-0000-0000-000000000000 151834d9-2232-43c8-a0e6-a39c6617246e {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:11:54.788614+00 +00000000-0000-0000-0000-000000000000 e903f726-e5fe-4e06-9051-f36dacc780ce {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:12:00.861214+00 +00000000-0000-0000-0000-000000000000 e300a60b-b944-4c72-bb52-9ff7dff08dd2 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:24:51.770222+00 +00000000-0000-0000-0000-000000000000 4580e71b-e19b-4a74-ad85-f08f74791e53 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:24:57.09306+00 +00000000-0000-0000-0000-000000000000 a12cb7e1-768c-4ca2-96de-d68ce160971a {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 01:25:50.776943+00 +00000000-0000-0000-0000-000000000000 ce559936-37f2-411f-ba99-401e6ac4d94f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 01:25:50.778414+00 +00000000-0000-0000-0000-000000000000 46ebfc8e-e65a-4118-8a42-1d873b1436cc {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 02:25:50.797262+00 +00000000-0000-0000-0000-000000000000 d09f6b35-52d2-4f5e-80f5-0b188fef98da {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 02:25:50.798466+00 +00000000-0000-0000-0000-000000000000 7e7a2284-bb63-4b8d-ab19-186394b12f8f {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:13:42.627459+00 +00000000-0000-0000-0000-000000000000 cd92d32d-ac1a-4681-b957-11d8d4bd1a09 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:13:46.932353+00 +00000000-0000-0000-0000-000000000000 54013ee2-2dc3-4e36-8a35-09eb748ccba4 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:20:03.154923+00 +00000000-0000-0000-0000-000000000000 598cd35b-8c6e-4d41-9ec6-c25dcc4c6715 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:20:07.255873+00 +00000000-0000-0000-0000-000000000000 520167bb-ec1c-4e67-8c07-8d2ba457ec3c {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:24:18.554167+00 +00000000-0000-0000-0000-000000000000 4dc11041-8b36-4703-b5c5-2411a13b96da {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:24:24.699031+00 +00000000-0000-0000-0000-000000000000 9cfd2eae-f911-4dc7-9c5d-78499fd608f1 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:25:07.736803+00 +00000000-0000-0000-0000-000000000000 258e6ed2-5959-4893-8433-caa4b9ccb106 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:29:21.141199+00 +00000000-0000-0000-0000-000000000000 c76a59d2-0829-456a-957b-c623b14ae03e {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 10:28:55.843362+00 +00000000-0000-0000-0000-000000000000 f96b1f88-1f48-4a96-bbfc-b8c6527a31df {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 10:29:14.90848+00 +00000000-0000-0000-0000-000000000000 65f06c2b-5ee5-4bf1-a6ff-21dbe657e0b4 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 10:29:19.403154+00 +00000000-0000-0000-0000-000000000000 9e2290b7-dcfa-4bf5-b244-1f91011bfbe3 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 11:27:32.781897+00 +00000000-0000-0000-0000-000000000000 9cc32bd5-ae94-4014-90cb-b468e133cc10 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 11:27:32.783409+00 +00000000-0000-0000-0000-000000000000 2e244065-7456-4aba-a841-0920875e7f1b {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 12:16:54.862937+00 +00000000-0000-0000-0000-000000000000 f7cf0072-bda9-4b59-9532-7354a79c0306 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 12:17:01.538685+00 +00000000-0000-0000-0000-000000000000 5d8407ed-be29-482b-8877-42cf6e78f6c3 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:05:17.486922+00 +00000000-0000-0000-0000-000000000000 a8a0c3ef-6a10-4c8f-b53c-830ee3c2cfaf {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:05:21.871823+00 +00000000-0000-0000-0000-000000000000 2dfcfe2d-3770-42a6-8ceb-f16bd2e52151 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:05:31.190404+00 +00000000-0000-0000-0000-000000000000 c9e57ce5-33e2-4e79-b0b6-204dc22e4c8f {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:05:54.861933+00 +00000000-0000-0000-0000-000000000000 f62ee264-f036-46b9-9bdc-8bababf26492 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:06:00.886992+00 +00000000-0000-0000-0000-000000000000 ace96eee-7f68-4139-9f73-c3aa21d5e1d7 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:29:24.160199+00 +00000000-0000-0000-0000-000000000000 75b60071-d53f-490c-be4c-69f3c6f08da4 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:29:33.800526+00 +00000000-0000-0000-0000-000000000000 4a736318-9980-48ed-b13b-b47e38be5d41 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:30:03.032141+00 +00000000-0000-0000-0000-000000000000 a61bb073-c754-406a-b123-0b40c22915a3 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:37:21.932796+00 +00000000-0000-0000-0000-000000000000 80fd895f-8963-40f4-a9bb-4c59d95e964e {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 14:40:14.190475+00 +00000000-0000-0000-0000-000000000000 d2392bf0-18e8-45af-98f0-1d70e7cbe6f0 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 14:40:14.193129+00 +00000000-0000-0000-0000-000000000000 3fcfde42-9951-4df4-aae3-4abf256ed25d {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 15:38:52.104451+00 +00000000-0000-0000-0000-000000000000 e4c92ebd-11df-4e4c-82a1-7af12b797164 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 15:38:52.121069+00 +00000000-0000-0000-0000-000000000000 e6d4fb6d-fca8-4ee4-992a-59661b86b3d7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 16:39:45.201853+00 +00000000-0000-0000-0000-000000000000 f2606751-7081-4463-8c3f-985860702b7a {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 16:39:45.203238+00 +00000000-0000-0000-0000-000000000000 0db3c68a-49b6-42e3-8df0-a3dd1daa805d {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 17:39:45.220264+00 +00000000-0000-0000-0000-000000000000 20d02e8a-bdae-4c3d-9629-e0c1bc5e9d0a {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 17:39:45.221411+00 +00000000-0000-0000-0000-000000000000 58afa2b2-3d21-4d56-9b99-f96ec194170a {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 18:32:34.529973+00 +00000000-0000-0000-0000-000000000000 4afd9a79-0d8e-462e-8439-8c86ae984aa8 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 18:32:38.624903+00 +00000000-0000-0000-0000-000000000000 ee9fb7dc-392e-4fb2-9ee1-5d6fd7f632a8 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 18:35:17.643808+00 +00000000-0000-0000-0000-000000000000 aa72f08d-bdd6-400e-8798-3ad17598c5a8 {"action":"login","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 18:35:21.349866+00 +00000000-0000-0000-0000-000000000000 7ffd90be-aa82-4fa0-9a6a-6015fccedf99 {"action":"logout","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 18:35:43.306104+00 +00000000-0000-0000-0000-000000000000 ce068f66-0a8a-47a0-9fb1-2c3f727409f3 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 18:35:46.92007+00 +00000000-0000-0000-0000-000000000000 76e76323-7144-4ab0-9350-b1b009a5688a {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 19:33:46.819266+00 +00000000-0000-0000-0000-000000000000 f4ad35f0-f7f4-45c7-9e6f-b76d7075bcbd {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 19:33:46.822119+00 +00000000-0000-0000-0000-000000000000 d8d7a18a-1cd7-4694-a709-1c25bafd47cd {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 20:32:08.57133+00 +00000000-0000-0000-0000-000000000000 c0efd1eb-d3a4-4588-a6e4-6c6316afcbfa {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 20:32:08.573939+00 +00000000-0000-0000-0000-000000000000 663aa064-f89b-4629-9853-6921cec5f658 {"action":"login","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 10:13:28.062141+00 +00000000-0000-0000-0000-000000000000 bada5aa6-4962-42ab-90ac-f10ec7ada174 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 11:12:40.283978+00 +00000000-0000-0000-0000-000000000000 0284d0a9-1045-4d63-84c0-199f8fd57463 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 11:12:40.285424+00 +00000000-0000-0000-0000-000000000000 7fe49307-b9fc-4045-86e6-77ec5b6724d4 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 12:12:40.261029+00 +00000000-0000-0000-0000-000000000000 1cf673eb-6fed-4b60-a756-e5d9d8164ab2 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 12:12:40.262344+00 +00000000-0000-0000-0000-000000000000 89931cd1-3362-4153-9e26-c3a3450315bf {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 15:51:12.813868+00 +00000000-0000-0000-0000-000000000000 d4ae8391-85ed-4d02-be33-fc9895d10993 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 15:51:12.815012+00 +00000000-0000-0000-0000-000000000000 26bfda48-9d72-443a-8324-1944ebe4a701 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 16:49:43.315+00 +00000000-0000-0000-0000-000000000000 0f7adbbc-aaa1-4f3a-b04c-a6845b9e0ca6 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 16:49:43.323835+00 +00000000-0000-0000-0000-000000000000 d9baf33f-c034-49ad-8f16-902bea98ec5e {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 17:48:58.248187+00 +00000000-0000-0000-0000-000000000000 ca2f00e5-300f-4a17-b87e-ccbec14f2c7e {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 17:48:58.250458+00 +00000000-0000-0000-0000-000000000000 fa02ec44-7c86-418c-99c7-de2622acfb31 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 18:49:41.829309+00 +00000000-0000-0000-0000-000000000000 136da06f-a9bb-4523-bb16-47717edd8eb5 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 18:49:41.831653+00 +00000000-0000-0000-0000-000000000000 e8a3a012-3e8d-499a-aa7a-4cbca7999463 {"action":"logout","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:46:23.049908+00 +00000000-0000-0000-0000-000000000000 c31208e4-7589-44be-9f3d-5f3f201a4df6 {"action":"login","actor_id":"aaaaaaaa-0008-0008-0008-000000000008","actor_username":"editor@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:46:29.068361+00 +00000000-0000-0000-0000-000000000000 81031375-449a-4a69-b4dc-2ee999a654b2 {"action":"logout","actor_id":"aaaaaaaa-0008-0008-0008-000000000008","actor_username":"editor@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:46:38.212531+00 +00000000-0000-0000-0000-000000000000 5bcaaf60-9f35-4d7b-a54b-b491ac07ed15 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:46:43.300041+00 +00000000-0000-0000-0000-000000000000 6e71806d-9b38-4aec-af46-15f9aa5b5375 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:46:49.885518+00 +00000000-0000-0000-0000-000000000000 ab45715c-877c-4cea-9249-5e02e163d77a {"action":"user_signedup","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"team","traits":{"provider":"email"}} 2026-03-26 19:47:30.494975+00 +00000000-0000-0000-0000-000000000000 98634ad1-21f0-448b-81a4-6686bbfc9932 {"action":"login","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:47:30.515066+00 +00000000-0000-0000-0000-000000000000 e0613923-7e54-45b6-9861-14fd4a03f8e7 {"action":"logout","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:48:35.667525+00 +00000000-0000-0000-0000-000000000000 11139b73-efc6-43e4-b228-2ea2c4da475b {"action":"login","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:48:44.25225+00 +00000000-0000-0000-0000-000000000000 cf97f01d-8b2d-45a2-a571-d5ae4adce236 {"action":"logout","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:48:53.19869+00 +00000000-0000-0000-0000-000000000000 51c6d2fc-9a32-4449-91b0-707a74077e92 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:51:35.028402+00 +00000000-0000-0000-0000-000000000000 6f152cff-1e9a-4f0d-b2aa-d556ed20ad19 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:53:14.391225+00 +00000000-0000-0000-0000-000000000000 4dbde0d4-78e6-408f-84dc-ab6c246168b0 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:53:17.60162+00 +00000000-0000-0000-0000-000000000000 719ef7ce-f477-4de3-85d4-cb39b346bbf1 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:56:14.350054+00 +00000000-0000-0000-0000-000000000000 cd55882a-9e54-4730-b063-c9b4887c0e0a {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:56:18.624252+00 +00000000-0000-0000-0000-000000000000 232c3b43-f160-40a1-be37-2da29fc997bd {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 20:57:32.788753+00 +00000000-0000-0000-0000-000000000000 8d6852ac-b3b5-4543-bfed-e2369a73f211 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 20:57:32.794287+00 +00000000-0000-0000-0000-000000000000 f860fa47-b12d-4463-ab9a-46cbf022837e {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:19:25.905866+00 +00000000-0000-0000-0000-000000000000 449ddd1b-11e8-4b1e-bdc0-abc398dca77d {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:19:52.487097+00 +00000000-0000-0000-0000-000000000000 61bf81aa-1454-4723-af1a-fb00b2203c72 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:19:54.580581+00 +00000000-0000-0000-0000-000000000000 74a985d1-14b7-40c7-92db-444d0ba62ffb {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:31:18.590523+00 +00000000-0000-0000-0000-000000000000 17d9c260-731d-4fa7-9d69-9c1241c30bd9 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:31:20.209708+00 +00000000-0000-0000-0000-000000000000 6ec569b6-f0a1-4a0c-8c81-54fc82f6be93 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:31:39.762447+00 +00000000-0000-0000-0000-000000000000 75c4095e-994d-4ce9-9d77-59c514d88653 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:32:10.888074+00 +00000000-0000-0000-0000-000000000000 8ec7110e-415d-4627-935f-f0327b1dd58e {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:32:12.867178+00 +00000000-0000-0000-0000-000000000000 8350236f-d596-4d1e-8925-ae917e599ad4 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:33:05.320834+00 +00000000-0000-0000-0000-000000000000 25a7c28b-8ee2-4b94-bf6f-d046387fcde2 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:33:25.418988+00 +00000000-0000-0000-0000-000000000000 ce951d2d-488f-4757-92d0-3460f5bde396 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:40:53.407118+00 +00000000-0000-0000-0000-000000000000 39056ca1-fbd1-4e85-88ab-355c01a969ab {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:40:55.222474+00 +00000000-0000-0000-0000-000000000000 9591ecbc-aad0-4d5a-9076-413ed1e585ff {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:42:37.889869+00 +00000000-0000-0000-0000-000000000000 5777adf8-b362-496b-899f-5b05c0d93721 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:42:53.50928+00 +00000000-0000-0000-0000-000000000000 6a1e5a17-c7ea-453c-8626-d5022145ddf7 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:46:43.464106+00 +00000000-0000-0000-0000-000000000000 d28f02fc-a6ff-4aac-b49a-4fda2583bf17 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:46:46.362488+00 +00000000-0000-0000-0000-000000000000 b0e12d07-d349-4e14-8a3a-6ad42e747b21 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:52:49.203802+00 +00000000-0000-0000-0000-000000000000 c95a6365-3083-4f4f-a910-48f1405b272a {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:52:50.894594+00 +00000000-0000-0000-0000-000000000000 a8da7e38-f276-4aac-9a34-704af25a8716 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:53:20.228765+00 +00000000-0000-0000-0000-000000000000 1630012a-3347-479e-ab2b-76996c192c36 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:53:40.261757+00 +00000000-0000-0000-0000-000000000000 a8db2936-54c5-4614-94c5-4a13e6c83654 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:59:17.880911+00 +00000000-0000-0000-0000-000000000000 3d4fb0b1-d8d1-4beb-8c3a-53a22607cddd {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:59:25.603132+00 +00000000-0000-0000-0000-000000000000 10ef60d6-f55d-4370-b320-78c7b6561104 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:59:34.821814+00 +00000000-0000-0000-0000-000000000000 efc9e3f6-c034-4a04-977e-b81d445763ed {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:59:55.339526+00 +00000000-0000-0000-0000-000000000000 3b3a02dc-067d-42cc-917c-7811c4fd6347 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:02:44.995107+00 +00000000-0000-0000-0000-000000000000 c7cb137e-33a1-4fb2-9960-4bb015db752c {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:02:46.891055+00 +00000000-0000-0000-0000-000000000000 9bd389c2-f3ed-4197-a22b-bab553eb4e4f {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:12:46.756181+00 +00000000-0000-0000-0000-000000000000 bab2f893-dc20-436c-8363-d33a4aee8d06 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:13:14.788124+00 +00000000-0000-0000-0000-000000000000 651c44ae-e597-4840-ab53-1e6732543227 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:21:43.325253+00 +00000000-0000-0000-0000-000000000000 b6c79412-3e78-40e3-b079-555f9240682f {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:21:56.512281+00 +00000000-0000-0000-0000-000000000000 8a4f0ef0-c0ef-4278-852c-ca0ca0393fb9 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:22:05.26663+00 +00000000-0000-0000-0000-000000000000 1811dd26-c005-4aa5-b35d-f1d3a6668be7 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:22:21.864228+00 +00000000-0000-0000-0000-000000000000 5b46125b-ec57-4907-90bd-0f797cf56bfc {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:48:00.736884+00 +00000000-0000-0000-0000-000000000000 45fd6936-faf4-4df8-aac0-263d13f756c8 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:48:53.242588+00 +00000000-0000-0000-0000-000000000000 2a2f7b53-e0c5-4cfc-9b38-cea9d67ad2ad {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 23:32:17.947743+00 +00000000-0000-0000-0000-000000000000 8e3139b2-1c40-47ad-a247-3ccaa94e08ad {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 23:32:44.158626+00 +00000000-0000-0000-0000-000000000000 c0f1f8c1-5da2-4171-acf4-f15f3fef837b {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 09:06:02.093059+00 +00000000-0000-0000-0000-000000000000 0f53bba3-4d36-4bd9-bc78-4e5ce659129c {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 09:06:02.096051+00 +00000000-0000-0000-0000-000000000000 e2ea64a5-54a5-4b0a-95b4-cc26be5dc85e {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:27:06.674863+00 +00000000-0000-0000-0000-000000000000 f008faaf-004d-4d13-ba4d-cec5a113cfe8 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:39:56.661836+00 +00000000-0000-0000-0000-000000000000 5d36ee39-6793-42c6-a157-31037e7ddcbe {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"asd","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:52:45.795272+00 +00000000-0000-0000-0000-000000000000 98a8a1e1-8a81-4837-ba3c-c149d8d0be77 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leoardno","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:57:12.890495+00 +00000000-0000-0000-0000-000000000000 7a5d36c5-48b9-4a8c-b5af-af0fee6b61e2 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leoardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:57:34.211215+00 +00000000-0000-0000-0000-000000000000 4f5a2ed6-3898-4274-a852-519d71b71b21 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:58:48.279047+00 +00000000-0000-0000-0000-000000000000 5404eb4e-93f4-41a5-a86d-4250e2301115 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 10:05:13.289504+00 +00000000-0000-0000-0000-000000000000 e1e7b9e4-86c0-4d43-b108-ce91cfa7305f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 10:05:13.291502+00 +00000000-0000-0000-0000-000000000000 51d32ba8-701c-4bcc-ab72-74c02b06f37e {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 10:05:38.42979+00 +00000000-0000-0000-0000-000000000000 04b8022c-7a17-466e-baab-8355226f82f6 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 10:13:27.443108+00 +00000000-0000-0000-0000-000000000000 c59f34c3-6d7c-43b6-8335-5605d7914abb {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 10:52:34.48919+00 +00000000-0000-0000-0000-000000000000 94735b50-0e6e-4c02-8e0a-7ed9fe148337 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 10:52:36.530338+00 +00000000-0000-0000-0000-000000000000 e2b764c8-79d0-4e51-95f0-b980ccf2d799 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:03:42.792326+00 +00000000-0000-0000-0000-000000000000 3e674f0a-99ab-4c1e-81cf-467b04d50628 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:04:08.619676+00 +00000000-0000-0000-0000-000000000000 b2e669ff-00d5-4de8-a7a1-212c058b997e {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:04:18.215073+00 +00000000-0000-0000-0000-000000000000 efd87e91-95e7-4d43-906c-8700b9763e5d {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:04:41.642564+00 +00000000-0000-0000-0000-000000000000 0e3b31f4-ac2c-427b-9d35-3e9888bea099 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:09:33.113539+00 +00000000-0000-0000-0000-000000000000 c66e88bb-5e43-4d64-9941-bf7b87df1f57 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:09:42.050633+00 +00000000-0000-0000-0000-000000000000 f0d83a39-f0b6-411b-99cd-522f82a7c614 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:27:39.615562+00 +00000000-0000-0000-0000-000000000000 df8b1dd9-47ac-48a7-a1b0-49d7fb81e65c {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:30:42.202869+00 +00000000-0000-0000-0000-000000000000 825d4eb3-8690-4d89-89ee-36235b27fd2b {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 12:24:04.81973+00 +00000000-0000-0000-0000-000000000000 5449ebbf-358c-4daf-941a-071a5aa38352 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 12:24:06.324772+00 +00000000-0000-0000-0000-000000000000 5241335c-6b1e-42cd-b5f0-cf43ae4eac42 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 12:36:58.667642+00 +00000000-0000-0000-0000-000000000000 f77cb484-a8c6-4f12-b203-e08788747c89 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 12:37:01.302944+00 +00000000-0000-0000-0000-000000000000 7f91383b-30cf-4e95-ba47-b901f59133e5 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 12:37:09.603409+00 +00000000-0000-0000-0000-000000000000 de46c96d-1bbd-42c1-82d6-d28eb0cefd38 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 12:37:21.704034+00 +00000000-0000-0000-0000-000000000000 107aa0d2-82b7-4bd7-8672-96a43308cce4 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 13:05:48.781671+00 +00000000-0000-0000-0000-000000000000 cafae40c-1851-459a-8015-bf579bf00dfb {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 13:06:26.132815+00 +00000000-0000-0000-0000-000000000000 a24e3b39-ec62-480e-a8ab-1abe7cf5b6d1 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 14:06:35.696602+00 +00000000-0000-0000-0000-000000000000 a3ec0876-8151-446b-8027-f98fd9caf51b {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 14:06:35.697965+00 +00000000-0000-0000-0000-000000000000 c4557809-e407-495e-80d8-2de06b0c3cf5 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 14:15:40.80509+00 +00000000-0000-0000-0000-000000000000 c12e09c1-2140-4214-b3a6-b3f865941b29 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 14:15:43.617516+00 +00000000-0000-0000-0000-000000000000 fc7e22db-e27e-422e-ac30-93d657d23e78 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 14:16:38.012591+00 +00000000-0000-0000-0000-000000000000 c4b30fe1-40d5-4c74-aa66-0736bb7a3fb8 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 14:17:04.239061+00 +00000000-0000-0000-0000-000000000000 1608005f-42b8-4860-b87b-0232d72312a6 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 15:19:35.676083+00 +00000000-0000-0000-0000-000000000000 8cf0e884-4e73-47b2-8ae6-6b782ef8ad10 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 15:19:35.678353+00 +00000000-0000-0000-0000-000000000000 f9dac421-76ac-45f4-86ef-8d3e6d5f98fb {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 15:59:56.791407+00 +00000000-0000-0000-0000-000000000000 33eed8d8-66e0-4b32-9911-9a51a021903b {"action":"login","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 16:00:02.074346+00 +00000000-0000-0000-0000-000000000000 78d20dc8-f2fc-49b7-b0d2-ab87d74f7c29 {"action":"logout","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 16:00:24.342213+00 +00000000-0000-0000-0000-000000000000 d0bf8715-a43d-43ac-88d9-e3258869de64 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 16:00:29.89578+00 +00000000-0000-0000-0000-000000000000 72c8a85f-a799-40d9-983e-3ac69c5b3423 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 16:59:35.640753+00 +00000000-0000-0000-0000-000000000000 b715f1b6-c4e3-4202-9862-4329e4122982 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 16:59:35.6428+00 +00000000-0000-0000-0000-000000000000 f77600bd-8300-4dbc-818d-0858ea37f087 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 00:09:26.410887+00 +00000000-0000-0000-0000-000000000000 ffa75206-56e7-44b4-a178-2c30d81d9f9d {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 00:09:26.419786+00 +00000000-0000-0000-0000-000000000000 07f1ae30-3e05-4c31-af31-0a8ee3e5d273 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 10:21:19.707113+00 +00000000-0000-0000-0000-000000000000 9f1825b9-5b7a-438c-88b3-f57e362d28ad {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 10:21:19.710186+00 +00000000-0000-0000-0000-000000000000 303beb7c-43b9-47bc-912b-eb79e60e6dc6 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 11:21:29.053307+00 +00000000-0000-0000-0000-000000000000 2a079185-3947-491f-8e82-f24a0c76296f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 11:21:29.054667+00 +00000000-0000-0000-0000-000000000000 14fb8f67-738e-47de-bbbe-56e7847cd64e {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 12:19:44.588785+00 +00000000-0000-0000-0000-000000000000 84d82180-07d8-479b-bed2-a192d529f215 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 12:19:44.590076+00 +00000000-0000-0000-0000-000000000000 bf82881e-4b5d-4097-825e-36c8521f4dfa {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 13:18:24.943319+00 +00000000-0000-0000-0000-000000000000 c2d62d87-760f-4587-a060-e98b8d8f8ffa {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 13:18:24.944856+00 +00000000-0000-0000-0000-000000000000 6e0891d3-a8c1-48f8-b5c0-322de7d7c5cb {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 16:29:24.12373+00 +00000000-0000-0000-0000-000000000000 9804cc85-8789-471c-898d-1fad349a06b2 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 16:29:24.141016+00 +00000000-0000-0000-0000-000000000000 1eae238f-1b13-4e81-8149-944142ed6d47 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 17:32:18.742735+00 +00000000-0000-0000-0000-000000000000 72e31b31-f40a-4068-8918-0c5b64644945 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 17:32:18.743711+00 +00000000-0000-0000-0000-000000000000 67ca8fca-4037-4e05-a540-41d512d4fccc {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 18:32:18.727376+00 +00000000-0000-0000-0000-000000000000 1fc355c0-9972-483f-adb7-c9d3b8d0b5d4 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 18:32:18.729407+00 +00000000-0000-0000-0000-000000000000 55042c1f-7216-47f9-b0ba-d54e3870c7e5 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 19:32:18.863727+00 +00000000-0000-0000-0000-000000000000 969bd349-4ecd-491b-a3f2-8f65a7d6523f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 19:32:18.865113+00 +00000000-0000-0000-0000-000000000000 a168aa6b-e162-457b-b5e2-d0a5af4ab940 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 20:32:19.191799+00 +00000000-0000-0000-0000-000000000000 6fb6746b-5b91-4e11-b8f1-c89f002bdddb {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 20:32:19.192779+00 +00000000-0000-0000-0000-000000000000 0784fc43-7924-4a9a-be13-2feb238cdf33 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 21:32:19.266164+00 +00000000-0000-0000-0000-000000000000 3450c0c7-9516-46e5-8ca8-e255c881aa61 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 21:32:19.267431+00 +00000000-0000-0000-0000-000000000000 736c7b14-d238-4efb-a96b-b6eef788b8f8 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 22:32:19.303114+00 +00000000-0000-0000-0000-000000000000 45fc0d26-6476-4bfe-afb2-76696e44ba87 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 22:32:19.304733+00 +00000000-0000-0000-0000-000000000000 41bd0117-bc99-4833-bfad-864cd8b82149 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 23:32:19.338465+00 +00000000-0000-0000-0000-000000000000 e2338140-054b-47aa-87de-2d93b214dd19 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 23:32:19.340439+00 +00000000-0000-0000-0000-000000000000 34e12e1c-8c15-47ce-82f8-c2388a927eb7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-29 11:26:58.456145+00 +00000000-0000-0000-0000-000000000000 afaff300-4e87-4a1d-9132-8b37de1b0fb1 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-29 11:26:58.462288+00 +00000000-0000-0000-0000-000000000000 27f97d70-943a-4aff-a76f-6aef08880011 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-29 11:27:04.444496+00 +00000000-0000-0000-0000-000000000000 779cbb08-431d-4aa5-8257-895ea5563c2e {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-29 11:27:10.843532+00 +00000000-0000-0000-0000-000000000000 ac0230ad-5db1-46bb-86c1-38290ce25ec5 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-29 12:28:22.663176+00 +00000000-0000-0000-0000-000000000000 bb9a5133-34ae-4ddf-a117-6a11f65c4b8c {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-29 12:28:22.666638+00 +\. + + +-- +-- Data for Name: flow_state; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.flow_state (id, user_id, auth_code, code_challenge_method, code_challenge, provider_type, provider_access_token, provider_refresh_token, created_at, updated_at, authentication_method, auth_code_issued_at, invite_token, referrer, oauth_client_state_id, linking_target_id, email_optional) FROM stdin; +\. + + +-- +-- Data for Name: users; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.users (instance_id, id, aud, role, email, encrypted_password, email_confirmed_at, invited_at, confirmation_token, confirmation_sent_at, recovery_token, recovery_sent_at, email_change_token_new, email_change, email_change_sent_at, last_sign_in_at, raw_app_meta_data, raw_user_meta_data, is_super_admin, created_at, updated_at, phone, phone_confirmed_at, phone_change, phone_change_token, phone_change_sent_at, email_change_token_current, email_change_confirm_status, banned_until, reauthentication_token, reauthentication_sent_at, is_sso_user, deleted_at, is_anonymous) FROM stdin; +00000000-0000-0000-0000-000000000000 aaaaaaaa-0001-0001-0001-000000000001 authenticated authenticated paciente@agenciapsi.com.br $2a$06$ipEc7puaVhQnpusOGhAYgOcrVHq4RnqZeDooS8FaehzHhueScf9S. 2026-03-23 10:46:29.876072+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Ana Paciente"} \N 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0005-0005-0005-000000000005 authenticated authenticated clinica3@agenciapsi.com.br $2a$06$L3aFykCSdduzTHEKsEQ3q.GdHTb5EJBvbIit4k7ZgnbRd5BCGuTxu 2026-03-23 10:46:29.876072+00 \N \N \N \N 2026-03-27 16:00:02.080329+00 {"provider": "email", "providers": ["email"]} {"name": "Clínica Bem Estar"} \N 2026-03-23 10:46:29.876072+00 2026-03-27 16:00:02.084768+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0011-0011-0011-000000000011 authenticated authenticated secretary@agenciapsi.com.br $2a$06$O7HeygRYgJViriMFCImLZu7DD.3A9wZWb9y3c5G2PIURgJ65UnqT. 2026-03-23 14:18:06.087973+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Gabriela Secretária"} \N 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0002-0002-0002-000000000002 authenticated authenticated terapeuta@agenciapsi.com.br $2a$06$CztXijQkaPZa6pUwXmMHWuzSF19GiVtRBdMLp.k4iWf7ftGWNBIg6 2026-03-23 10:46:29.876072+00 \N \N \N \N 2026-03-29 11:27:10.845265+00 {"provider": "email", "providers": ["email"]} {"name": "Bruno Terapeuta", "full_name": "Leonardo Nohama", "avatar_url": "http://127.0.0.1:54321/storage/v1/object/public/avatars/aaaaaaaa-0002-0002-0002-000000000002/avatar.jpg"} \N 2026-03-23 10:46:29.876072+00 2026-03-29 12:28:22.676402+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 384a69d8-b7cd-40ac-9d3c-764c93532b66 authenticated authenticated terapeuta2@agenciapsi.com.br $2a$10$MBE/uZcT1lpKira6nsTY6OzrUabKwtOrm.QvJzdy.IU95tiX3M2ia 2026-03-26 19:47:30.496218+00 \N \N \N \N 2026-03-26 19:48:44.253788+00 {"provider": "email", "providers": ["email"]} {"sub": "384a69d8-b7cd-40ac-9d3c-764c93532b66", "email": "terapeuta2@agenciapsi.com.br", "email_verified": true, "phone_verified": false} \N 2026-03-26 19:47:30.478057+00 2026-03-26 19:48:44.258995+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0007-0007-0007-000000000007 authenticated authenticated supervisor@agenciapsi.com.br $2a$06$.kF47/tagPNwSpgGM4ryZOu01L0ykU2IXakM8trZ.Hon1TTUDeqYK 2026-03-23 14:18:05.215881+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Carlos Supervisor"} \N 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0009-0009-0009-000000000009 authenticated authenticated therapist2@agenciapsi.com.br $2a$06$16hf/nUbN0lElm9l8vQI4ek8vIM2T8ymiJTQ8CHXXw/jD1gMuDFJS 2026-03-23 14:18:06.087973+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Eva Terapeuta"} \N 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0010-0010-0010-000000000010 authenticated authenticated therapist3@agenciapsi.com.br $2a$06$sBJPPHRI/MsrCTEeCK5/vOhASc/SNLeO.B/QEE2MZNWEP8FamyCXW 2026-03-23 14:18:06.087973+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Felipe Terapeuta"} \N 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0003-0003-0003-000000000003 authenticated authenticated clinica1@agenciapsi.com.br $2a$06$cxZ2uXWIOS9MgzoyzSla8Oocid6wKtEBPA4k9QyC8DvwzmOsI0co2 2026-03-23 10:46:29.876072+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Clínica Espaço Psi"} \N 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0004-0004-0004-000000000004 authenticated authenticated clinica2@agenciapsi.com.br $2a$06$ZSW6FPPCmhO8EkfSM4/QLu/J32HRe/87zoNLvPtCbqTdNBbanaLPi 2026-03-23 10:46:29.876072+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Clínica Mente Sã"} \N 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0008-0008-0008-000000000008 authenticated authenticated editor@agenciapsi.com.br $2a$06$lcF3sQOKaQOMwo5OTPPpcODcMtjDoUpHw3rOBhJMYow15LoJFLvH6 2026-03-23 14:18:05.215881+00 \N \N \N \N 2026-03-26 19:46:29.070205+00 {"provider": "email", "providers": ["email"]} {"name": "Diana Editora"} \N 2026-03-23 14:18:05.215881+00 2026-03-26 19:46:29.07828+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0006-0006-0006-000000000006 authenticated authenticated saas@agenciapsi.com.br $2a$06$QsvWGUd7HQTv6kSQDbRsiOkNcLM4O2BnQflXPbx3MK9E4RPGz5FvS 2026-03-23 10:46:29.876072+00 \N \N \N \N 2026-03-26 19:46:43.303588+00 {"provider": "email", "providers": ["email"]} {"name": "Admin Plataforma"} \N 2026-03-23 10:46:29.876072+00 2026-03-26 19:46:43.308732+00 \N \N \N 0 \N \N f \N f +\. + + +-- +-- Data for Name: identities; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.identities (provider_id, user_id, identity_data, provider, last_sign_in_at, created_at, updated_at, id) FROM stdin; +paciente@agenciapsi.com.br aaaaaaaa-0001-0001-0001-000000000001 {"sub": "aaaaaaaa-0001-0001-0001-000000000001", "email": "paciente@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 977a296d-b7ac-4151-8017-3099808e1f7f +terapeuta@agenciapsi.com.br aaaaaaaa-0002-0002-0002-000000000002 {"sub": "aaaaaaaa-0002-0002-0002-000000000002", "email": "terapeuta@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 44ccd811-6c51-48e9-81a8-77247022c693 +clinica1@agenciapsi.com.br aaaaaaaa-0003-0003-0003-000000000003 {"sub": "aaaaaaaa-0003-0003-0003-000000000003", "email": "clinica1@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 433175a2-4065-47ab-96dd-32508c9a7389 +clinica2@agenciapsi.com.br aaaaaaaa-0004-0004-0004-000000000004 {"sub": "aaaaaaaa-0004-0004-0004-000000000004", "email": "clinica2@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 390305dc-1f61-44e8-9c95-bd9de9591e5a +clinica3@agenciapsi.com.br aaaaaaaa-0005-0005-0005-000000000005 {"sub": "aaaaaaaa-0005-0005-0005-000000000005", "email": "clinica3@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 f669ef52-181e-44d5-8649-833d6b133313 +saas@agenciapsi.com.br aaaaaaaa-0006-0006-0006-000000000006 {"sub": "aaaaaaaa-0006-0006-0006-000000000006", "email": "saas@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 e6af6a5e-55ce-4872-a745-40d688d08982 +supervisor@agenciapsi.com.br aaaaaaaa-0007-0007-0007-000000000007 {"sub": "aaaaaaaa-0007-0007-0007-000000000007", "email": "supervisor@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 8dff38d2-1b09-4b88-9038-28b747358645 +editor@agenciapsi.com.br aaaaaaaa-0008-0008-0008-000000000008 {"sub": "aaaaaaaa-0008-0008-0008-000000000008", "email": "editor@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 a7856eb7-e96f-4752-84a3-b676503b74ae +therapist2@agenciapsi.com.br aaaaaaaa-0009-0009-0009-000000000009 {"sub": "aaaaaaaa-0009-0009-0009-000000000009", "email": "therapist2@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 8129cc38-db24-4f84-b48e-bf0b46235811 +therapist3@agenciapsi.com.br aaaaaaaa-0010-0010-0010-000000000010 {"sub": "aaaaaaaa-0010-0010-0010-000000000010", "email": "therapist3@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 754b015d-3c88-4104-b246-eede6fffabba +secretary@agenciapsi.com.br aaaaaaaa-0011-0011-0011-000000000011 {"sub": "aaaaaaaa-0011-0011-0011-000000000011", "email": "secretary@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 a7aa272b-659f-4bc7-8667-10cf49200410 +384a69d8-b7cd-40ac-9d3c-764c93532b66 384a69d8-b7cd-40ac-9d3c-764c93532b66 {"sub": "384a69d8-b7cd-40ac-9d3c-764c93532b66", "email": "terapeuta2@agenciapsi.com.br", "email_verified": false, "phone_verified": false} email 2026-03-26 19:47:30.489663+00 2026-03-26 19:47:30.489705+00 2026-03-26 19:47:30.489705+00 7e35a3f9-29d8-4f8d-bdfa-5bfe1328293f +\. + + +-- +-- Data for Name: instances; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.instances (id, uuid, raw_base_config, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: oauth_clients; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_clients (id, client_secret_hash, registration_type, redirect_uris, grant_types, client_name, client_uri, logo_uri, created_at, updated_at, deleted_at, client_type, token_endpoint_auth_method) FROM stdin; +\. + + +-- +-- Data for Name: sessions; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.sessions (id, user_id, created_at, updated_at, factor_id, aal, not_after, refreshed_at, user_agent, ip, tag, oauth_client_id, refresh_token_hmac_key, refresh_token_counter, scopes) FROM stdin; +2dc73205-6913-432b-8eb7-e8b609e5f0f4 aaaaaaaa-0002-0002-0002-000000000002 2026-03-27 16:00:29.897964+00 2026-03-29 11:26:58.474232+00 \N aal1 \N 2026-03-29 11:26:58.474151 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 172.19.0.1 \N \N \N \N \N +3b2b06a2-19d1-4bd6-9cc5-7e0903adfbec aaaaaaaa-0002-0002-0002-000000000002 2026-03-29 11:27:04.446516+00 2026-03-29 11:27:04.446516+00 \N aal1 \N \N Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 172.19.0.1 \N \N \N \N \N +5af1b6d5-ae69-4eaa-b951-34e5ee9df49a aaaaaaaa-0002-0002-0002-000000000002 2026-03-29 11:27:10.845343+00 2026-03-29 12:28:22.680165+00 \N aal1 \N 2026-03-29 12:28:22.680085 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 172.19.0.1 \N \N \N \N \N +\. + + +-- +-- Data for Name: mfa_amr_claims; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.mfa_amr_claims (session_id, created_at, updated_at, authentication_method, id) FROM stdin; +2dc73205-6913-432b-8eb7-e8b609e5f0f4 2026-03-27 16:00:29.920845+00 2026-03-27 16:00:29.920845+00 password eb1238d8-fd33-4b72-b879-46e82e0abe4c +3b2b06a2-19d1-4bd6-9cc5-7e0903adfbec 2026-03-29 11:27:04.463145+00 2026-03-29 11:27:04.463145+00 password fb84282b-14d2-4b24-b5f8-00892a474c71 +5af1b6d5-ae69-4eaa-b951-34e5ee9df49a 2026-03-29 11:27:10.851292+00 2026-03-29 11:27:10.851292+00 password 873191aa-89fe-418c-b1d0-36a6e01d91fa +\. + + +-- +-- Data for Name: mfa_factors; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.mfa_factors (id, user_id, friendly_name, factor_type, status, created_at, updated_at, secret, phone, last_challenged_at, web_authn_credential, web_authn_aaguid, last_webauthn_challenge_data) FROM stdin; +\. + + +-- +-- Data for Name: mfa_challenges; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.mfa_challenges (id, factor_id, created_at, verified_at, ip_address, otp_code, web_authn_session_data) FROM stdin; +\. + + +-- +-- Data for Name: oauth_authorizations; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_authorizations (id, authorization_id, client_id, user_id, redirect_uri, scope, state, resource, code_challenge, code_challenge_method, response_type, status, authorization_code, created_at, expires_at, approved_at, nonce) FROM stdin; +\. + + +-- +-- Data for Name: oauth_client_states; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_client_states (id, provider_type, code_verifier, created_at) FROM stdin; +\. + + +-- +-- Data for Name: oauth_consents; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_consents (id, user_id, client_id, scopes, granted_at, revoked_at) FROM stdin; +\. + + +-- +-- Data for Name: one_time_tokens; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.one_time_tokens (id, user_id, token_type, token_hash, relates_to, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: refresh_tokens; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.refresh_tokens (instance_id, id, token, user_id, revoked, created_at, updated_at, parent, session_id) FROM stdin; +00000000-0000-0000-0000-000000000000 137 ow4odpbr72tv aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-27 16:00:29.902122+00 2026-03-27 16:59:35.643784+00 \N 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 138 o3r5wxkohiyw aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-27 16:59:35.646788+00 2026-03-28 00:09:26.422611+00 ow4odpbr72tv 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 139 kfkyr35zoc3h aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 00:09:26.424171+00 2026-03-28 10:21:19.710939+00 o3r5wxkohiyw 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 140 kuezvais3wpo aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 10:21:19.711933+00 2026-03-28 11:21:29.055573+00 kfkyr35zoc3h 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 141 z5x6eax4igbd aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 11:21:29.056815+00 2026-03-28 12:19:44.591001+00 kuezvais3wpo 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 142 ske3i45rhxno aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 12:19:44.59206+00 2026-03-28 13:18:24.94649+00 z5x6eax4igbd 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 143 j2kgfyyvyzoz aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 13:18:24.947764+00 2026-03-28 16:29:24.142032+00 ske3i45rhxno 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 144 lbfbw2cfblg6 aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 16:29:24.143185+00 2026-03-28 17:32:18.744294+00 j2kgfyyvyzoz 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 145 5hxaedb3akrf aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 17:32:18.745103+00 2026-03-28 18:32:18.730707+00 lbfbw2cfblg6 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 146 mfcvxvsspo6x aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 18:32:18.73203+00 2026-03-28 19:32:18.86568+00 5hxaedb3akrf 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 147 7y34dduriqt7 aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 19:32:18.866633+00 2026-03-28 20:32:19.19363+00 mfcvxvsspo6x 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 148 eu4ulwavwkgg aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 20:32:19.194909+00 2026-03-28 21:32:19.268258+00 7y34dduriqt7 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 149 eld7gsmd4hv6 aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 21:32:19.269488+00 2026-03-28 22:32:19.307119+00 eu4ulwavwkgg 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 150 bjkdbypnep24 aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 22:32:19.308462+00 2026-03-28 23:32:19.341287+00 eld7gsmd4hv6 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 151 p5yygguwwdiy aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 23:32:19.342438+00 2026-03-29 11:26:58.46342+00 bjkdbypnep24 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 152 mly6ofb2hilw aaaaaaaa-0002-0002-0002-000000000002 f 2026-03-29 11:26:58.464478+00 2026-03-29 11:26:58.464478+00 p5yygguwwdiy 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 153 aeam4sb4pg3c aaaaaaaa-0002-0002-0002-000000000002 f 2026-03-29 11:27:04.459198+00 2026-03-29 11:27:04.459198+00 \N 3b2b06a2-19d1-4bd6-9cc5-7e0903adfbec +00000000-0000-0000-0000-000000000000 154 ipmk2xa65emx aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-29 11:27:10.848209+00 2026-03-29 12:28:22.670434+00 \N 5af1b6d5-ae69-4eaa-b951-34e5ee9df49a +00000000-0000-0000-0000-000000000000 155 snr5vebtnvai aaaaaaaa-0002-0002-0002-000000000002 f 2026-03-29 12:28:22.674079+00 2026-03-29 12:28:22.674079+00 ipmk2xa65emx 5af1b6d5-ae69-4eaa-b951-34e5ee9df49a +\. + + +-- +-- Data for Name: sso_providers; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.sso_providers (id, resource_id, created_at, updated_at, disabled) FROM stdin; +\. + + +-- +-- Data for Name: saml_providers; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.saml_providers (id, sso_provider_id, entity_id, metadata_xml, metadata_url, attribute_mapping, created_at, updated_at, name_id_format) FROM stdin; +\. + + +-- +-- Data for Name: saml_relay_states; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.saml_relay_states (id, sso_provider_id, request_id, for_email, redirect_to, created_at, updated_at, flow_state_id) FROM stdin; +\. + + +-- +-- Data for Name: schema_migrations; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.schema_migrations (version) FROM stdin; +20171026211738 +20171026211808 +20171026211834 +20180103212743 +20180108183307 +20180119214651 +20180125194653 +00 +20210710035447 +20210722035447 +20210730183235 +20210909172000 +20210927181326 +20211122151130 +20211124214934 +20211202183645 +20220114185221 +20220114185340 +20220224000811 +20220323170000 +20220429102000 +20220531120530 +20220614074223 +20220811173540 +20221003041349 +20221003041400 +20221011041400 +20221020193600 +20221021073300 +20221021082433 +20221027105023 +20221114143122 +20221114143410 +20221125140132 +20221208132122 +20221215195500 +20221215195800 +20221215195900 +20230116124310 +20230116124412 +20230131181311 +20230322519590 +20230402418590 +20230411005111 +20230508135423 +20230523124323 +20230818113222 +20230914180801 +20231027141322 +20231114161723 +20231117164230 +20240115144230 +20240214120130 +20240306115329 +20240314092811 +20240427152123 +20240612123726 +20240729123726 +20240802193726 +20240806073726 +20241009103726 +20250717082212 +20250731150234 +20250804100000 +20250901200500 +20250903112500 +20250904133000 +20250925093508 +20251007112900 +20251104100000 +20251111201300 +20251201000000 +20260115000000 +20260121000000 +\. + + +-- +-- Data for Name: sso_domains; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.sso_domains (id, sso_provider_id, domain, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: job; Type: TABLE DATA; Schema: cron; Owner: - +-- + +COPY cron.job (jobid, schedule, command, nodename, nodeport, database, username, active, jobname) FROM stdin; +\. + + +-- +-- Data for Name: job_run_details; Type: TABLE DATA; Schema: cron; Owner: - +-- + +COPY cron.job_run_details (jobid, runid, job_pid, database, username, command, status, return_message, start_time, end_time) FROM stdin; +\. + + +-- +-- Data for Name: _db_migrations; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public._db_migrations (id, filename, hash, category, applied_at) FROM stdin; +1 seed_001_fixed.sql 87fc24517f6446f7 seed 2026-03-23 14:15:02.14603+00 +2 seed_002.sql b05d565b35c97300 seed 2026-03-23 14:15:02.45035+00 +3 seed_003.sql 257ef8bba4e319a2 seed 2026-03-23 14:15:02.755322+00 +4 seed_010_plans.sql 0de612f2301e27d3 seed 2026-03-23 14:15:02.974622+00 +5 seed_011_features.sql e7326ac0e33e4fee seed 2026-03-23 14:15:03.261589+00 +6 seed_012_plan_features.sql f0e1b4ab684383f7 seed 2026-03-23 14:15:03.553899+00 +7 seed_013_subscriptions.sql b61e4af59262f3ac seed 2026-03-23 14:15:03.816997+00 +8 seed_014_global_data.sql a7bc086bc6f052ee seed 2026-03-23 14:15:04.080095+00 +9 fix_addon_credits_fk.sql aaff13facb98e4d8 fix 2026-03-23 14:15:04.372331+00 +10 fix_addon_rls_saas_admin.sql 84d85284eb441afc fix 2026-03-23 14:15:04.630692+00 +11 fix_missing_subscriptions.sql f5740f6eef9e5405 fix 2026-03-23 14:15:04.916745+00 +12 fix_notification_templates_rls_admin.sql ede371cbce54e13e fix 2026-03-23 14:15:05.15764+00 +13 fix_seed_patient_groups.sql e9b870ba0ad5f359 fix 2026-03-23 14:15:05.485322+00 +14 fix_subscriptions_validate_scope.sql c814a90d768d339c fix 2026-03-23 14:15:06.461275+00 +15 fix_template_keys_match_populate.sql e0fdd2a420abaeb8 fix 2026-03-23 14:15:06.977464+00 +\. + + +-- +-- Data for Name: tenants; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenants (id, name, created_at, kind) FROM stdin; +bbbbbbbb-0009-0009-0009-000000000009 Eva Terapeuta 2026-03-23 10:47:12.826179+00 therapist +bbbbbbbb-0010-0010-0010-000000000010 Felipe Terapeuta 2026-03-23 10:47:12.826179+00 therapist +bbbbbbbb-0003-0003-0003-000000000003 Clínica Espaço Psi 2026-03-23 10:46:29.876072+00 clinic_coworking +bbbbbbbb-0004-0004-0004-000000000004 Clínica Mente Sã 2026-03-23 10:46:29.876072+00 clinic_reception +bbbbbbbb-0005-0005-0005-000000000005 Clínica Bem Estar 2026-03-23 10:46:29.876072+00 clinic_full +a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 Diana Editora 2026-03-26 19:46:29.161046+00 therapist +1e98ca49-a46c-4701-847b-145a14d53d19 terapeuta2 2026-03-26 19:47:30.68134+00 therapist +bbbbbbbb-0002-0002-0002-000000000002 Bruno Terapeuta 2026-03-23 10:46:29.876072+00 therapist +\. + + +-- +-- Data for Name: addon_credits; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.addon_credits (id, tenant_id, owner_id, addon_type, balance, total_purchased, total_consumed, low_balance_threshold, low_balance_notified, daily_limit, hourly_limit, daily_used, hourly_used, daily_reset_at, hourly_reset_at, from_number_override, expires_at, is_active, created_at, updated_at) FROM stdin; +0f2ed178-d2d1-4bf0-a58c-206be0183c1c bbbbbbbb-0002-0002-0002-000000000002 \N sms 320 320 0 10 f \N \N 0 0 \N \N \N \N t 2026-03-25 00:15:28.741153+00 2026-03-25 00:15:37.596479+00 +\. + + +-- +-- Data for Name: addon_products; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.addon_products (id, slug, name, description, addon_type, icon, credits_amount, price_cents, currency, is_active, is_visible, sort_order, metadata, created_at, updated_at, deleted_at) FROM stdin; +a6cfa47f-e26d-4835-9c14-4629e8afa331 sms_basico SMS Básico 100 créditos SMS. Ideal para quem está começando ou tem poucos pacientes. sms pi pi-comment 100 2500 BRL t t 10 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +56d95e40-9e32-491d-969d-8970f51c05ed sms_essencial SMS Essencial 220 créditos SMS. Para consultórios em crescimento com envio regular de lembretes. sms pi pi-comments 220 5000 BRL t t 20 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +7589b9fc-2ccf-43f0-aea4-abc3edf05473 sms_profissional SMS Profissional 350 créditos SMS. Para quem envia lembretes, confirmações e avisos de cobrança. sms pi pi-send 350 7500 BRL t t 30 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +2dff4229-32a8-4090-8f19-6c3ab836b9c1 sms_premium SMS Premium 500 créditos SMS. Melhor custo-benefício. Clínicas e agenda cheia. sms pi pi-star 500 10000 BRL t t 40 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +\. + + +-- +-- Data for Name: addon_transactions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.addon_transactions (id, tenant_id, owner_id, addon_type, type, amount, balance_before, balance_after, product_id, queue_id, description, admin_user_id, payment_method, payment_reference, price_cents, currency, created_at, metadata) FROM stdin; +46181c84-9f01-4fec-baaf-c10aa935f240 bbbbbbbb-0002-0002-0002-000000000002 \N sms purchase 100 0 100 a6cfa47f-e26d-4835-9c14-4629e8afa331 \N SMS Básico aaaaaaaa-0006-0006-0006-000000000006 manual \N 2500 BRL 2026-03-25 00:15:28.741153+00 {} +a07a56b5-1b6d-46a1-88fd-15623c77f07f bbbbbbbb-0002-0002-0002-000000000002 \N sms purchase 220 100 320 56d95e40-9e32-491d-969d-8970f51c05ed \N SMS Essencial aaaaaaaa-0006-0006-0006-000000000006 manual \N 5000 BRL 2026-03-25 00:15:37.596479+00 {} +\. + + +-- +-- Data for Name: agenda_bloqueios; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_bloqueios (id, owner_id, tenant_id, tipo, titulo, data_inicio, data_fim, hora_inicio, hora_fim, recorrente, dia_semana, observacao, origem, created_at) FROM stdin; +f809fcb6-0369-42fd-985a-5d9976798e88 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 bloqueio Feriado: Sexta-feira Santa 2026-04-03 2026-04-03 \N \N f \N \N agenda_feriado 2026-03-23 11:32:18.225845+00 +\. + + +-- +-- Data for Name: agenda_configuracoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_configuracoes (owner_id, duracao_padrao_minutos, intervalo_padrao_minutos, timezone, usar_horario_admin_custom, admin_inicio_visualizacao, admin_fim_visualizacao, admin_slot_visual_minutos, online_ativo, online_min_antecedencia_horas, online_max_dias_futuro, online_cancelar_ate_horas, online_reagendar_ate_horas, online_limite_agendamentos_futuros, online_modo, online_buffer_antes_min, online_buffer_depois_min, online_modalidade, created_at, updated_at, usar_granularidade_custom, granularidade_min, setup_concluido, setup_concluido_em, agenda_view_mode, agenda_custom_start, agenda_custom_end, session_duration_min, session_break_min, pausas_semanais, setup_clinica_concluido, setup_clinica_concluido_em, tenant_id, jornada_igual_todos, slot_mode, atendimento_mode) FROM stdin; +aaaaaaaa-0005-0005-0005-000000000005 50 0 America/Sao_Paulo f \N \N 30 f 24 60 12 12 1 automatico 0 0 ambos 2026-03-25 18:35:26.138564+00 2026-03-26 17:19:10.818241+00 f \N t 2026-03-26 17:19:10.804+00 full_24h \N \N 40 10 [{"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 0}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 1}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 2}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 3}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 4}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 5}] f \N bbbbbbbb-0005-0005-0005-000000000005 t fixed ambos +aaaaaaaa-0002-0002-0002-000000000002 50 0 America/Sao_Paulo f \N \N 30 f 24 60 12 12 1 automatico 0 0 ambos 2026-03-23 10:58:31.422764+00 2026-03-27 14:18:31.232768+00 f \N t 2026-03-27 14:18:31.221+00 full_24h \N \N 50 10 [{"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 0}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 1}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 2}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 3}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 4}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 5}] f \N bbbbbbbb-0002-0002-0002-000000000002 t fixed ambos +\. + + +-- +-- Data for Name: insurance_plans; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.insurance_plans (id, owner_id, tenant_id, name, notes, default_value, active, created_at, updated_at) FROM stdin; +ec7aa65f-cbd2-48a5-9ec0-368a788438a7 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste asd \N t 2026-03-24 12:21:20.501533+00 2026-03-24 12:21:20.501533+00 +\. + + +-- +-- Data for Name: tenant_members; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_members (id, tenant_id, user_id, role, status, created_at) FROM stdin; +72be63ef-bc2c-4c8e-8522-0c6d64746bbc bbbbbbbb-0002-0002-0002-000000000002 aaaaaaaa-0002-0002-0002-000000000002 tenant_admin active 2026-03-23 10:46:29.876072+00 +07974953-677e-4941-b90f-1ae15ffbbbf6 bbbbbbbb-0003-0003-0003-000000000003 aaaaaaaa-0003-0003-0003-000000000003 tenant_admin active 2026-03-23 10:46:29.876072+00 +11417585-46af-4faa-a34a-dc23a36c5704 bbbbbbbb-0004-0004-0004-000000000004 aaaaaaaa-0004-0004-0004-000000000004 tenant_admin active 2026-03-23 10:46:29.876072+00 +cd22a662-2b16-4cfe-a9e6-15ccb8e7d67e bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0005-0005-0005-000000000005 tenant_admin active 2026-03-23 10:46:29.876072+00 +e40559cd-43af-4547-b447-2cc7bbff5ad7 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0002-0002-0002-000000000002 therapist active 2026-03-23 10:46:29.876072+00 +0ee00481-77f2-4bc0-ab09-3bc441c03b1b bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0007-0007-0007-000000000007 supervisor active 2026-03-23 14:18:05.215881+00 +344a6ae3-379a-4776-9441-8ac9f8733c99 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0008-0008-0008-000000000008 therapist active 2026-03-23 14:18:05.215881+00 +aac8e805-da4f-4089-b2e1-0b32b679d0dd bbbbbbbb-0009-0009-0009-000000000009 aaaaaaaa-0009-0009-0009-000000000009 tenant_admin active 2026-03-23 14:18:06.087973+00 +2633ed47-76e8-4a30-8e3c-07bec4f7c9cd bbbbbbbb-0010-0010-0010-000000000010 aaaaaaaa-0010-0010-0010-000000000010 tenant_admin active 2026-03-23 14:18:06.087973+00 +a72fa56d-04e4-4e23-853c-fd4926b263c3 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0009-0009-0009-000000000009 therapist active 2026-03-23 14:18:06.087973+00 +677f130c-06bd-44d7-b283-844351d65c45 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0010-0010-0010-000000000010 therapist active 2026-03-23 14:18:06.087973+00 +978ce15a-8dc5-4540-8430-f9e1e19d4952 bbbbbbbb-0004-0004-0004-000000000004 aaaaaaaa-0011-0011-0011-000000000011 clinic_admin active 2026-03-23 14:18:06.087973+00 +da8fd413-1710-40f2-85a0-5af9cf2c78d8 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 aaaaaaaa-0008-0008-0008-000000000008 tenant_admin active 2026-03-26 19:46:29.161046+00 +fb7a3517-0479-42ac-8c37-ec6ffc03c5be 1e98ca49-a46c-4701-847b-145a14d53d19 384a69d8-b7cd-40ac-9d3c-764c93532b66 tenant_admin active 2026-03-26 19:47:30.68134+00 +\. + + +-- +-- Data for Name: patients; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patients (id, nome_completo, email_principal, telefone, created_at, owner_id, avatar_url, status, last_attended_at, is_native, naturalidade, data_nascimento, rg, cpf, identification_color, genero, estado_civil, email_alternativo, pais, cep, cidade, estado, endereco, numero, bairro, complemento, escolaridade, profissao, nome_parente, grau_parentesco, telefone_alternativo, onde_nos_conheceu, encaminhado_por, nome_responsavel, telefone_responsavel, cpf_responsavel, observacao_responsavel, cobranca_no_responsavel, observacoes, notas_internas, updated_at, telefone_parente, tenant_id, responsible_member_id, user_id, patient_scope, therapist_member_id, nome_social, pronomes, etnia, religiao, faixa_renda, canal_preferido, horario_contato_inicio, horario_contato_fim, idioma, origem, metodo_pagamento_preferido, motivo_saida, data_saida, encaminhado_para, risco_elevado, risco_nota, risco_sinalizado_em, risco_sinalizado_por, horario_contato, convenio, convenio_id) FROM stdin; +6449e64b-050b-419f-8845-029b6f10a17d Otto Rank otto.rank.437@example.com 86363331874 2026-03-23 11:33:02.010795+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-23 11:33:02.010795+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +76cec0bb-1e63-45bf-9b03-feaaa2a5d18d Peter Fonagy peter.fonagy.466@example.com 88283303064 2026-03-24 10:20:35.019712+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-24 10:20:35.019712+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 Carla Lima Souza carla.lima.souza.882@email.com 55327597657 2026-03-25 20:47:18.217202+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Bauru 1980-10-09 498989206 41727305582 \N Feminino Casado(a) alt.709@email.com Brasil 73591-841 Santos SP Av. Brasil 5688 Jardim Paulista Apto 637 Superior completo Estudante Henrique Costa Irmão 55216555751 Site Ana Ribeiro Yasmin Araújo Lima 55209246525 85070073257 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:47:18.217202+00 55979769772 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +d8a170f0-c67e-4e6d-8dc1-b51423d20889 Anna Freud anna.freud.323@example.com 96307749614 2026-03-25 20:47:35.342234+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-25 20:47:35.342234+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +4fdd639d-5f32-453f-9092-74b183c8bbfe Daniel Oliveira Silva daniel.oliveira.silva.779@email.com 55629968595 2026-03-25 20:47:43.705237+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Ribeirão Preto 2018-06-14 644776179 07710834329 \N Feminino Solteiro(a) alt.72@email.com Brasil 43519-831 Ribeirão Preto RS Rua XV de Novembro 5937 Vila Prado Apto 78 Superior incompleto Autônomo Felipe Lima Irmã 55619689156 Outro Vanessa Martins Vanessa Carvalho Barbosa 55209874519 24378696207 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:47:43.705237+00 55738955502 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +8e33a4dc-4bfb-4cce-aec0-fe5a5c91300f John Bowlby john.bowlby.398@example.com 84123228043 2026-03-25 20:48:39.752714+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-25 20:48:39.752714+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +2aaa34f7-c770-4d78-a2a2-c77bd4771c6a Yasmin Martins Lima yasmin.martins.lima.810@email.com 55979361027 2026-03-25 20:58:11.897509+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Santos 1950-10-11 25599572 65091040676 \N Prefere não informar Divorciado(a) alt.364@email.com Brasil 55632-609 Santos RJ Av. Brasil 1268 Jardim Paulista Apto 117 Superior incompleto Autônomo Bruno Martins Pai 55819227599 Google Marcos Martins Ana Oliveira Martins 55129232266 29325344165 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:58:11.897509+00 55529945515 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +c193905f-e70c-4935-aec3-b9c161c6044c Otávio Souza Ferreira888 otavio.souza.ferreira.212@email.com 55519100542 2026-03-25 20:58:37.007581+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Campinas 2018-03-10 249907937 85207459899 \N Feminino Solteiro(a) alt.523@email.com Brasil 80355-723 Araraquara RS Rua XV de Novembro 2199 Vila Prado Apto 249 Ensino Médio Professora Felipe Carvalho Pai 55759833223 Threads Yasmin Carvalho Sabrina Martins Santos 55749688637 89130651778 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:58:37.007581+00 55225172394 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +919ce219-3cca-4bf3-ad9d-2cc8db68cec0 João Almeida Martins joao.almeida.martins.979@email.com 55309899385 2026-03-28 10:35:52.861986+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Santos 2009-10-23 203656518 40331112558 \N Outro União estável alt.44@email.com Brasil 96195-389 São Carlos SP Rua XV de Novembro 9156 Santa Felícia Apto 486 Pós-graduação Autônomo João Ferreira Pai 55289171423 Outro Larissa Almeida Eduarda Almeida Almeida 55141999898 38448234766 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-28 10:35:52.861986+00 55389349597 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +778338f5-7122-4d80-a6da-88a52deee1ca Larissa Souza Santos teste7@agenciapsi.com.br 16988280038 2026-03-28 12:17:09.861474+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f São Carlos 1984-05-14 914872638 88993691274 \N Não-binário Casado(a) teste1@agenciapsi.com.br Brasil 13561-260 São Carlos SP Rua Conde do Pinhal 457 Centro Apartamento Ensino médio completo Professora \N \N 16996005268 Indicação teste Responsavel 11111111111 11111111111 Obs responsavel t Próximo ao posto de saúde do centro. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-28 12:17:09.861474+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N Híbrido \N \N ela/dela Indígena \N \N WhatsApp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N 08h–20h teste ec7aa65f-cbd2-48a5-9ec0-368a788438a7 +368f2fe0-72c9-4dca-ac40-0ee1af11b44c Otto Rank otto.rank.724@example.com 67720136104 2026-03-28 12:37:23.13379+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-28 12:37:23.13379+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +ad3bb70e-881e-4ee1-b44e-436eaae2caf1 Larissa Souza Santos teste7@agenciapsi.com.br 16988280038 2026-03-28 12:18:06.204794+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f São Carlos 1984-05-14 914872638 88993691274 \N Não-binário Casado(a) teste1@agenciapsi.com.br Brasil 13561-260 São Carlos SP Rua Conde do Pinhal 457 Centro Apartamento Ensino médio completo Professora \N \N 16996005268 Indicação teste Responsavel 11111111111 11111111111 Obs responsavel t Próximo ao posto de saúde do centro. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-28 12:20:59.238927+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N Híbrido \N \N ela/dela Indígena \N \N WhatsApp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N 08h–20h teste ec7aa65f-cbd2-48a5-9ec0-368a788438a7 +542156ce-9266-487c-85e6-91a00cdfb3ea Felipe Santos Pereira teste2@agenciapsi.com.br 16988280038 2026-03-28 12:43:28.632137+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f São Carlos 2001-10-05 922577885 55094969838 \N Masculino Casado(a) teste3@agenciapsi.com.br Brasil 13561-260 São Carlos SP Rua Conde do Pinhal 457 Centro Apartamento Superior incompleto Autônomo \N \N 16996005268 Instagram teste2 (2), teste \N \N \N \N f Próximo ao posto de saúde do centro. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-28 12:43:28.632137+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N Híbrido \N \N ele/dele Preta \N \N Telefone 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N 13h–18h teste ec7aa65f-cbd2-48a5-9ec0-368a788438a7 +fe670066-0d81-49ea-b177-61e83b455c59 Henrique Ferreira Ferreira 88 teste2@agenciapsi.com.br 16988280038 2026-03-28 12:18:31.642656+00 aaaaaaaa-0002-0002-0002-000000000002 http://127.0.0.1:54321/storage/v1/object/public/avatars/owners/aaaaaaaa-0002-0002-0002-000000000002/patients/fe670066-0d81-49ea-b177-61e83b455c59/avatar.png Ativo \N f São Carlos 1958-08-16 851689197 29628589709 \N Masculino União estável teste3@agenciapsi.com.br Brasil 13561-260 São Carlos SP Rua Conde do Pinhal 457 Centro Apartamento Mestrado Professora \N \N 16996005268 Site teste, teste2 (2) resposnavel 11111111111 11111111111 obs t Próximo ao posto de saúde do centro. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-29 12:18:54.15896+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N Híbrido \N teste ela/dela Amarela \N \N Telefone 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N 08h–12h teste ec7aa65f-cbd2-48a5-9ec0-368a788438a7 +\. + + +-- +-- Data for Name: billing_contracts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.billing_contracts (id, owner_id, tenant_id, patient_id, type, total_sessions, sessions_used, package_price, amount, billing_interval, active_from, active_to, status, created_at) FROM stdin; +\. + + +-- +-- Data for Name: determined_commitments; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.determined_commitments (id, tenant_id, created_by, is_native, native_key, is_locked, active, name, description, created_at, updated_at, bg_color, text_color) FROM stdin; +5944e5d9-622d-4db1-b73d-0083a6a4a9a1 bbbbbbbb-0002-0002-0002-000000000002 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +cd1076b1-0f3d-460e-842f-1585c0ae2a64 bbbbbbbb-0003-0003-0003-000000000003 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +b82819d4-02a1-40c5-bf57-6e2e0425e62d bbbbbbbb-0003-0003-0003-000000000003 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +2e5f4151-e259-4261-8954-e3d196a28383 bbbbbbbb-0004-0004-0004-000000000004 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +bec193c2-172b-40ef-a5fe-91b7c44c841e bbbbbbbb-0004-0004-0004-000000000004 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +b3bf589d-4a72-429f-b9bd-ba426c3f42f1 bbbbbbbb-0002-0002-0002-000000000002 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 16:44:21.537071+00 \N \N +793f5b1d-b218-40a3-aa73-3e1440d9ea66 bbbbbbbb-0005-0005-0005-000000000005 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +717e552c-99cc-4375-b7f2-a1b29f3581e3 bbbbbbbb-0002-0002-0002-000000000002 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 16:46:11.099434+00 \N \N +8c1a77a1-b66f-462e-940d-2f60d080149c bbbbbbbb-0005-0005-0005-000000000005 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +fc6da673-fd7c-4101-b980-54eb3842804c bbbbbbbb-0009-0009-0009-000000000009 \N t reading f t Leitura Praticar leitura 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +4c4a8ced-d109-443b-bc83-817c858f9bb6 bbbbbbbb-0009-0009-0009-000000000009 \N t class f f Aula Dar aula 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +fe52a6b2-e86e-47e0-9dc7-9b7e63599cff bbbbbbbb-0010-0010-0010-000000000010 \N t reading f t Leitura Praticar leitura 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +53193b8a-8af1-4824-a7c6-22cbb7459c45 bbbbbbbb-0010-0010-0010-000000000010 \N t class f f Aula Dar aula 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 bbbbbbbb-0002-0002-0002-000000000002 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 20:57:27.372517+00 \N \N +b567bfb6-7a58-4893-b072-eb23c72948a2 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t reading f t Leitura Praticar leitura 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +19072432-4eaf-411e-940f-c505639ec78a bbbbbbbb-0002-0002-0002-000000000002 \N f \N f t test teste 2026-03-24 19:42:19.588131+00 2026-03-24 19:42:19.588131+00 f97316 #ffffff +a4dcb5bf-86ab-4daa-80ca-357b88d7a6ba bbbbbbbb-0002-0002-0002-000000000002 \N f \N f t teste teste 2026-03-24 22:56:02.422706+00 2026-03-24 22:56:02.422706+00 6366f1 #ffffff +be219d8b-b2fb-4cfc-8910-64171cd7692f a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t session t t Sess??o Sess??o com paciente 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t supervision f t Supervis??o Supervis??o 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +6a3194d1-475a-4131-9d5f-d8a3bb4ad84e a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t class f f Aula Dar aula 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +fcd58365-0042-4df3-b93d-26cbae67d14a a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t analysis f t An??lise Pessoal Minha an??lise pessoal 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +65f1a136-ce11-46a0-b452-80f00132319d 1e98ca49-a46c-4701-847b-145a14d53d19 \N t session t t Sess??o Sess??o com paciente 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +b5bcbc9e-abf0-4ff3-8960-133c3592de88 1e98ca49-a46c-4701-847b-145a14d53d19 \N t reading f t Leitura Praticar leitura 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +8b0f4d71-f1d2-4c97-bd75-241aae919cfe 1e98ca49-a46c-4701-847b-145a14d53d19 \N t supervision f t Supervis??o Supervis??o 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 1e98ca49-a46c-4701-847b-145a14d53d19 \N t class f f Aula Dar aula 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +871f7d97-2d2c-485b-a631-6a4884e1c1e9 1e98ca49-a46c-4701-847b-145a14d53d19 \N t analysis f t An??lise Pessoal Minha an??lise pessoal 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +d2ea6a5e-e563-434b-950e-2d27bbf445ec bbbbbbbb-0003-0003-0003-000000000003 \N t session t t Sessão Sess??o com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +21f39315-5171-42ce-848c-129c1955206b bbbbbbbb-0004-0004-0004-000000000004 \N t session t t Sessão Sess??o com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +3276b032-6990-4bfc-942d-9155943c241e bbbbbbbb-0005-0005-0005-000000000005 \N t session t t Sessão Sess??o com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +fb194a3e-06ee-4704-a8cb-fe302dd2528e bbbbbbbb-0009-0009-0009-000000000009 \N t session t t Sessão Sess??o com paciente 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +1936c4af-5757-4c10-ad8f-8a5988287acc bbbbbbbb-0010-0010-0010-000000000010 \N t session t t Sessão Sess??o com paciente 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +42a8c681-8b32-4608-870f-b617acbe249e bbbbbbbb-0002-0002-0002-000000000002 \N t session t t Sessão Sessão com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 6366f1 #ffffff +9ded91f1-1974-4e56-afc6-12b2e8f9456c bbbbbbbb-0003-0003-0003-000000000003 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +0d6cb161-8d89-4b18-9323-5631e8cddd8f bbbbbbbb-0004-0004-0004-000000000004 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +83666a58-2e53-49a6-8b13-73eeb203e5ba bbbbbbbb-0005-0005-0005-000000000005 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +3dbb8c2f-6fab-478f-b094-5bc13606a504 bbbbbbbb-0009-0009-0009-000000000009 \N t supervision f t Supervisão Supervis??o 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +fb2724d6-3938-4b3f-b8ec-aeb4417a4057 bbbbbbbb-0010-0010-0010-000000000010 \N t supervision f t Supervisão Supervis??o 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +b5c43dc2-a3a1-40ca-8b75-02ec649fd914 bbbbbbbb-0003-0003-0003-000000000003 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 bbbbbbbb-0004-0004-0004-000000000004 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad bbbbbbbb-0005-0005-0005-000000000005 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +b180f6e4-39ad-464c-842e-d42dc60cdf13 bbbbbbbb-0009-0009-0009-000000000009 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +29b5f51c-0833-4902-8c03-ee8bb7836a33 bbbbbbbb-0010-0010-0010-000000000010 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +\. + + +-- +-- Data for Name: insurance_plan_services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.insurance_plan_services (id, insurance_plan_id, name, value, active, created_at, updated_at) FROM stdin; +b24c1a29-a5b3-4676-94b0-8effa89c672a ec7aa65f-cbd2-48a5-9ec0-368a788438a7 procedimento 1111.00 t 2026-03-24 12:21:29.140157+00 2026-03-24 12:21:29.140157+00 +\. + + +-- +-- Data for Name: recurrence_rules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.recurrence_rules (id, tenant_id, owner_id, therapist_id, patient_id, determined_commitment_id, type, "interval", weekdays, start_time, end_time, timezone, duration_min, start_date, end_date, max_occurrences, open_ended, modalidade, titulo_custom, observacoes, extra_fields, status, created_at, updated_at, price, insurance_plan_id, insurance_guide_number, insurance_value, insurance_plan_service_id) FROM stdin; +\. + + +-- +-- Data for Name: agenda_eventos; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_eventos (id, owner_id, tipo, status, titulo, observacoes, inicio_em, fim_em, created_at, updated_at, terapeuta_id, tenant_id, visibility_scope, mirror_of_event_id, mirror_source, patient_id, determined_commitment_id, link_online, titulo_custom, extra_fields, recurrence_id, recurrence_date, modalidade, price, billing_contract_id, billed, services_customized, insurance_plan_id, insurance_guide_number, insurance_value, insurance_plan_service_id) FROM stdin; +dbf6b44e-24f1-4cd2-86f5-a594fa39f9d3 aaaaaaaa-0002-0002-0002-000000000002 sessao agendado Otto Rank [Sess??o] \N 2026-03-23 13:00:00+00 2026-03-23 13:50:00+00 2026-03-23 11:33:08.0471+00 2026-03-23 11:33:08.0471+00 \N bbbbbbbb-0002-0002-0002-000000000002 public \N \N 6449e64b-050b-419f-8845-029b6f10a17d 42a8c681-8b32-4608-870f-b617acbe249e \N \N \N \N \N presencial 0.00 \N f f \N \N \N \N +\. + + +-- +-- Data for Name: agenda_excecoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_excecoes (id, owner_id, data, hora_inicio, hora_fim, tipo, motivo, created_at, updated_at, status, fonte, aplicavel_online, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agenda_online_slots; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_online_slots (id, owner_id, weekday, "time", enabled, created_at, updated_at, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agenda_regras_semanais; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_regras_semanais (id, owner_id, dia_semana, hora_inicio, hora_fim, modalidade, ativo, created_at, updated_at, tenant_id) FROM stdin; +a3212bcd-fc80-40c3-b3ea-92d31d1d051a aaaaaaaa-0002-0002-0002-000000000002 0 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +cbeae6bd-1ebd-494d-b5c0-2f3a8bf09e14 aaaaaaaa-0002-0002-0002-000000000002 1 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +dd9ecded-d82c-4d87-a2df-85b89c331001 aaaaaaaa-0002-0002-0002-000000000002 2 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +ce5054c2-f382-4135-8365-06b3dab7ea1c aaaaaaaa-0002-0002-0002-000000000002 3 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +e8e22a8d-4deb-4eeb-a9e8-e1f906030499 aaaaaaaa-0002-0002-0002-000000000002 4 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +265ff3ab-645f-4dd6-8a91-4a3a4d085ff0 aaaaaaaa-0002-0002-0002-000000000002 5 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +cf21b2ed-fbf6-4899-95b6-bee502e7b139 aaaaaaaa-0005-0005-0005-000000000005 0 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +a6fc9552-59dd-4717-9282-129d3297b5e0 aaaaaaaa-0005-0005-0005-000000000005 1 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +c028ea38-75a0-4e5f-a6c5-705264f43826 aaaaaaaa-0005-0005-0005-000000000005 2 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +c674e740-90b9-4ae1-8b9b-56ee6342235e aaaaaaaa-0005-0005-0005-000000000005 3 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +a272ac19-32d2-4c3c-b1a1-fbdedfffc6d3 aaaaaaaa-0005-0005-0005-000000000005 4 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +1a98514b-8508-4539-9e96-6af89b91417d aaaaaaaa-0005-0005-0005-000000000005 5 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +\. + + +-- +-- Data for Name: agenda_slots_bloqueados_semanais; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_slots_bloqueados_semanais (id, owner_id, dia_semana, hora_inicio, motivo, ativo, created_at, updated_at, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agenda_slots_regras; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_slots_regras (id, owner_id, dia_semana, passo_minutos, offset_minutos, buffer_antes_min, buffer_depois_min, min_antecedencia_horas, ativo, created_at, updated_at, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agendador_configuracoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agendador_configuracoes (owner_id, tenant_id, ativo, link_slug, imagem_fundo_url, imagem_header_url, logomarca_url, cor_primaria, nome_exibicao, endereco, botao_como_chegar_ativo, maps_url, modo_aprovacao, modalidade, tipos_habilitados, duracao_sessao_min, antecedencia_minima_horas, prazo_resposta_horas, reserva_horas, pagamento_obrigatorio, pix_chave, pix_countdown_minutos, triagem_motivo, triagem_como_conheceu, verificacao_email, exigir_aceite_lgpd, mensagem_boas_vindas, texto_como_se_preparar, texto_termos_lgpd, created_at, updated_at, pagamento_modo, pagamento_metodos_visiveis) FROM stdin; +\. + + +-- +-- Data for Name: agendador_solicitacoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agendador_solicitacoes (id, owner_id, tenant_id, paciente_nome, paciente_sobrenome, paciente_email, paciente_celular, paciente_cpf, tipo, modalidade, data_solicitada, hora_solicitada, reservado_ate, motivo, como_conheceu, pix_status, pix_pago_em, status, recusado_motivo, autorizado_em, autorizado_por, user_id, patient_id, evento_id, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.services (id, owner_id, tenant_id, name, description, price, duration_min, active, created_at, updated_at) FROM stdin; +2f2d31a7-abef-4efc-89fc-794a458682cb aaaaaaaa-0005-0005-0005-000000000005 bbbbbbbb-0005-0005-0005-000000000005 Atendimento padrão \N 0.00 50 t 2026-03-26 16:50:09.010706+00 2026-03-26 16:50:09.010706+00 +0166ac88-669d-4f0c-8769-17eb0029c8a9 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 Atendimento padrão \N 40.00 50 t 2026-03-26 21:48:12.84259+00 2026-03-26 21:50:44.411125+00 +045843e1-815a-437b-b11a-ea9136e10cdf aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste \N 1.00 50 t 2026-03-27 11:40:12.054774+00 2026-03-27 11:40:12.054774+00 +36cc58d9-7beb-451b-b630-e81271d45de4 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste \N 2.00 50 t 2026-03-27 13:03:35.570064+00 2026-03-27 13:03:35.570064+00 +\. + + +-- +-- Data for Name: commitment_services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.commitment_services (id, commitment_id, service_id, quantity, unit_price, discount_pct, discount_flat, final_price, created_at) FROM stdin; +\. + + +-- +-- Data for Name: commitment_time_logs; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.commitment_time_logs (id, tenant_id, commitment_id, calendar_event_id, source, started_at, ended_at, minutes, created_by, created_at) FROM stdin; +\. + + +-- +-- Data for Name: company_profiles; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.company_profiles (id, tenant_id, nome_fantasia, razao_social, tipo_empresa, cnpj, ie, im, cep, logradouro, numero, complemento, bairro, cidade, estado, email, telefone, site, logo_url, redes_sociais, created_at, updated_at) FROM stdin; +9c2039d4-0058-46d0-b60f-29e16459bb85 bbbbbbbb-0002-0002-0002-000000000002 teste \N consultorio \N \N \N 13561-260 Avenida Tancredo de Almeida Neves 457 complemento Parque Santa Mônica São Carlos SP comercial@gmail.com (11) 11111-1111 site http://127.0.0.1:54321/storage/v1/object/public/logos/bbbbbbbb-0002-0002-0002-000000000002/logo.png [{"url": "@perfil", "name": "Instagram"}] 2026-03-27 12:35:48.759848+00 2026-03-27 14:18:20.352667+00 +\. + + +-- +-- Data for Name: determined_commitment_fields; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.determined_commitment_fields (id, tenant_id, commitment_id, key, label, field_type, required, sort_order, created_at, updated_at) FROM stdin; +3f45731e-f025-4a95-b37d-35becf557474 bbbbbbbb-0002-0002-0002-000000000002 5944e5d9-622d-4db1-b73d-0083a6a4a9a1 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +7cb99c51-3de2-4216-be29-1067d1d2a6e8 bbbbbbbb-0002-0002-0002-000000000002 5944e5d9-622d-4db1-b73d-0083a6a4a9a1 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +29a48e4d-b4b9-41e6-8a3f-23e862b11f71 bbbbbbbb-0002-0002-0002-000000000002 5944e5d9-622d-4db1-b73d-0083a6a4a9a1 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +de1bf25c-2c6e-4de1-9be2-9976eb26e9e6 bbbbbbbb-0002-0002-0002-000000000002 717e552c-99cc-4375-b7f2-a1b29f3581e3 supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +0a2f4c96-be82-49a1-b0bc-65d08bf29078 bbbbbbbb-0002-0002-0002-000000000002 717e552c-99cc-4375-b7f2-a1b29f3581e3 topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +ea018b37-ac9a-49a5-93dd-ebc4f2bcc069 bbbbbbbb-0002-0002-0002-000000000002 717e552c-99cc-4375-b7f2-a1b29f3581e3 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4f4fff30-8b01-46cc-a6c9-25e0c623b03c bbbbbbbb-0002-0002-0002-000000000002 b3bf589d-4a72-429f-b9bd-ba426c3f42f1 theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +3ddb7185-eea6-43e4-bab7-686aa996f4e6 bbbbbbbb-0002-0002-0002-000000000002 b3bf589d-4a72-429f-b9bd-ba426c3f42f1 group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +20c208a1-eae7-4021-916f-a3a45f7d9e84 bbbbbbbb-0002-0002-0002-000000000002 b3bf589d-4a72-429f-b9bd-ba426c3f42f1 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +82c2b726-8de7-4bdd-a7ab-0fe519200f2c bbbbbbbb-0002-0002-0002-000000000002 e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +88bb598e-44f1-4090-a09a-21a11e628ab0 bbbbbbbb-0002-0002-0002-000000000002 e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +f0aa7c4c-e26d-42c2-8f90-5e4a87308fe9 bbbbbbbb-0002-0002-0002-000000000002 e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +fa8083ef-7e1f-4e00-84b6-6dd197a31e11 bbbbbbbb-0003-0003-0003-000000000003 cd1076b1-0f3d-460e-842f-1585c0ae2a64 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +2fea846d-3d7f-4946-8b8a-921756d1375d bbbbbbbb-0003-0003-0003-000000000003 cd1076b1-0f3d-460e-842f-1585c0ae2a64 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +7f71d073-7337-40c3-9f2b-80d359ee6825 bbbbbbbb-0003-0003-0003-000000000003 cd1076b1-0f3d-460e-842f-1585c0ae2a64 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +9ea25942-9529-4ffb-99b1-17c8f48fa1b3 bbbbbbbb-0003-0003-0003-000000000003 9ded91f1-1974-4e56-afc6-12b2e8f9456c supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +642ee453-276b-4636-808b-0deed8827416 bbbbbbbb-0003-0003-0003-000000000003 9ded91f1-1974-4e56-afc6-12b2e8f9456c topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +1107588b-dafc-49f0-adf1-1d478410fbab bbbbbbbb-0003-0003-0003-000000000003 9ded91f1-1974-4e56-afc6-12b2e8f9456c notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +2d00519b-2d66-4b66-9471-4edfd7f03152 bbbbbbbb-0003-0003-0003-000000000003 b82819d4-02a1-40c5-bf57-6e2e0425e62d theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +6fb9b8f8-0a50-4267-93ff-a154487d0ee2 bbbbbbbb-0003-0003-0003-000000000003 b82819d4-02a1-40c5-bf57-6e2e0425e62d group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +99d0df7d-20f7-49df-acd8-c359b195348d bbbbbbbb-0003-0003-0003-000000000003 b82819d4-02a1-40c5-bf57-6e2e0425e62d notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +241c3b0f-78c1-4485-9f11-c8fc5fdf6650 bbbbbbbb-0003-0003-0003-000000000003 b5c43dc2-a3a1-40ca-8b75-02ec649fd914 analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +08440a39-027f-4879-8091-7a9b2cf522c1 bbbbbbbb-0003-0003-0003-000000000003 b5c43dc2-a3a1-40ca-8b75-02ec649fd914 focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +d11f4029-c46e-42bf-b7aa-bc3b1cc9874a bbbbbbbb-0003-0003-0003-000000000003 b5c43dc2-a3a1-40ca-8b75-02ec649fd914 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +d2d11b9f-5dcf-4355-bc33-c977ac7591a9 bbbbbbbb-0004-0004-0004-000000000004 2e5f4151-e259-4261-8954-e3d196a28383 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +2d3e3c6e-6ca0-4170-b9ed-e45e470a8738 bbbbbbbb-0004-0004-0004-000000000004 2e5f4151-e259-4261-8954-e3d196a28383 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +a7003c31-46b1-44a2-876e-034f3c61ce8c bbbbbbbb-0004-0004-0004-000000000004 2e5f4151-e259-4261-8954-e3d196a28383 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +29f2cb01-22c2-45c3-ba02-55b908b5ceac bbbbbbbb-0004-0004-0004-000000000004 0d6cb161-8d89-4b18-9323-5631e8cddd8f supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +e0b8f4dd-740c-4162-b988-62fc8216de8c bbbbbbbb-0004-0004-0004-000000000004 0d6cb161-8d89-4b18-9323-5631e8cddd8f topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +187902e7-4497-4ab6-81f7-7dbe177189e9 bbbbbbbb-0004-0004-0004-000000000004 0d6cb161-8d89-4b18-9323-5631e8cddd8f notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4ef4ac8e-adf4-4034-851f-3767ef37a8e4 bbbbbbbb-0004-0004-0004-000000000004 bec193c2-172b-40ef-a5fe-91b7c44c841e theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4972b76d-c080-4989-a4f8-3d3c57a9b96e bbbbbbbb-0004-0004-0004-000000000004 bec193c2-172b-40ef-a5fe-91b7c44c841e group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +fc589056-f8b8-4d96-94f0-7ac49ab53059 bbbbbbbb-0004-0004-0004-000000000004 bec193c2-172b-40ef-a5fe-91b7c44c841e notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4300058b-c0b4-4b86-8f6e-eda6d99553fc bbbbbbbb-0004-0004-0004-000000000004 d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +20add80c-2807-42c9-8c42-40291a49e5df bbbbbbbb-0004-0004-0004-000000000004 d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4d01680a-733e-4c0c-a26a-e19b962fe9fa bbbbbbbb-0004-0004-0004-000000000004 d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +e56ad576-c831-444a-bb21-0bcd74630d48 bbbbbbbb-0005-0005-0005-000000000005 793f5b1d-b218-40a3-aa73-3e1440d9ea66 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +cc36d270-7961-481f-8a8b-99e0b9ef2e64 bbbbbbbb-0005-0005-0005-000000000005 793f5b1d-b218-40a3-aa73-3e1440d9ea66 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +8eb622a4-8ee9-4c34-af09-9dd5c74923fa bbbbbbbb-0005-0005-0005-000000000005 793f5b1d-b218-40a3-aa73-3e1440d9ea66 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +24d0c58f-3382-4ea8-81e7-3dc32f122b90 bbbbbbbb-0005-0005-0005-000000000005 83666a58-2e53-49a6-8b13-73eeb203e5ba supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4993e870-d101-4754-b2bc-18205255d643 bbbbbbbb-0005-0005-0005-000000000005 83666a58-2e53-49a6-8b13-73eeb203e5ba topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +b6e403de-e93a-42e0-832a-e6103a935421 bbbbbbbb-0005-0005-0005-000000000005 83666a58-2e53-49a6-8b13-73eeb203e5ba notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +a552127c-1b16-464c-a8f3-d7bdf054b0a9 bbbbbbbb-0005-0005-0005-000000000005 8c1a77a1-b66f-462e-940d-2f60d080149c theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +be9ecf92-2dc8-4f62-94f3-223b0165c0af bbbbbbbb-0005-0005-0005-000000000005 8c1a77a1-b66f-462e-940d-2f60d080149c group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +7f7a9b35-60f1-4192-bf6c-d91ca42813a1 bbbbbbbb-0005-0005-0005-000000000005 8c1a77a1-b66f-462e-940d-2f60d080149c notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +528d426f-23d4-422f-8624-5b4178ce3a8b bbbbbbbb-0005-0005-0005-000000000005 a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +11a02a30-7a0a-4baf-acdd-2525c2f2cf84 bbbbbbbb-0005-0005-0005-000000000005 a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +f456679b-05d7-4ccc-ae3b-c5f673f8f6f8 bbbbbbbb-0005-0005-0005-000000000005 a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +07ba5c18-9ebb-4b38-bd2f-591b8e4f7f87 bbbbbbbb-0009-0009-0009-000000000009 fc6da673-fd7c-4101-b980-54eb3842804c book Livro text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +92eec7d8-20f3-46e4-8700-c80e9bd90bcb bbbbbbbb-0009-0009-0009-000000000009 fc6da673-fd7c-4101-b980-54eb3842804c author Autor text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +fdf6b850-b627-494e-9cb0-d1a044409b3c bbbbbbbb-0009-0009-0009-000000000009 fc6da673-fd7c-4101-b980-54eb3842804c notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +ef9a9605-9b0f-4671-b056-835c56844cf9 bbbbbbbb-0009-0009-0009-000000000009 3dbb8c2f-6fab-478f-b094-5bc13606a504 supervisor Supervisor text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e19f0ad4-fe52-4595-84aa-cf11828a7ecd bbbbbbbb-0009-0009-0009-000000000009 3dbb8c2f-6fab-478f-b094-5bc13606a504 topic Assunto text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +41a085ac-6849-4e29-8728-79e1a47c1ac5 bbbbbbbb-0009-0009-0009-000000000009 3dbb8c2f-6fab-478f-b094-5bc13606a504 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +8fdaad8b-f31c-4510-9b72-7346026103b9 bbbbbbbb-0009-0009-0009-000000000009 4c4a8ced-d109-443b-bc83-817c858f9bb6 theme Tema text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +9e53f6f5-6770-4f3e-8b0e-cb18c9df54b7 bbbbbbbb-0009-0009-0009-000000000009 4c4a8ced-d109-443b-bc83-817c858f9bb6 group Turma text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +222021b7-6917-4977-a08f-e19532aa2a0e bbbbbbbb-0009-0009-0009-000000000009 4c4a8ced-d109-443b-bc83-817c858f9bb6 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +329eebff-55c4-49e2-87ee-b3d0f418234b bbbbbbbb-0009-0009-0009-000000000009 b180f6e4-39ad-464c-842e-d42dc60cdf13 analyst Analista text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e87c45d8-8f5f-476d-811d-5b5b4a764e07 bbbbbbbb-0009-0009-0009-000000000009 b180f6e4-39ad-464c-842e-d42dc60cdf13 focus Foco text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +2a8c3eff-4eed-4b2d-98e7-50267ebb910e bbbbbbbb-0009-0009-0009-000000000009 b180f6e4-39ad-464c-842e-d42dc60cdf13 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e6109ef3-7409-4268-8ecf-eaff64fa9498 bbbbbbbb-0010-0010-0010-000000000010 fe52a6b2-e86e-47e0-9dc7-9b7e63599cff book Livro text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +2069cb4c-3c49-4a82-ada1-63082d14bba3 bbbbbbbb-0010-0010-0010-000000000010 fe52a6b2-e86e-47e0-9dc7-9b7e63599cff author Autor text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +d8428a90-f534-4794-99a7-1c752d42e0d2 bbbbbbbb-0010-0010-0010-000000000010 fe52a6b2-e86e-47e0-9dc7-9b7e63599cff notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +69435311-e703-4fbb-84b8-d634fd0148fe bbbbbbbb-0010-0010-0010-000000000010 fb2724d6-3938-4b3f-b8ec-aeb4417a4057 supervisor Supervisor text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +2c71883c-dee5-420e-9c7a-d1b1ade908f5 bbbbbbbb-0010-0010-0010-000000000010 fb2724d6-3938-4b3f-b8ec-aeb4417a4057 topic Assunto text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +1cf750d0-449d-4780-8828-bffce53cc651 bbbbbbbb-0010-0010-0010-000000000010 fb2724d6-3938-4b3f-b8ec-aeb4417a4057 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e38226ca-3af4-41c3-a901-9edfc0814e46 bbbbbbbb-0010-0010-0010-000000000010 53193b8a-8af1-4824-a7c6-22cbb7459c45 theme Tema text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +6100b0b2-db5c-4aeb-98e3-3c6f6a20f058 bbbbbbbb-0010-0010-0010-000000000010 53193b8a-8af1-4824-a7c6-22cbb7459c45 group Turma text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +ef948cc1-4e2b-4c7a-9043-956a6341085a bbbbbbbb-0010-0010-0010-000000000010 53193b8a-8af1-4824-a7c6-22cbb7459c45 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +9405625b-f781-4b5d-836c-19371fccefab bbbbbbbb-0010-0010-0010-000000000010 29b5f51c-0833-4902-8c03-ee8bb7836a33 analyst Analista text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +cc414a63-c433-4269-be6c-caad9f83408d bbbbbbbb-0010-0010-0010-000000000010 29b5f51c-0833-4902-8c03-ee8bb7836a33 focus Foco text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +c5c4f4c9-2e74-4f65-b5d9-ee1121005cbc bbbbbbbb-0010-0010-0010-000000000010 29b5f51c-0833-4902-8c03-ee8bb7836a33 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +155f3805-15e9-48fe-9f36-454e589ca2b2 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b567bfb6-7a58-4893-b072-eb23c72948a2 book Livro text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +95283550-683a-4c3d-a7d7-ac66f3b1f3e5 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b567bfb6-7a58-4893-b072-eb23c72948a2 author Autor text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +f293f578-4b69-46e9-b652-9eb535ae904b a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b567bfb6-7a58-4893-b072-eb23c72948a2 notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +9fa8bb78-eed2-4d10-9cab-e527c19137d8 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 supervisor Supervisor text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +972ac699-e2aa-4312-a0c9-1faa8853e38d a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 topic Assunto text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +f70a4cbb-b55a-4661-94de-d322f80f78a7 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +bc4036a8-32c2-4e13-bfde-0a85e21016f8 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 6a3194d1-475a-4131-9d5f-d8a3bb4ad84e theme Tema text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +4a5acde2-4d27-4377-8cf7-f94c25b2b13a a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 6a3194d1-475a-4131-9d5f-d8a3bb4ad84e group Turma text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +fe89c629-de7b-4dfe-b5b4-00954cf5ef4e a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 6a3194d1-475a-4131-9d5f-d8a3bb4ad84e notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +60192c88-c9ef-4650-8591-104e02f22460 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 fcd58365-0042-4df3-b93d-26cbae67d14a analyst Analista text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +dd49a6ca-755e-4eb2-b4e8-9c17393c2e77 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 fcd58365-0042-4df3-b93d-26cbae67d14a focus Foco text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +d2e9d95a-be44-4bde-a853-0e516345421b a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 fcd58365-0042-4df3-b93d-26cbae67d14a notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +c0206c42-b9c0-4fc4-afc1-22a6178750e2 1e98ca49-a46c-4701-847b-145a14d53d19 b5bcbc9e-abf0-4ff3-8960-133c3592de88 book Livro text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +23f30e08-728e-460d-9410-cb1bb24548ce 1e98ca49-a46c-4701-847b-145a14d53d19 b5bcbc9e-abf0-4ff3-8960-133c3592de88 author Autor text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +5406a62c-430a-4959-b0c5-cca804b03f53 1e98ca49-a46c-4701-847b-145a14d53d19 b5bcbc9e-abf0-4ff3-8960-133c3592de88 notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +1248cce1-2f5e-4e16-a193-503d374408f0 1e98ca49-a46c-4701-847b-145a14d53d19 8b0f4d71-f1d2-4c97-bd75-241aae919cfe supervisor Supervisor text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +cb9c6678-be1b-474a-b5a8-f4aaaff51213 1e98ca49-a46c-4701-847b-145a14d53d19 8b0f4d71-f1d2-4c97-bd75-241aae919cfe topic Assunto text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +b1ade16e-28f0-4b3e-b25e-7dcc6c338f7f 1e98ca49-a46c-4701-847b-145a14d53d19 8b0f4d71-f1d2-4c97-bd75-241aae919cfe notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +fd21821a-58db-40a6-8d2c-54e8bf8dfe45 1e98ca49-a46c-4701-847b-145a14d53d19 e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 theme Tema text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +757a5af1-e35e-4f70-a81c-89295d85979e 1e98ca49-a46c-4701-847b-145a14d53d19 e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 group Turma text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +198896b3-8181-428b-9022-938587ac9db7 1e98ca49-a46c-4701-847b-145a14d53d19 e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +ca55ceb1-a1d4-48ee-b82d-d767dac745c6 1e98ca49-a46c-4701-847b-145a14d53d19 871f7d97-2d2c-485b-a631-6a4884e1c1e9 analyst Analista text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +21832e62-718c-405a-b602-1e725330ccb3 1e98ca49-a46c-4701-847b-145a14d53d19 871f7d97-2d2c-485b-a631-6a4884e1c1e9 focus Foco text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +cad4c61b-482c-4d8c-adf1-9bf08d04f90a 1e98ca49-a46c-4701-847b-145a14d53d19 871f7d97-2d2c-485b-a631-6a4884e1c1e9 notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +\. + + +-- +-- Data for Name: dev_user_credentials; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.dev_user_credentials (id, user_id, email, password_dev, kind, note, created_at) FROM stdin; +\. + + +-- +-- Data for Name: email_layout_config; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.email_layout_config (id, tenant_id, header_config, footer_config, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: email_templates_global; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.email_templates_global (id, key, domain, channel, subject, body_html, body_text, version, is_active, variables, created_at, updated_at) FROM stdin; +be1e52ec-0c1d-4cbe-97da-9c47ce052631 session.reminder.email session email Lembrete: sua sessão amanhã às {{session_time}} \n

Olá, {{patient_name}}!

\n

Este é um lembrete da sua sessão agendada para {{session_date}} às {{session_time}}.

\n

Modalidade: {{session_modality}}

\n {{#if session_link}}\n

Clique aqui para entrar na sessão online

\n {{/if}}\n

Em caso de necessidade de cancelamento, entre em contato com antecedência.

\n

Até logo,
{{therapist_name}}

\n Olá, {{patient_name}}! Lembrete da sua sessão: {{session_date}} às {{session_time}} ({{session_modality}}). 2 t {"patient_name": "Nome completo do paciente", "session_date": "Data da sessão (ex: 20/03/2026)", "session_link": "Link da videochamada (apenas online)", "session_time": "Horário da sessão (ex: 14:00)", "therapist_name": "Nome do terapeuta", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +bf956245-c79a-4ed0-86f1-e0be57072cc8 session.confirmation.email session email Sessão confirmada — {{session_date}} às {{session_time}} \n

Olá, {{patient_name}}!

\n

Sua sessão foi confirmada com sucesso.

\n
    \n
  • Data: {{session_date}}
  • \n
  • Horário: {{session_time}}
  • \n
  • Modalidade: {{session_modality}}
  • \n {{#if session_address}}
  • Local: {{session_address}}
  • {{/if}}\n
\n

Até lá,
{{therapist_name}}

\n Sessão confirmada: {{session_date}} às {{session_time}} ({{session_modality}}). 1 t {"patient_name": "Nome do paciente", "session_date": "Data da sessão", "session_time": "Horário da sessão", "therapist_name": "Nome do terapeuta", "session_address": "Endereço (apenas presencial)", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +9039f35e-e31f-4bad-a6a5-2b865f1152b3 session.cancellation.email session email Sessão cancelada — {{session_date}} \n

Olá, {{patient_name}}!

\n

Informamos que sua sessão do dia {{session_date}} às {{session_time}} foi cancelada.

\n {{#if cancellation_reason}}

Motivo: {{cancellation_reason}}

{{/if}}\n

Entre em contato para reagendar.

\n

{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Data cancelada", "session_time": "Horário cancelado", "therapist_name": "Nome do terapeuta", "cancellation_reason": "Motivo do cancelamento (opcional)"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +ff36dccc-6fe1-4391-9eac-bd03927b38f5 session.rescheduled.email session email Sessão reagendada — {{session_date}} às {{session_time}} \n

Olá, {{patient_name}}!

\n

Sua sessão foi reagendada para {{session_date}} às {{session_time}}.

\n

Modalidade: {{session_modality}}

\n

Até lá,
{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Nova data", "session_time": "Novo horário", "therapist_name": "Nome do terapeuta", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +f17b10f8-d811-4f83-bf5e-3ed636262f28 intake.received.email intake email Recebemos seu cadastro — {{clinic_name}} \n

Olá, {{patient_name}}!

\n

Recebemos seu cadastro com sucesso. Nossa equipe entrará em contato em breve para dar continuidade ao processo.

\n

Obrigado pela confiança,
{{clinic_name}}

\n \N 1 t {"clinic_name": "Nome da clínica ou terapeuta", "patient_name": "Nome do solicitante"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +5b4a9a8b-bc92-4030-92ec-acbb6f9e7ee0 intake.approved.email intake email Cadastro aprovado — bem-vindo(a)! \n

Olá, {{patient_name}}!

\n

Seu cadastro foi aprovado. Você já pode acessar o portal e agendar sua primeira sessão.

\n

Acessar portal →

\n

Qualquer dúvida, estamos à disposição.
{{therapist_name}}

\n \N 1 t {"portal_link": "Link do portal do paciente", "patient_name": "Nome do paciente", "therapist_name": "Nome do terapeuta"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +04da6880-708a-4018-9b1f-0f398d152e0c intake.rejected.email intake email Atualização sobre seu cadastro — {{clinic_name}} \n

Olá, {{patient_name}}!

\n

Agradecemos seu interesse. Infelizmente não será possível dar continuidade ao seu cadastro no momento.

\n {{#if rejection_reason}}

{{rejection_reason}}

{{/if}}\n

{{clinic_name}}

\n \N 1 t {"clinic_name": "Nome da clínica", "patient_name": "Nome do paciente", "rejection_reason": "Motivo (opcional)"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +3e9ae66a-598c-4763-ab61-f02bb71323a4 scheduler.request_accepted.email session email Sua solicitação foi aceita — {{session_date}} às {{session_time}} \n

Olá, {{patient_name}}!

\n

Sua solicitação de agendamento foi aceita.

\n
    \n
  • Data: {{session_date}}
  • \n
  • Horário: {{session_time}}
  • \n
  • Tipo: {{session_type}}
  • \n
  • Modalidade: {{session_modality}}
  • \n
\n

Até logo,
{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Data confirmada", "session_time": "Horário confirmado", "session_type": "Primeira consulta / Retorno", "therapist_name": "Nome do terapeuta", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +7c7f736d-1ddb-46ba-ab41-f66d2267b730 scheduler.request_rejected.email session email Atualização sobre sua solicitação de agendamento \n

Olá, {{patient_name}}!

\n

Infelizmente não foi possível confirmar sua solicitação de agendamento para {{session_date}}.

\n {{#if rejection_reason}}

Motivo: {{rejection_reason}}

{{/if}}\n

Entre em contato para verificar outros horários disponíveis.

\n

{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Data solicitada", "therapist_name": "Nome do terapeuta", "rejection_reason": "Motivo (opcional)"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +78a33f29-3428-482f-9b0e-362c69ff20a8 system.welcome.email system email Bem-vindo(a) ao {{clinic_name}}! \n

Olá, {{patient_name}}!

\n

Seja bem-vindo(a)! Sua conta foi criada com sucesso.

\n

Acessar minha área →

\n \N 1 t {"clinic_name": "Nome da clínica", "portal_link": "Link do portal", "patient_name": "Nome do paciente"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +21a3fab3-01af-4b16-bd63-50929be5001d system.password_reset.email system email Redefinição de senha \n

Olá, {{patient_name}}!

\n

Recebemos uma solicitação para redefinir sua senha.

\n

Clique aqui para redefinir sua senha →

\n

Se você não solicitou a redefinição, ignore este e-mail.

\n \N 1 t {"reset_link": "Link de redefinição de senha", "patient_name": "Nome do usuário"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +\. + + +-- +-- Data for Name: email_templates_tenant; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.email_templates_tenant (id, tenant_id, owner_id, template_key, subject, body_html, body_text, enabled, synced_version, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: entitlements_invalidation; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.entitlements_invalidation (owner_id, changed_at) FROM stdin; +aaaaaaaa-0002-0002-0002-000000000002 2026-03-27 09:41:23.50964+00 +\. + + +-- +-- Data for Name: features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.features (id, key, description, created_at, descricao, name) FROM stdin; +5e539124-630f-4c2a-a9de-7999317085e6 agenda.view Visualização da agenda 2026-02-21 02:36:01.562728+00 Visualizar agenda Agenda - Visualizar +a74fef14-c9d9-4884-ba45-f81c60e0783a agenda.manage Gerenciamento completo da agenda 2026-02-21 02:35:50.629667+00 Adicionar Compromissos na agenda Agenda - Gerenciar +a56482a1-0787-49da-90a7-e1857488734a patients Módulo de pacientes 2026-03-02 12:35:19.955748+00 Pacientes Pacientes +57f631a1-9ebe-480b-a2cb-144ad32ff5f0 patients.view Visualização de pacientes 2026-02-28 11:15:14.275572+00 Visualizar pacientes Pacientes - Visualizar +4e5bc50b-e339-42fe-9d91-61e8555f83e7 patients.manage Gerenciamento completo de pacientes 2026-02-28 11:15:14.275572+00 Gerenciar pacientes Pacientes - Gerenciar +53a48c3b-0617-4618-adf8-f3a255c51ee4 online_scheduling Sistema de agendamento online 2026-03-01 09:59:15.432733+00 Agendamento online Agendamento Online +5739aa27-b089-4b15-b149-31b13d768825 online_scheduling.manage Gerenciamento do agendamento online 2026-02-15 21:50:02.056357+00 Gerenciar agendamento online (admin) Agendamento Online - Gerenciar +0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 online_scheduling.public Página pública do agendador 2026-02-15 21:50:02.056357+00 Página pública de agendamento Agendamento Online - Público +f5d66212-fd73-4472-a306-07928e5deaec reminders Sistema de lembretes automáticos 2026-03-01 09:59:15.432733+00 Lembretes Lembretes +b3efa25d-60a4-4974-8153-6ec098b3d477 reports_basic Relatórios básicos 2026-03-01 09:59:15.432733+00 Relatórios básicos Relatórios Básicos +bf133ad1-da8e-4ea9-bd66-21901cb50075 reports_advanced Relatórios avançados com exportação 2026-03-01 09:59:15.432733+00 Relatórios avançados Relatórios Avançados +336aeeba-b18e-4e68-8303-d42ba09f4b20 secretary Funcionalidade de secretária 2026-03-01 09:59:15.432733+00 Secretaria Secretária +30c9cdd5-7c8c-44d9-8c0b-614165bb9496 shared_reception Recepção compartilhada entre terapeutas 2026-03-02 12:35:19.955748+00 Recepção / Secretária Recepção Compartilhada +74fc1321-4d17-49c3-b72e-db3a7f4be451 rooms Gerenciamento de salas 2026-03-02 12:35:19.955748+00 Salas / Coworking Salas +c109ad27-0edf-4774-91a7-94dac4faab49 intake_public Formulário de intake público 2026-03-02 12:35:19.955748+00 Link externo de cadastro Intake Público +90e92108-8124-40ee-88a0-f0ecafb57d76 intakes_pro Funcionalidades avançadas de intake 2026-02-15 23:29:55.845638+00 Formulários PRO Intakes PRO +f393178c-284d-422f-b096-8793f85428d5 custom_branding Personalização de marca 2026-03-01 09:59:15.432733+00 Personalização de marca Branding Personalizado +d6f54674-ea8b-484b-af0e-99127a510da2 api_access Acesso via API 2026-03-01 09:59:15.432733+00 Integrações/API Acesso API +a5593d96-dd95-46bb-bef0-bd379b56ad50 audit_log Log de auditoria completo 2026-03-01 09:59:15.432733+00 Auditoria Log de Auditoria +8cc81988-d02a-4542-9cb2-ce2ed7c18d60 sms_reminder Lembretes via SMS 2026-02-15 23:29:55.845638+00 Lembretes por SMS Lembrete SMS +9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 clinic_calendar Visão consolidada do calendário 2026-03-01 09:59:15.432733+00 Agenda da clínica Calendário da Clínica +a830e45b-3bb4-4b17-812d-fe83777a2377 advanced_reports Relatórios avançados da clínica 2026-02-15 23:29:55.845638+00 Relatórios avançados Relatórios Avançados (Clínica) +9ab8bdbb-838b-4946-aa5d-fd9cfdd257b3 supervisor.access Acesso ao módulo de supervisão 2026-03-05 00:58:17.218326+00 Acesso básico ao espaço de supervisão (sala, lista de supervisionados). Supervisor - Acesso +1167b54a-0e93-43a2-94d7-c12e64eb56de supervisor.invite Convidar supervisionados 2026-03-05 00:58:17.218326+00 Permite convidar terapeutas para participar da sala de supervisão. Supervisor - Convidar +761e4495-b46a-4791-9519-86ffe48dc47f supervisor.sessions Gerenciar sessões de supervisão 2026-03-05 00:58:17.218326+00 Agendamento e registro de sessões de supervisão. Supervisor - Sessões +7e82ee01-44f6-4b3f-9861-840c58e13f58 supervisor.reports Relatórios de supervisão 2026-03-05 00:58:17.218326+00 Relatórios avançados de progresso e evolução dos supervisionados. Supervisor - Relatórios +\. + + +-- +-- Data for Name: feriados; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.feriados (id, tenant_id, owner_id, tipo, nome, data, cidade, estado, observacao, bloqueia_sessoes, criado_em) FROM stdin; +\. + + +-- +-- Data for Name: financial_categories; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.financial_categories (id, user_id, name, type, color, icon, sort_order, created_at) FROM stdin; +\. + + +-- +-- Data for Name: financial_exceptions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.financial_exceptions (id, owner_id, tenant_id, exception_type, charge_mode, charge_value, charge_pct, min_hours_notice, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: financial_records; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.financial_records (id, owner_id, tenant_id, type, amount, description, category, payment_method, paid_at, due_date, installments, installment_number, installment_group, agenda_evento_id, patient_id, clinic_fee_pct, clinic_fee_amount, insurance_plan_id, notes, tags, created_at, updated_at, deleted_at, discount_amount, final_amount, status, category_id) FROM stdin; +d51c8380-812a-45a7-8154-c799f4f95723 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 receita 1.00 \N \N \N \N 2026-03-25 1 1 \N \N 76cec0bb-1e63-45bf-9b03-feaaa2a5d18d 0.00 0.00 \N \N \N 2026-03-24 22:57:09.509577+00 2026-03-24 22:57:09.509577+00 \N 0.00 1.00 pending \N +\. + + +-- +-- Data for Name: global_notices; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.global_notices (id, title, message, variant, roles, contexts, starts_at, ends_at, is_active, priority, dismissible, persist_dismiss, dismiss_scope, show_once, max_views, cooldown_minutes, version, action_type, action_label, action_url, action_route, views_count, clicks_count, created_at, updated_at, created_by, content_align, link_target) FROM stdin; +\. + + +-- +-- Data for Name: login_carousel_slides; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.login_carousel_slides (id, title, body, icon, ordem, ativo, created_at, updated_at) FROM stdin; +0af63b3a-1819-4384-bf94-b29cbe84aca3 Gestão clínica simplificada Agendamentos, prontuários e sessões em um único painel. Foco no que importa: seus pacientes. pi-calendar-clock 0 t 2026-03-23 14:18:08.414378+00 2026-03-23 14:18:08.414378+00 +c02309d3-85cf-452f-bb85-363889aea9f3 Gestão clínica simplificada Gerencie agenda, pacientes e financeiro em um só lugar. Simples, rápido e seguro. pi-users 1 t 2026-03-23 14:18:08.414378+00 2026-03-23 14:18:08.414378+00 +763a600e-987c-4e42-8f62-29b5dea59c39 Múltiplos profissionais, uma só plataforma Ideal para clínicas com vários terapeutas. Cada profissional com sua agenda e seus pacientes. pi-shield 2 t 2026-03-23 14:18:08.414378+00 2026-03-23 14:18:08.414378+00 +\. + + +-- +-- Data for Name: medicos; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.medicos (id, owner_id, tenant_id, nome, crm, especialidade, telefone_profissional, telefone_pessoal, email, clinica, cidade, estado, observacoes, ativo, created_at, updated_at) FROM stdin; +135fa2c3-64f5-4268-af93-b8cd7fe96a0f aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste \N teste 11111111111 11111111111 teste \N \N SP \N t 2026-03-28 11:35:32.106083+00 2026-03-28 11:35:32.106083+00 +6e77083f-107b-48a0-bfa7-255078ffc15a aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste2 2 \N \N \N \N \N \N SP \N t 2026-03-28 12:21:30.879819+00 2026-03-28 12:21:30.879819+00 +\. + + +-- +-- Data for Name: modules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.modules (id, key, name, description, is_active, created_at) FROM stdin; +\. + + +-- +-- Data for Name: module_features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.module_features (module_id, feature_id, enabled, limits, created_at) FROM stdin; +\. + + +-- +-- Data for Name: notice_dismissals; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notice_dismissals (id, notice_id, user_id, version, dismissed_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_channels; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_channels (id, tenant_id, owner_id, channel, provider, is_active, display_name, sender_address, credentials, connection_status, last_health_check, metadata, created_at, updated_at, deleted_at, twilio_subaccount_sid, twilio_phone_number, twilio_phone_sid, webhook_url, cost_per_message_usd, price_per_message_brl, provisioned_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_queue; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_queue (id, tenant_id, owner_id, agenda_evento_id, patient_id, channel, template_key, schedule_key, resolved_vars, recipient_address, status, scheduled_at, sent_at, next_retry_at, attempts, max_attempts, last_error, idempotency_key, provider_message_id, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_logs; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_logs (id, tenant_id, owner_id, queue_id, agenda_evento_id, patient_id, channel, template_key, schedule_key, recipient_address, resolved_message, resolved_vars, status, provider, provider_message_id, provider_status, provider_response, sent_at, delivered_at, read_at, failed_at, failure_reason, estimated_cost_brl, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_preferences; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_preferences (id, tenant_id, owner_id, patient_id, whatsapp_opt_in, email_opt_in, sms_opt_in, preferred_time_start, preferred_time_end, lgpd_consent_given, lgpd_consent_date, lgpd_consent_version, lgpd_consent_ip, lgpd_opt_out_date, lgpd_opt_out_reason, created_at, updated_at, deleted_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_schedules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_schedules (id, tenant_id, owner_id, schedule_key, event_type, trigger_type, offset_minutes, whatsapp_enabled, email_enabled, sms_enabled, allowed_time_start, allowed_time_end, skip_weekends, skip_holidays, is_active, sort_order, created_at, updated_at, deleted_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_templates; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_templates (id, tenant_id, owner_id, key, domain, channel, event_type, body_text, meta_template_name, meta_template_namespace, meta_components, meta_status, variables, version, is_active, is_default, created_at, updated_at, deleted_at) FROM stdin; +37311b1a-2919-4f38-8dbc-7ccf81942062 \N \N session.reminder.whatsapp session whatsapp lembrete_sessao Olá {{nome_paciente}}! 👋\n\nLembrete: você tem sessão agendada para *{{data_sessao}}* às *{{hora_sessao}}* com {{nome_terapeuta}}.\n\n📋 {{modalidade}}\n\nPara confirmar, responda ✅\nPara cancelar, responda ❌\n\nSe precisar remarcar, entre em contato.\n\n_Responda SAIR para não receber mais lembretes._ \N \N \N draft ["nome_paciente", "data_sessao", "hora_sessao", "nome_terapeuta", "modalidade"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +4e61a64d-b2f9-49de-9d27-6496ddba8aef \N \N session.confirmation.whatsapp session whatsapp confirmacao_sessao ✅ Sessão confirmada!\n\nOlá {{nome_paciente}}, sua sessão com {{nome_terapeuta}} foi confirmada:\n\n📅 {{data_sessao}} às {{hora_sessao}}\n📋 {{modalidade}}\n\nTe esperamos! \N \N \N draft ["nome_paciente", "data_sessao", "hora_sessao", "nome_terapeuta", "modalidade"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +262e9dba-0324-478f-9d76-3faba555ec3c \N \N session.cancellation.whatsapp session whatsapp cancelamento_sessao Olá {{nome_paciente}},\n\nSua sessão de {{data_sessao}} às {{hora_sessao}} com {{nome_terapeuta}} foi *cancelada*.\n\nSe desejar reagendar, entre em contato.\n\nAtenciosamente,\n{{nome_terapeuta}} \N \N \N draft ["nome_paciente", "data_sessao", "hora_sessao", "nome_terapeuta"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +2c5331f9-6728-4cea-b4cc-e9b2659f5362 \N \N session.reminder.sms session sms lembrete_sessao Lembrete: sessao em {{data_sessao}} as {{hora_sessao}} com {{nome_terapeuta}}. Confirme respondendo OK. \N \N \N draft ["data_sessao", "hora_sessao", "nome_terapeuta"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +3e4386ba-2790-41e8-9b92-3b6beab549d8 \N \N session.lembrete.whatsapp session whatsapp lembrete_sessao Olá, {{nome_paciente}}! 👋\n\nLembrete da sua sessão com {{nome_terapeuta}}.\n\n📅 Data: {{data_sessao}}\n🕐 Hora: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nQualquer dúvida, entre em contato. Até lá! 💚 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +eb218e05-4ea0-4884-acd2-a766d3986d04 \N \N session.lembrete_2h.whatsapp session whatsapp lembrete_sessao Olá, {{nome_paciente}}! Sua sessão com {{nome_terapeuta}} começa em 2 horas.\n\n🕐 Hora: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nAté já! 😊 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +4574eb97-aeda-4cbb-a7e9-243c8f8c0b17 \N \N session.confirmacao.whatsapp session whatsapp confirmacao_sessao Olá, {{nome_paciente}}! ✅\n\nSua sessão foi confirmada!\n\n📅 Data: {{data_sessao}}\n🕐 Hora: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nAté lá! 💚 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade", "link_confirmacao"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +b321696c-96e2-4d16-8f8d-aa46d98945b8 \N \N session.cancelamento.whatsapp session whatsapp cancelamento_sessao Olá, {{nome_paciente}}. Infelizmente a sessão do dia {{data_sessao}} às {{hora_sessao}} foi cancelada.\n\nEntre em contato para reagendar. 🙏 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +216c6404-6e6c-4cb6-b5bd-486c8abc3c48 \N \N session.reagendamento.whatsapp session whatsapp reagendamento Olá, {{nome_paciente}}! Sua sessão foi reagendada.\n\n📅 Nova data: {{data_sessao}}\n🕐 Novo horário: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nAté lá! 😊 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +48a93a15-9675-49a7-a2d7-3b9b589e14f5 \N \N cobranca.pendente.whatsapp billing whatsapp cobranca_pendente Olá, {{nome_paciente}}. Identificamos uma cobrança pendente no valor de {{valor_sessao}} referente à sua sessão do dia {{data_sessao}}.\n\nPor favor, entre em contato para regularizar. 🙏 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade", "valor_sessao"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +26edef88-2d45-4ee2-8496-30f76f35cb77 \N \N sistema.boas_vindas.whatsapp system whatsapp boas_vindas_paciente Olá, {{nome_paciente}}! 🎉\n\nSeja muito bem-vindo(a)! Estamos felizes em ter você aqui.\n\nEm caso de dúvidas, estamos à disposição. Até a nossa primeira sessão! 💚 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +e66ef5f5-ab3a-46c1-8446-25b55487a017 \N \N session.confirmation.sms session sms confirmacao_sessao Sessão confirmada! {{session_date}} às {{session_time}} ({{session_modality}}) com {{therapist_name}}. \N \N \N draft ["patient_name", "session_date", "session_time", "session_modality", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +aae09094-82cb-406f-8015-c42cd24afded \N \N session.cancellation.sms session sms cancelamento_sessao Sua sessão de {{session_date}} às {{session_time}} foi cancelada. Entre em contato para reagendar. — {{therapist_name}} \N \N \N draft ["patient_name", "session_date", "session_time", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +c8521ea4-31c0-4015-8923-7ef57b3f8b47 \N \N session.rescheduled.sms session sms reagendamento Sua sessão foi reagendada para {{session_date}} às {{session_time}} ({{session_modality}}). — {{therapist_name}} \N \N \N draft ["patient_name", "session_date", "session_time", "session_modality", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +ba32ee4e-6548-48de-bf83-b0cb11a00761 \N \N intake.received.sms intake sms intake_recebido Olá {{patient_name}}, recebemos seu cadastro. Em breve entraremos em contato. — {{clinic_name}} \N \N \N draft ["patient_name", "clinic_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +7ccd6a04-8b89-4238-bb97-d78b0d56312f \N \N intake.approved.sms intake sms intake_aprovado Olá {{patient_name}}, seu cadastro foi aprovado! Acesse o portal para agendar sua sessão. — {{therapist_name}} \N \N \N draft ["patient_name", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +\. + + +-- +-- Data for Name: notifications; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notifications (id, owner_id, tenant_id, type, ref_id, ref_table, payload, read_at, archived, created_at) FROM stdin; +de9453b5-688c-411a-9ea1-c03ec3afea56 aaaaaaaa-0002-0002-0002-000000000002 \N new_patient f94e4a39-c92b-4f9b-82b4-1a2956f2140d patient_intake_requests {"title": "Novo cadastro externo", "detail": "Yasmin Gomes Ferreira", "deeplink": "/therapist/patients/cadastro/recebidos", "avatar_initials": "YA"} \N f 2026-03-23 23:25:39.670692+00 +efb5e6fd-ab89-46d9-b9d5-3534199b10e1 aaaaaaaa-0002-0002-0002-000000000002 \N new_patient 208f1883-a9d8-4056-831d-a22aec679cfb patient_intake_requests {"title": "Novo cadastro externo", "detail": "Carla Lima Almeida", "deeplink": "/therapist/patients/cadastro/recebidos", "avatar_initials": "CA"} 2026-03-23 23:34:19.18+00 f 2026-03-23 23:25:46.914188+00 +\. + + +-- +-- Data for Name: owner_users; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.owner_users (owner_id, user_id, role, created_at) FROM stdin; +\. + + +-- +-- Data for Name: patient_contacts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_contacts (id, patient_id, tenant_id, nome, tipo, relacao, telefone, email, cpf, especialidade, registro_profissional, is_primario, ativo, created_at, updated_at) FROM stdin; +26b3a63f-d44c-476b-ba15-8df162c597af 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 bbbbbbbb-0002-0002-0002-000000000002 Henrique Costa emergencia Irmão 55979769772 \N \N \N \N t t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +7334c030-eab5-42ac-a3e7-7091e40676ba 4fdd639d-5f32-453f-9092-74b183c8bbfe bbbbbbbb-0002-0002-0002-000000000002 Felipe Lima emergencia Irmã 55738955502 \N \N \N \N t t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +868bb964-b624-4a19-901e-6550d486893f 2aaa34f7-c770-4d78-a2a2-c77bd4771c6a bbbbbbbb-0002-0002-0002-000000000002 Bruno Martins emergencia Pai 55529945515 \N \N \N \N t t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +0a28b612-7c1b-4517-b0bb-fb5b3efab512 c193905f-e70c-4935-aec3-b9c161c6044c bbbbbbbb-0002-0002-0002-000000000002 Felipe Carvalho emergencia Pai 55225172394 \N \N \N \N t t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +882bd6bc-ea68-4a2e-bf3a-6f043698c1f0 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 bbbbbbbb-0002-0002-0002-000000000002 Yasmin Araújo Lima responsavel_legal Responsável legal 55209246525 \N 85070073257 \N \N f t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +e8ae6155-1445-4c9c-a0bd-e65f2b7799d4 4fdd639d-5f32-453f-9092-74b183c8bbfe bbbbbbbb-0002-0002-0002-000000000002 Vanessa Carvalho Barbosa responsavel_legal Responsável legal 55209874519 \N 24378696207 \N \N f t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +f6032ed5-0b69-4eed-89f9-c6e2ccce7fc3 2aaa34f7-c770-4d78-a2a2-c77bd4771c6a bbbbbbbb-0002-0002-0002-000000000002 Ana Oliveira Martins responsavel_legal Responsável legal 55129232266 \N 29325344165 \N \N f t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +0249c61e-3c4c-41a2-97f5-620228d1b967 c193905f-e70c-4935-aec3-b9c161c6044c bbbbbbbb-0002-0002-0002-000000000002 Sabrina Martins Santos responsavel_legal Responsável legal 55749688637 \N 89130651778 \N \N f t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +\. + + +-- +-- Data for Name: patient_discounts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_discounts (id, owner_id, tenant_id, patient_id, discount_pct, discount_flat, reason, active, active_from, active_to, created_at) FROM stdin; +\. + + +-- +-- Data for Name: patient_group_patient; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_group_patient (patient_group_id, patient_id, created_at, tenant_id) FROM stdin; +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 2026-03-25 20:47:18.396593+00 bbbbbbbb-0002-0002-0002-000000000002 +a5b1e98b-e951-4f8a-b8ab-8221bf74342f 4fdd639d-5f32-453f-9092-74b183c8bbfe 2026-03-25 20:47:43.917435+00 bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 2aaa34f7-c770-4d78-a2a2-c77bd4771c6a 2026-03-25 20:58:12.198254+00 bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 c193905f-e70c-4935-aec3-b9c161c6044c 2026-03-25 20:58:37.212766+00 bbbbbbbb-0002-0002-0002-000000000002 +a5b1e98b-e951-4f8a-b8ab-8221bf74342f 919ce219-3cca-4bf3-ad9d-2cc8db68cec0 2026-03-28 10:35:53.088394+00 bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 778338f5-7122-4d80-a6da-88a52deee1ca 2026-03-28 12:17:10.121291+00 bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 ad3bb70e-881e-4ee1-b44e-436eaae2caf1 2026-03-28 12:20:59.425179+00 bbbbbbbb-0002-0002-0002-000000000002 +a5b1e98b-e951-4f8a-b8ab-8221bf74342f 542156ce-9266-487c-85e6-91a00cdfb3ea 2026-03-28 12:43:28.793188+00 bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 fe670066-0d81-49ea-b177-61e83b455c59 2026-03-29 12:18:54.34788+00 bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: patient_groups; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_groups (id, nome, descricao, cor, is_active, is_system, owner_id, created_at, updated_at, therapist_id, tenant_id) FROM stdin; +a5b1e98b-e951-4f8a-b8ab-8221bf74342f teste \N #22c55e t f aaaaaaaa-0002-0002-0002-000000000002 2026-03-23 15:35:37.961391+00 2026-03-23 17:13:24.546376+00 \N bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 teste 2 \N \N t f aaaaaaaa-0002-0002-0002-000000000002 2026-03-24 22:43:26.378387+00 2026-03-24 22:43:26.378387+00 \N bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: patient_intake_requests; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_intake_requests (id, owner_id, token, consent, status, created_at, converted_patient_id, rejected_reason, updated_at, cpf, rg, cep, nome_completo, email_principal, telefone, pais, cidade, estado, endereco, numero, bairro, complemento, data_nascimento, naturalidade, genero, estado_civil, onde_nos_conheceu, encaminhado_por, observacoes, notas_internas, email_alternativo, telefone_alternativo, profissao, escolaridade, nacionalidade, avatar_url, tenant_id) FROM stdin; +208f1883-a9d8-4056-831d-a22aec679cfb aaaaaaaa-0002-0002-0002-000000000002 9c0780f2-bf0a-442e-9aa5-787c2f585f5c t converted 2026-03-23 23:25:46.914188+00 f816b137-fb45-471c-81d2-bda8504b4f00 \N 2026-03-23 23:29:46.973175+00 68948962086 98.292.802-5 13561260 Carla Lima Almeida carla.lima.almeida.336@email.com 57973277841 Brasil São Carlos SP Avenida Tancredo de Almeida Neves 7363 Parque Santa Mônica Apto 122 1966-10-10 Campinas other single Outro \N Tenho disponibilidade no período da noite. \N \N \N Enfermeira Superior completo \N \N \N +f94e4a39-c92b-4f9b-82b4-1a2956f2140d aaaaaaaa-0002-0002-0002-000000000002 9c0780f2-bf0a-442e-9aa5-787c2f585f5c t converted 2026-03-23 23:25:39.670692+00 4214ea1c-c387-47ba-922d-5a492aca2ee7 \N 2026-03-23 23:30:08.162018+00 16487081612 43.076.886-9 13561260 Yasmin Gomes Ferreira yasmin.gomes.ferreira.69@email.com 12947152592 Brasil São Carlos SP Avenida Tancredo de Almeida Neves 8346 Parque Santa Mônica \N 1992-03-02 Bauru na single Google \N Cadastro realizado via link externo. \N \N \N Professora Pós-graduação \N \N \N +\. + + +-- +-- Data for Name: patient_invites; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_invites (id, owner_id, token, active, expires_at, max_uses, uses, created_at, tenant_id) FROM stdin; +d04d07d7-736c-4a28-8102-347cd2987bdd aaaaaaaa-0002-0002-0002-000000000002 9c0780f2-bf0a-442e-9aa5-787c2f585f5c t \N \N 0 2026-03-23 16:52:59.16431+00 \N +\. + + +-- +-- Data for Name: patient_tags; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_tags (id, owner_id, nome, cor, is_padrao, created_at, updated_at, tenant_id) FROM stdin; +69826bd2-005f-414f-a7fe-74b873e88012 aaaaaaaa-0002-0002-0002-000000000002 teste #b53e3e f 2026-03-23 16:56:08.12277+00 2026-03-23 22:54:26.043013+00 bbbbbbbb-0002-0002-0002-000000000002 +d39ec5c3-ae75-411c-9f2c-2d19f017b53b aaaaaaaa-0002-0002-0002-000000000002 teste2 #ef4444 f 2026-03-23 23:34:11.517479+00 \N bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: patient_patient_tag; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_patient_tag (owner_id, patient_id, tag_id, created_at, tenant_id) FROM stdin; +aaaaaaaa-0002-0002-0002-000000000002 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-25 20:47:18.644356+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-25 20:47:18.644356+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 4fdd639d-5f32-453f-9092-74b183c8bbfe 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-25 20:47:44.202242+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 2aaa34f7-c770-4d78-a2a2-c77bd4771c6a d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-25 20:58:12.518944+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 c193905f-e70c-4935-aec3-b9c161c6044c 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-25 20:58:37.551427+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 c193905f-e70c-4935-aec3-b9c161c6044c d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-25 20:58:37.551427+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 919ce219-3cca-4bf3-ad9d-2cc8db68cec0 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-28 10:35:53.314452+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 919ce219-3cca-4bf3-ad9d-2cc8db68cec0 d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-28 10:35:53.314452+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 778338f5-7122-4d80-a6da-88a52deee1ca 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-28 12:17:10.597057+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 ad3bb70e-881e-4ee1-b44e-436eaae2caf1 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-28 12:20:59.659774+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 542156ce-9266-487c-85e6-91a00cdfb3ea 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-28 12:43:29.045752+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 542156ce-9266-487c-85e6-91a00cdfb3ea d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-28 12:43:29.045752+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 fe670066-0d81-49ea-b177-61e83b455c59 d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-29 12:18:54.625126+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 fe670066-0d81-49ea-b177-61e83b455c59 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-29 12:18:54.625126+00 bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: patient_status_history; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_status_history (id, patient_id, tenant_id, status_anterior, status_novo, motivo, encaminhado_para, data_saida, alterado_por, alterado_em) FROM stdin; +be09f88d-3935-423d-9ed3-04454a187fbb 2aaa34f7-c770-4d78-a2a2-c77bd4771c6a bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-25 20:58:11.897509+00 +00a38f56-b1e4-4880-9a1f-59124d05156f d8a170f0-c67e-4e6d-8dc1-b51423d20889 bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-25 20:47:35.342234+00 +d321e238-a0e9-48e3-8319-47ffd42050cf 76cec0bb-1e63-45bf-9b03-feaaa2a5d18d bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-24 10:20:35.019712+00 +a5f5e7ed-a96c-4ce0-9503-df651b7326e2 8e33a4dc-4bfb-4cce-aec0-fe5a5c91300f bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-25 20:48:39.752714+00 +187af3e8-0ede-4197-adc1-3aa89bcbde2d 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-25 20:47:18.217202+00 +b9fd23ec-db77-47aa-bca7-6e75229fefd0 6449e64b-050b-419f-8845-029b6f10a17d bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-23 11:33:02.010795+00 +ba28b753-c79b-48ad-ab60-b0a397c71c8c 4fdd639d-5f32-453f-9092-74b183c8bbfe bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-25 20:47:43.705237+00 +a939c592-901e-4b23-bfd3-51a654f02a15 c193905f-e70c-4935-aec3-b9c161c6044c bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-25 20:58:37.007581+00 +f0f4685b-bf2e-4f80-80d8-e54d499d7a9c 919ce219-3cca-4bf3-ad9d-2cc8db68cec0 bbbbbbbb-0002-0002-0002-000000000002 \N Ativo \N \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 10:35:52.861986+00 +9f2e7fdc-9cac-46eb-b021-6d8ef110e52b 778338f5-7122-4d80-a6da-88a52deee1ca bbbbbbbb-0002-0002-0002-000000000002 \N Ativo \N \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:17:09.861474+00 +cb51a4c4-0f23-4a0e-9f24-bd658ffdd1b1 ad3bb70e-881e-4ee1-b44e-436eaae2caf1 bbbbbbbb-0002-0002-0002-000000000002 \N Ativo \N \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:18:06.204794+00 +f298c10c-2476-4f11-adf5-486aaee37597 fe670066-0d81-49ea-b177-61e83b455c59 bbbbbbbb-0002-0002-0002-000000000002 \N Ativo \N \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:18:31.642656+00 +d5a73b0e-52e1-4316-be48-69a6f3c6445b 368f2fe0-72c9-4dca-ac40-0ee1af11b44c bbbbbbbb-0002-0002-0002-000000000002 \N Ativo \N \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:37:23.13379+00 +37f0bdda-cde0-4bbb-bbf4-7e602b5dfbe4 542156ce-9266-487c-85e6-91a00cdfb3ea bbbbbbbb-0002-0002-0002-000000000002 \N Ativo \N \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:43:28.632137+00 +\. + + +-- +-- Data for Name: patient_support_contacts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_support_contacts (id, patient_id, owner_id, tenant_id, nome, relacao, tipo, telefone, email, is_primario, created_at, updated_at) FROM stdin; +5b8d1b8f-4b2b-4550-8515-9445f14342ad ad3bb70e-881e-4ee1-b44e-436eaae2caf1 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 Henrique Ferreira cônjuge emergencia 16988280038 teste7@agenciapsi.com.br t 2026-03-28 12:20:59.681978+00 2026-03-28 12:20:59.681978+00 +34c50b28-f647-4e9e-92c8-0b94c5674409 542156ce-9266-487c-85e6-91a00cdfb3ea aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 João Lima cônjuge emergencia 16988280038 teste2@agenciapsi.com.br t 2026-03-28 12:43:29.077599+00 2026-03-28 12:43:29.077599+00 +14384d53-b292-4413-b7ae-55dd68dbdcce fe670066-0d81-49ea-b177-61e83b455c59 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 Eduarda Oliveira psiquiatra emergencia 16988280038 teste2@agenciapsi.com.br t 2026-03-29 12:18:54.647261+00 2026-03-29 12:18:54.647261+00 +\. + + +-- +-- Data for Name: patient_timeline; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_timeline (id, patient_id, tenant_id, evento_tipo, titulo, descricao, icone_cor, link_ref_tipo, link_ref_id, gerado_por, ocorrido_em) FROM stdin; +2e6490ab-94be-40b4-bfa7-25eb7ce8a333 919ce219-3cca-4bf3-ad9d-2cc8db68cec0 bbbbbbbb-0002-0002-0002-000000000002 status_alterado Status alterado para Ativo Paciente cadastrado green \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 10:35:52.861986+00 +2c74de8a-2f98-4cd3-bed7-9768342e29d5 778338f5-7122-4d80-a6da-88a52deee1ca bbbbbbbb-0002-0002-0002-000000000002 status_alterado Status alterado para Ativo Paciente cadastrado green \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:17:09.861474+00 +add20535-37da-4cb1-b933-9f3da92ed9b7 ad3bb70e-881e-4ee1-b44e-436eaae2caf1 bbbbbbbb-0002-0002-0002-000000000002 status_alterado Status alterado para Ativo Paciente cadastrado green \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:18:06.204794+00 +5491b6b1-e4ef-4370-acdb-d7ab834cdfbc fe670066-0d81-49ea-b177-61e83b455c59 bbbbbbbb-0002-0002-0002-000000000002 status_alterado Status alterado para Ativo Paciente cadastrado green \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:18:31.642656+00 +f5ffb4b3-58b8-4dd5-bb89-648851f2895a 368f2fe0-72c9-4dca-ac40-0ee1af11b44c bbbbbbbb-0002-0002-0002-000000000002 status_alterado Status alterado para Ativo Paciente cadastrado green \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:37:23.13379+00 +e5d9e952-1f87-4252-bb10-170e1b78963e 542156ce-9266-487c-85e6-91a00cdfb3ea bbbbbbbb-0002-0002-0002-000000000002 status_alterado Status alterado para Ativo Paciente cadastrado green \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:43:28.632137+00 +\. + + +-- +-- Data for Name: payment_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.payment_settings (id, owner_id, tenant_id, pix_ativo, pix_tipo, pix_chave, pix_nome_titular, deposito_ativo, deposito_banco, deposito_agencia, deposito_conta, deposito_tipo_conta, deposito_titular, deposito_cpf_cnpj, dinheiro_ativo, cartao_ativo, cartao_instrucao, convenio_ativo, convenio_lista, observacoes_pagamento, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: plans; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plans (id, key, name, description, is_active, created_at, price_cents, currency, billing_interval, target, max_supervisees) FROM stdin; +984c1f29-a975-4208-93ac-2118ed1039b7 patient_free Paciente Free Plano gratuito para pacientes t 2026-03-03 22:40:11.413107+00 0 BRL month patient \N +c56fe2a8-2c17-4048-adc7-ff7fbd89461a therapist_free THERAPIST FREE Plano gratuito para terapeutas. t 2026-03-01 09:40:48.439668+00 0 BRL month therapist \N +82067ba7-16f0-4803-b36f-4c4e8919d4b4 therapist_pro THERAPIST PRO Plano completo para terapeutas. t 2026-03-01 09:25:03.878498+00 4900 BRL month therapist \N +01a5867f-0705-4714-ac97-a23470949157 clinic_free CLINIC FREE Plano gratuito para clínicas iniciarem. t 2026-03-01 09:25:03.878498+00 0 BRL month clinic \N +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 clinic_pro CLINIC PRO Plano completo para clínicas. t 2026-03-01 09:30:06.50975+00 14900 BRL month clinic \N +8c4895a3-e12d-48de-a078-efb8a4ea2eb2 supervisor_free Supervisor Free Plano gratuito de supervisão. Até 3 terapeutas supervisionados. t 2026-03-05 00:58:17.218326+00 0 BRL month supervisor 3 +ca28e46c-0687-45d5-9406-0a0f56a5b625 supervisor_pro Supervisor PRO Plano profissional de supervisão. Até 20 terapeutas supervisionados. t 2026-03-05 00:58:17.218326+00 0 BRL month supervisor 20 +\. + + +-- +-- Data for Name: plan_features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_features (plan_id, feature_id, enabled, limits, created_at) FROM stdin; +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 t {"max_patients": 9999, "max_therapists": 999} 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a56482a1-0787-49da-90a7-e1857488734a t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 9999} 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 bf133ad1-da8e-4ea9-bd66-21901cb50075 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a830e45b-3bb4-4b17-812d-fe83777a2377 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 9999} 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 336aeeba-b18e-4e68-8303-d42ba09f4b20 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 30c9cdd5-7c8c-44d9-8c0b-614165bb9496 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 74fc1321-4d17-49c3-b72e-db3a7f4be451 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 90e92108-8124-40ee-88a0-f0ecafb57d76 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 f393178c-284d-422f-b096-8793f85428d5 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 d6f54674-ea8b-484b-af0e-99127a510da2 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a5593d96-dd95-46bb-bef0-bd379b56ad50 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 t {"max_patients": 30, "max_therapists": 5} 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a56482a1-0787-49da-90a7-e1857488734a t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 40} 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 bf133ad1-da8e-4ea9-bd66-21901cb50075 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a830e45b-3bb4-4b17-812d-fe83777a2377 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 50} 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 90e92108-8124-40ee-88a0-f0ecafb57d76 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 336aeeba-b18e-4e68-8303-d42ba09f4b20 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 30c9cdd5-7c8c-44d9-8c0b-614165bb9496 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 74fc1321-4d17-49c3-b72e-db3a7f4be451 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 f393178c-284d-422f-b096-8793f85428d5 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 d6f54674-ea8b-484b-af0e-99127a510da2 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a5593d96-dd95-46bb-bef0-bd379b56ad50 f \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 9999} 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 bf133ad1-da8e-4ea9-bd66-21901cb50075 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 a830e45b-3bb4-4b17-812d-fe83777a2377 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 9999} 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 336aeeba-b18e-4e68-8303-d42ba09f4b20 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 90e92108-8124-40ee-88a0-f0ecafb57d76 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 f393178c-284d-422f-b096-8793f85428d5 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 d6f54674-ea8b-484b-af0e-99127a510da2 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 a5593d96-dd95-46bb-bef0-bd379b56ad50 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 40} 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a bf133ad1-da8e-4ea9-bd66-21901cb50075 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 50} 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 90e92108-8124-40ee-88a0-f0ecafb57d76 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a f393178c-284d-422f-b096-8793f85428d5 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a d6f54674-ea8b-484b-af0e-99127a510da2 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a a5593d96-dd95-46bb-bef0-bd379b56ad50 f \N 2026-03-23 14:18:07.731209+00 +8c4895a3-e12d-48de-a078-efb8a4ea2eb2 9ab8bdbb-838b-4946-aa5d-fd9cfdd257b3 t \N 2026-03-23 14:18:07.731209+00 +8c4895a3-e12d-48de-a078-efb8a4ea2eb2 1167b54a-0e93-43a2-94d7-c12e64eb56de t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 9ab8bdbb-838b-4946-aa5d-fd9cfdd257b3 t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 1167b54a-0e93-43a2-94d7-c12e64eb56de t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 761e4495-b46a-4791-9519-86ffe48dc47f t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 7e82ee01-44f6-4b3f-9861-840c58e13f58 t \N 2026-03-23 14:18:07.731209+00 +\. + + +-- +-- Data for Name: plan_prices; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_prices (id, plan_id, currency, "interval", amount_cents, is_active, active_from, active_to, source, provider, provider_price_id, created_at) FROM stdin; +37510504-4617-4421-9979-4249778bd5ae 82067ba7-16f0-4803-b36f-4c4e8919d4b4 BRL month 4900 t 2026-03-01 09:25:03.878498+00 \N manual \N \N 2026-03-01 09:25:03.878498+00 +225afd5a-9f30-46bc-a0df-5eb8f91660cb 82067ba7-16f0-4803-b36f-4c4e8919d4b4 BRL year 49000 t 2026-03-01 09:25:03.878498+00 \N manual \N \N 2026-03-01 09:25:03.878498+00 +124779b4-362d-4890-9631-747021ecc1c0 a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 BRL month 14900 t 2026-03-01 09:30:06.50975+00 \N manual \N \N 2026-03-01 09:30:06.50975+00 +73908784-6299-45c8-b547-e1556b45c292 a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 BRL year 149000 t 2026-03-01 09:30:06.50975+00 \N manual \N \N 2026-03-01 09:30:06.50975+00 +\. + + +-- +-- Data for Name: plan_public; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_public (plan_id, public_name, public_description, badge, is_featured, is_visible, sort_order, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: plan_public_bullets; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_public_bullets (id, plan_id, text, sort_order, highlight, created_at) FROM stdin; +\. + + +-- +-- Data for Name: professional_pricing; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.professional_pricing (id, owner_id, tenant_id, determined_commitment_id, price, notes, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: profiles; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.profiles (id, role, full_name, created_at, updated_at, avatar_url, phone, bio, language, timezone, notify_system_email, notify_reminders, notify_news, account_type, platform_roles, nickname, work_description, work_description_other, site_url, social_instagram, social_youtube, social_facebook, social_x, social_custom) FROM stdin; +aaaaaaaa-0001-0001-0001-000000000001 portal_user Ana Paciente 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N pt-BR America/Sao_Paulo t t f patient {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0002-0002-0002-000000000002 tenant_member Leonardo Nohama223 2026-03-23 10:46:29.876072+00 2026-03-27 14:18:26.567019+00 http://127.0.0.1:54321/storage/v1/object/public/avatars/aaaaaaaa-0002-0002-0002-000000000002/avatar.webp (11) 11111-1111 Bio 2asd pt-BR America/Sao_Paulo t t t therapist {} Léo psicologo_clinico \N \N \N \N \N \N [] +aaaaaaaa-0006-0006-0006-000000000006 saas_admin Admin Plataforma 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N pt-BR America/Sao_Paulo t t f free {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0007-0007-0007-000000000007 tenant_member Carlos Supervisor 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0009-0009-0009-000000000009 tenant_member Eva Terapeuta 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0010-0010-0010-000000000010 tenant_member Felipe Terapeuta 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0011-0011-0011-000000000011 tenant_member Gabriela Secretária 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0003-0003-0003-000000000003 tenant_member Clínica Espaço Psi 2026-03-23 10:46:29.876072+00 2026-03-23 14:18:43.066684+00 \N \N \N pt-BR America/Sao_Paulo t t f clinic {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0004-0004-0004-000000000004 tenant_member Clínica Mente Sã 2026-03-23 10:46:29.876072+00 2026-03-23 14:18:59.312611+00 \N \N \N pt-BR America/Sao_Paulo t t f clinic {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0005-0005-0005-000000000005 tenant_member Leonardo Nohama 2026-03-23 10:46:29.876072+00 2026-03-26 17:19:07.27483+00 \N (16) 98828-0038 bio curta pt-BR America/Sao_Paulo t t f clinic {} Léo psicanalista \N \N \N \N \N \N [] +aaaaaaaa-0008-0008-0008-000000000008 tenant_member Diana Editora 2026-03-23 14:18:05.215881+00 2026-03-26 19:46:29.161046+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {editor} \N \N \N \N \N \N \N \N [] +384a69d8-b7cd-40ac-9d3c-764c93532b66 portal_user \N 2026-03-26 19:47:30.477551+00 2026-03-26 19:47:30.68134+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +\. + + +-- +-- Data for Name: recurrence_exceptions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.recurrence_exceptions (id, recurrence_id, tenant_id, original_date, type, new_date, new_start_time, new_end_time, modalidade, observacoes, titulo_custom, extra_fields, reason, agenda_evento_id, created_at) FROM stdin; +\. + + +-- +-- Data for Name: recurrence_rule_services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.recurrence_rule_services (id, rule_id, service_id, quantity, unit_price, discount_pct, discount_flat, final_price, created_at) FROM stdin; +\. + + +-- +-- Data for Name: saas_admins; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_admins (user_id, created_at) FROM stdin; +aaaaaaaa-0006-0006-0006-000000000006 2026-03-23 10:46:29.876072+00 +\. + + +-- +-- Data for Name: saas_docs; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_docs (id, titulo, conteudo, medias, tipo_acesso, pagina_path, docs_relacionados, ativo, ordem, created_at, updated_at, categoria, exibir_no_faq, votos_util, votos_nao_util) FROM stdin; +\. + + +-- +-- Data for Name: saas_doc_votos; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_doc_votos (id, doc_id, user_id, util, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: saas_faq; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_faq (id, pergunta, categoria, publico, votos, titulo, conteudo, tipo_acesso, pagina_path, pagina_label, medias, faqs_relacionados, ativo, ordem, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: saas_faq_itens; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_faq_itens (id, doc_id, pergunta, resposta, ordem, ativo, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: subscriptions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscriptions (id, user_id, plan_id, status, current_period_start, current_period_end, cancel_at_period_end, provider, provider_customer_id, provider_subscription_id, created_at, updated_at, tenant_id, plan_key, "interval", source, started_at, canceled_at, activated_at, past_due_since, suspended_at, suspended_reason, cancelled_at, cancel_reason, expired_at) FROM stdin; +61f81f06-c718-4d52-8063-67c38c1c1df9 aaaaaaaa-0001-0001-0001-000000000001 984c1f29-a975-4208-93ac-2118ed1039b7 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N patient_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +8716fb6d-372f-4560-98a5-68c40aec96dc aaaaaaaa-0007-0007-0007-000000000007 8c4895a3-e12d-48de-a078-efb8a4ea2eb2 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N supervisor_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +3fc090ba-3e9a-47ef-9711-edf9a3fd1795 aaaaaaaa-0008-0008-0008-000000000008 c56fe2a8-2c17-4048-adc7-ff7fbd89461a active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N therapist_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +cc4c4eb4-c9e5-4ce7-a705-9f5dc415fcb4 aaaaaaaa-0009-0009-0009-000000000009 c56fe2a8-2c17-4048-adc7-ff7fbd89461a active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N therapist_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +d2516bea-712d-453a-a5ed-9e40456f9aca aaaaaaaa-0010-0010-0010-000000000010 c56fe2a8-2c17-4048-adc7-ff7fbd89461a active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N therapist_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +bd6dd2c0-9c50-4939-8fc9-41fd67423a3e \N 01a5867f-0705-4714-ac97-a23470949157 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 bbbbbbbb-0003-0003-0003-000000000003 clinic_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +505d05fa-a6db-46b7-8cbf-bb6b12fb8e2b \N 01a5867f-0705-4714-ac97-a23470949157 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 bbbbbbbb-0004-0004-0004-000000000004 clinic_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +27eef221-7db2-442a-99f3-989f910cdcb4 \N 01a5867f-0705-4714-ac97-a23470949157 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 bbbbbbbb-0005-0005-0005-000000000005 clinic_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +6864f71c-c4d4-4d7e-9897-89a644002b6d aaaaaaaa-0002-0002-0002-000000000002 82067ba7-16f0-4803-b36f-4c4e8919d4b4 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-27 09:41:23.50964+00 \N therapist_pro month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +\. + + +-- +-- Data for Name: subscription_events; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_events (id, subscription_id, owner_id, event_type, old_plan_id, new_plan_id, created_at, created_by, source, reason, metadata, owner_type, owner_ref) FROM stdin; +b9180edb-76a7-4f0b-be24-92f056091f14 6864f71c-c4d4-4d7e-9897-89a644002b6d aaaaaaaa-0002-0002-0002-000000000002 plan_changed c56fe2a8-2c17-4048-adc7-ff7fbd89461a 82067ba7-16f0-4803-b36f-4c4e8919d4b4 2026-03-27 09:41:23.50964+00 aaaaaaaa-0002-0002-0002-000000000002 dev_menu Plan change via DEV menu {"new_plan": "82067ba7-16f0-4803-b36f-4c4e8919d4b4", "new_plan_key": "therapist_pro", "previous_plan": "c56fe2a8-2c17-4048-adc7-ff7fbd89461a", "new_plan_target": "therapist"} therapist aaaaaaaa-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: subscription_intents_legacy; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_intents_legacy (id, user_id, email, plan_key, "interval", amount_cents, currency, status, source, notes, created_at, paid_at, tenant_id, created_by_user_id) FROM stdin; +\. + + +-- +-- Data for Name: subscription_intents_personal; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_intents_personal (id, user_id, created_by_user_id, email, plan_id, plan_key, "interval", amount_cents, currency, status, source, notes, created_at, paid_at, subscription_id) FROM stdin; +\. + + +-- +-- Data for Name: subscription_intents_tenant; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_intents_tenant (id, user_id, created_by_user_id, email, plan_id, plan_key, "interval", amount_cents, currency, status, source, notes, created_at, paid_at, tenant_id, subscription_id) FROM stdin; +\. + + +-- +-- Data for Name: support_sessions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.support_sessions (id, tenant_id, admin_id, token, expires_at, created_at) FROM stdin; +\. + + +-- +-- Data for Name: tenant_feature_exceptions_log; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_feature_exceptions_log (id, tenant_id, feature_key, enabled, reason, created_by, created_at) FROM stdin; +\. + + +-- +-- Data for Name: tenant_features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_features (tenant_id, feature_key, enabled, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: tenant_invites; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_invites (id, tenant_id, email, role, token, invited_by, created_at, expires_at, accepted_at, accepted_by, revoked_at, revoked_by) FROM stdin; +\. + + +-- +-- Data for Name: tenant_modules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_modules (id, owner_id, module_id, status, settings, provider, provider_item_id, installed_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: therapist_payouts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.therapist_payouts (id, owner_id, tenant_id, period_start, period_end, total_sessions, gross_amount, clinic_fee_total, net_amount, status, paid_at, notes, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: therapist_payout_records; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.therapist_payout_records (payout_id, financial_record_id) FROM stdin; +\. + + +-- +-- Data for Name: twilio_subaccount_usage; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.twilio_subaccount_usage (id, tenant_id, channel_id, twilio_subaccount_sid, period_start, period_end, messages_sent, messages_delivered, messages_failed, cost_usd, cost_brl, revenue_brl, usd_brl_rate, synced_at, created_at) FROM stdin; +\. + + +-- +-- Data for Name: user_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.user_settings (user_id, theme_mode, preset, primary_color, surface_color, menu_mode, created_at, updated_at, layout_variant) FROM stdin; +aaaaaaaa-0002-0002-0002-000000000002 light Lara rose soho static 2026-03-25 18:47:05.458455+00 2026-03-28 10:24:35.930276+00 classic +aaaaaaaa-0005-0005-0005-000000000005 dark Aura orange slate static 2026-03-26 10:14:04.329765+00 2026-03-26 17:19:07.587294+00 classic +\. + + +-- +-- Name: refresh_tokens_id_seq; Type: SEQUENCE SET; Schema: auth; Owner: - +-- + +SELECT pg_catalog.setval('auth.refresh_tokens_id_seq', 155, true); + + +-- +-- Name: jobid_seq; Type: SEQUENCE SET; Schema: cron; Owner: - +-- + +SELECT pg_catalog.setval('cron.jobid_seq', 1, false); + + +-- +-- Name: runid_seq; Type: SEQUENCE SET; Schema: cron; Owner: - +-- + +SELECT pg_catalog.setval('cron.runid_seq', 1, false); + + +-- +-- Name: _db_migrations_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public._db_migrations_id_seq', 15, true); + + +-- +-- Name: agenda_online_slots_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.agenda_online_slots_id_seq', 1, false); + + +-- +-- PostgreSQL database dump complete +-- + +\unrestrict XPa1LcYWz3K821NmGm5bTrtk26L4QuNyoejVScPd40dVb1JeGkFrWmrMc2TIigR + diff --git a/database-novo/backups/2026-03-29/full_dump.sql b/database-novo/backups/2026-03-29/full_dump.sql new file mode 100644 index 0000000..3bdb53e --- /dev/null +++ b/database-novo/backups/2026-03-29/full_dump.sql @@ -0,0 +1,22428 @@ +-- +-- PostgreSQL database dump +-- + +\restrict sHssDG8nRcgy91i34c0GgSqKTkbvMenWmDdSa1Uuu0ZRvDQMFU2yjZxjPLTMS1x + +-- Dumped from database version 17.6 +-- Dumped by pg_dump version 17.6 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET transaction_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Name: _realtime; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA _realtime; + + +-- +-- Name: auth; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA auth; + + +-- +-- Name: pg_cron; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_cron WITH SCHEMA pg_catalog; + + +-- +-- Name: EXTENSION pg_cron; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_cron IS 'Job scheduler for PostgreSQL'; + + +-- +-- Name: extensions; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA extensions; + + +-- +-- Name: graphql; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA graphql; + + +-- +-- Name: graphql_public; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA graphql_public; + + +-- +-- Name: pg_net; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_net WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION pg_net; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_net IS 'Async HTTP'; + + +-- +-- Name: pgbouncer; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA pgbouncer; + + +-- +-- Name: realtime; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA realtime; + + +-- +-- Name: storage; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA storage; + + +-- +-- Name: supabase_functions; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA supabase_functions; + + +-- +-- Name: vault; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA vault; + + +-- +-- Name: btree_gist; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS btree_gist WITH SCHEMA public; + + +-- +-- Name: EXTENSION btree_gist; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION btree_gist IS 'support for indexing common datatypes in GiST'; + + +-- +-- Name: citext; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public; + + +-- +-- Name: EXTENSION citext; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION citext IS 'data type for case-insensitive character strings'; + + +-- +-- Name: pg_graphql; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_graphql WITH SCHEMA graphql; + + +-- +-- Name: EXTENSION pg_graphql; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_graphql IS 'pg_graphql: GraphQL support'; + + +-- +-- Name: pg_stat_statements; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION pg_stat_statements; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_stat_statements IS 'track planning and execution statistics of all SQL statements executed'; + + +-- +-- Name: pg_trgm; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA public; + + +-- +-- Name: EXTENSION pg_trgm; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_trgm IS 'text similarity measurement and index searching based on trigrams'; + + +-- +-- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pgcrypto IS 'cryptographic functions'; + + +-- +-- Name: supabase_vault; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS supabase_vault WITH SCHEMA vault; + + +-- +-- Name: EXTENSION supabase_vault; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION supabase_vault IS 'Supabase Vault Extension'; + + +-- +-- Name: uuid-ossp; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION "uuid-ossp"; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)'; + + +-- +-- Name: aal_level; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.aal_level AS ENUM ( + 'aal1', + 'aal2', + 'aal3' +); + + +-- +-- Name: code_challenge_method; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.code_challenge_method AS ENUM ( + 's256', + 'plain' +); + + +-- +-- Name: factor_status; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.factor_status AS ENUM ( + 'unverified', + 'verified' +); + + +-- +-- Name: factor_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.factor_type AS ENUM ( + 'totp', + 'webauthn', + 'phone' +); + + +-- +-- Name: oauth_authorization_status; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_authorization_status AS ENUM ( + 'pending', + 'approved', + 'denied', + 'expired' +); + + +-- +-- Name: oauth_client_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_client_type AS ENUM ( + 'public', + 'confidential' +); + + +-- +-- Name: oauth_registration_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_registration_type AS ENUM ( + 'dynamic', + 'manual' +); + + +-- +-- Name: oauth_response_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_response_type AS ENUM ( + 'code' +); + + +-- +-- Name: one_time_token_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.one_time_token_type AS ENUM ( + 'confirmation_token', + 'reauthentication_token', + 'recovery_token', + 'email_change_token_new', + 'email_change_token_current', + 'phone_change_token' +); + + +-- +-- Name: commitment_log_source; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.commitment_log_source AS ENUM ( + 'manual', + 'auto' +); + + +-- +-- Name: determined_field_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.determined_field_type AS ENUM ( + 'text', + 'textarea', + 'number', + 'date', + 'select', + 'boolean' +); + + +-- +-- Name: financial_record_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.financial_record_type AS ENUM ( + 'receita', + 'despesa' +); + + +-- +-- Name: recurrence_exception_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.recurrence_exception_type AS ENUM ( + 'cancel_session', + 'reschedule_session', + 'patient_missed', + 'therapist_canceled', + 'holiday_block' +); + + +-- +-- Name: recurrence_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.recurrence_type AS ENUM ( + 'weekly', + 'biweekly', + 'monthly', + 'yearly', + 'custom_weekdays' +); + + +-- +-- Name: status_agenda_serie; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.status_agenda_serie AS ENUM ( + 'ativo', + 'pausado', + 'cancelado' +); + + +-- +-- Name: status_evento_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.status_evento_agenda AS ENUM ( + 'agendado', + 'realizado', + 'faltou', + 'cancelado', + 'remarcar' +); + + +-- +-- Name: status_excecao_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.status_excecao_agenda AS ENUM ( + 'pendente', + 'ativo', + 'arquivado' +); + + +-- +-- Name: tipo_evento_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.tipo_evento_agenda AS ENUM ( + 'sessao', + 'bloqueio' +); + + +-- +-- Name: tipo_excecao_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.tipo_excecao_agenda AS ENUM ( + 'bloqueio', + 'horario_extra' +); + + +-- +-- Name: action; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.action AS ENUM ( + 'INSERT', + 'UPDATE', + 'DELETE', + 'TRUNCATE', + 'ERROR' +); + + +-- +-- Name: equality_op; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.equality_op AS ENUM ( + 'eq', + 'neq', + 'lt', + 'lte', + 'gt', + 'gte', + 'in' +); + + +-- +-- Name: user_defined_filter; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.user_defined_filter AS ( + column_name text, + op realtime.equality_op, + value text +); + + +-- +-- Name: wal_column; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.wal_column AS ( + name text, + type_name text, + type_oid oid, + value jsonb, + is_pkey boolean, + is_selectable boolean +); + + +-- +-- Name: wal_rls; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.wal_rls AS ( + wal jsonb, + is_rls_enabled boolean, + subscription_ids uuid[], + errors text[] +); + + +-- +-- Name: buckettype; Type: TYPE; Schema: storage; Owner: - +-- + +CREATE TYPE storage.buckettype AS ENUM ( + 'STANDARD', + 'ANALYTICS', + 'VECTOR' +); + + +-- +-- Name: email(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.email() RETURNS text + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim.email', true), ''), + (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'email') + )::text +$$; + + +-- +-- Name: FUNCTION email(); Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON FUNCTION auth.email() IS 'Deprecated. Use auth.jwt() -> ''email'' instead.'; + + +-- +-- Name: jwt(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.jwt() RETURNS jsonb + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim', true), ''), + nullif(current_setting('request.jwt.claims', true), '') + )::jsonb +$$; + + +-- +-- Name: role(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.role() RETURNS text + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim.role', true), ''), + (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'role') + )::text +$$; + + +-- +-- Name: FUNCTION role(); Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON FUNCTION auth.role() IS 'Deprecated. Use auth.jwt() -> ''role'' instead.'; + + +-- +-- Name: uid(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.uid() RETURNS uuid + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim.sub', true), ''), + (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'sub') + )::uuid +$$; + + +-- +-- Name: FUNCTION uid(); Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON FUNCTION auth.uid() IS 'Deprecated. Use auth.jwt() -> ''sub'' instead.'; + + +-- +-- Name: grant_pg_cron_access(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.grant_pg_cron_access() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT + FROM pg_event_trigger_ddl_commands() AS ev + JOIN pg_extension AS ext + ON ev.objid = ext.oid + WHERE ext.extname = 'pg_cron' + ) + THEN + grant usage on schema cron to postgres with grant option; + + alter default privileges in schema cron grant all on tables to postgres with grant option; + alter default privileges in schema cron grant all on functions to postgres with grant option; + alter default privileges in schema cron grant all on sequences to postgres with grant option; + + alter default privileges for user supabase_admin in schema cron grant all + on sequences to postgres with grant option; + alter default privileges for user supabase_admin in schema cron grant all + on tables to postgres with grant option; + alter default privileges for user supabase_admin in schema cron grant all + on functions to postgres with grant option; + + grant all privileges on all tables in schema cron to postgres with grant option; + revoke all on table cron.job from postgres; + grant select on table cron.job to postgres with grant option; + END IF; +END; +$$; + + +-- +-- Name: FUNCTION grant_pg_cron_access(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.grant_pg_cron_access() IS 'Grants access to pg_cron'; + + +-- +-- Name: grant_pg_graphql_access(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.grant_pg_graphql_access() RETURNS event_trigger + LANGUAGE plpgsql + AS $_$ +DECLARE + func_is_graphql_resolve bool; +BEGIN + func_is_graphql_resolve = ( + SELECT n.proname = 'resolve' + FROM pg_event_trigger_ddl_commands() AS ev + LEFT JOIN pg_catalog.pg_proc AS n + ON ev.objid = n.oid + ); + + IF func_is_graphql_resolve + THEN + -- Update public wrapper to pass all arguments through to the pg_graphql resolve func + DROP FUNCTION IF EXISTS graphql_public.graphql; + create or replace function graphql_public.graphql( + "operationName" text default null, + query text default null, + variables jsonb default null, + extensions jsonb default null + ) + returns jsonb + language sql + as $$ + select graphql.resolve( + query := query, + variables := coalesce(variables, '{}'), + "operationName" := "operationName", + extensions := extensions + ); + $$; + + -- This hook executes when `graphql.resolve` is created. That is not necessarily the last + -- function in the extension so we need to grant permissions on existing entities AND + -- update default permissions to any others that are created after `graphql.resolve` + grant usage on schema graphql to postgres, anon, authenticated, service_role; + grant select on all tables in schema graphql to postgres, anon, authenticated, service_role; + grant execute on all functions in schema graphql to postgres, anon, authenticated, service_role; + grant all on all sequences in schema graphql to postgres, anon, authenticated, service_role; + alter default privileges in schema graphql grant all on tables to postgres, anon, authenticated, service_role; + alter default privileges in schema graphql grant all on functions to postgres, anon, authenticated, service_role; + alter default privileges in schema graphql grant all on sequences to postgres, anon, authenticated, service_role; + + -- Allow postgres role to allow granting usage on graphql and graphql_public schemas to custom roles + grant usage on schema graphql_public to postgres with grant option; + grant usage on schema graphql to postgres with grant option; + END IF; + +END; +$_$; + + +-- +-- Name: FUNCTION grant_pg_graphql_access(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.grant_pg_graphql_access() IS 'Grants access to pg_graphql'; + + +-- +-- Name: grant_pg_net_access(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.grant_pg_net_access() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_event_trigger_ddl_commands() AS ev + JOIN pg_extension AS ext + ON ev.objid = ext.oid + WHERE ext.extname = 'pg_net' + ) + THEN + GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role; + + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + + REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + + GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + END IF; +END; +$$; + + +-- +-- Name: FUNCTION grant_pg_net_access(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.grant_pg_net_access() IS 'Grants access to pg_net'; + + +-- +-- Name: pgrst_ddl_watch(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.pgrst_ddl_watch() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +DECLARE + cmd record; +BEGIN + FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands() + LOOP + IF cmd.command_tag IN ( + 'CREATE SCHEMA', 'ALTER SCHEMA' + , 'CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO', 'ALTER TABLE' + , 'CREATE FOREIGN TABLE', 'ALTER FOREIGN TABLE' + , 'CREATE VIEW', 'ALTER VIEW' + , 'CREATE MATERIALIZED VIEW', 'ALTER MATERIALIZED VIEW' + , 'CREATE FUNCTION', 'ALTER FUNCTION' + , 'CREATE TRIGGER' + , 'CREATE TYPE', 'ALTER TYPE' + , 'CREATE RULE' + , 'COMMENT' + ) + -- don't notify in case of CREATE TEMP table or other objects created on pg_temp + AND cmd.schema_name is distinct from 'pg_temp' + THEN + NOTIFY pgrst, 'reload schema'; + END IF; + END LOOP; +END; $$; + + +-- +-- Name: pgrst_drop_watch(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.pgrst_drop_watch() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +DECLARE + obj record; +BEGIN + FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() + LOOP + IF obj.object_type IN ( + 'schema' + , 'table' + , 'foreign table' + , 'view' + , 'materialized view' + , 'function' + , 'trigger' + , 'type' + , 'rule' + ) + AND obj.is_temporary IS false -- no pg_temp objects + THEN + NOTIFY pgrst, 'reload schema'; + END IF; + END LOOP; +END; $$; + + +-- +-- Name: set_graphql_placeholder(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.set_graphql_placeholder() RETURNS event_trigger + LANGUAGE plpgsql + AS $_$ + DECLARE + graphql_is_dropped bool; + BEGIN + graphql_is_dropped = ( + SELECT ev.schema_name = 'graphql_public' + FROM pg_event_trigger_dropped_objects() AS ev + WHERE ev.schema_name = 'graphql_public' + ); + + IF graphql_is_dropped + THEN + create or replace function graphql_public.graphql( + "operationName" text default null, + query text default null, + variables jsonb default null, + extensions jsonb default null + ) + returns jsonb + language plpgsql + as $$ + DECLARE + server_version float; + BEGIN + server_version = (SELECT (SPLIT_PART((select version()), ' ', 2))::float); + + IF server_version >= 14 THEN + RETURN jsonb_build_object( + 'errors', jsonb_build_array( + jsonb_build_object( + 'message', 'pg_graphql extension is not enabled.' + ) + ) + ); + ELSE + RETURN jsonb_build_object( + 'errors', jsonb_build_array( + jsonb_build_object( + 'message', 'pg_graphql is only available on projects running Postgres 14 onwards.' + ) + ) + ); + END IF; + END; + $$; + END IF; + + END; +$_$; + + +-- +-- Name: FUNCTION set_graphql_placeholder(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.set_graphql_placeholder() IS 'Reintroduces placeholder function for graphql_public.graphql'; + + +-- +-- Name: get_auth(text); Type: FUNCTION; Schema: pgbouncer; Owner: - +-- + +CREATE FUNCTION pgbouncer.get_auth(p_usename text) RETURNS TABLE(username text, password text) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $_$ +begin + raise debug 'PgBouncer auth request: %', p_usename; + + return query + select + rolname::text, + case when rolvaliduntil < now() + then null + else rolpassword::text + end + from pg_authid + where rolname=$1 and rolcanlogin; +end; +$_$; + + +-- +-- Name: __rls_ping(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.__rls_ping() RETURNS text + LANGUAGE sql STABLE + AS $$ + select 'ok'::text; +$$; + + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: subscriptions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscriptions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid, + plan_id uuid NOT NULL, + status text DEFAULT 'active'::text NOT NULL, + current_period_start timestamp with time zone, + current_period_end timestamp with time zone, + cancel_at_period_end boolean DEFAULT false NOT NULL, + provider text DEFAULT 'manual'::text NOT NULL, + provider_customer_id text, + provider_subscription_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid, + plan_key text, + "interval" text, + source text DEFAULT 'manual'::text NOT NULL, + started_at timestamp with time zone DEFAULT now() NOT NULL, + canceled_at timestamp with time zone, + activated_at timestamp with time zone, + past_due_since timestamp with time zone, + suspended_at timestamp with time zone, + suspended_reason text, + cancelled_at timestamp with time zone, + cancel_reason text, + expired_at timestamp with time zone, + CONSTRAINT subscriptions_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text]))), + CONSTRAINT subscriptions_owner_xor CHECK ((((tenant_id IS NOT NULL) AND (user_id IS NULL)) OR ((tenant_id IS NULL) AND (user_id IS NOT NULL)))), + CONSTRAINT subscriptions_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'active'::text, 'past_due'::text, 'suspended'::text, 'cancelled'::text, 'expired'::text]))) +); + + +-- +-- Name: activate_subscription_from_intent(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_intent record; + v_sub public.subscriptions; + v_days int; + v_user_id uuid; + v_plan_id uuid; + v_target text; +begin + -- l?? pela VIEW unificada + select * into v_intent + from public.subscription_intents + where id = p_intent_id; + + if not found then + raise exception 'Intent n??o encontrado: %', p_intent_id; + end if; + + if v_intent.status <> 'paid' then + raise exception 'Intent precisa estar paid para ativar assinatura'; + end if; + + -- resolve target e plan_id via plans.key + select p.id, p.target + into v_plan_id, v_target + from public.plans p + where p.key = v_intent.plan_key + limit 1; + + if v_plan_id is null then + raise exception 'Plano n??o encontrado em plans.key = %', v_intent.plan_key; + end if; + + v_target := lower(coalesce(v_target, '')); + + -- ??? supervisor adicionado + if v_target not in ('clinic', 'therapist', 'supervisor') then + raise exception 'Target inv??lido em plans.target: %', v_target; + end if; + + -- regra por target + if v_target = 'clinic' then + if v_intent.tenant_id is null then + raise exception 'Intent sem tenant_id'; + end if; + else + -- therapist ou supervisor: vinculado ao user + v_user_id := v_intent.user_id; + if v_user_id is null then + v_user_id := v_intent.created_by_user_id; + end if; + end if; + + if v_target in ('therapist', 'supervisor') and v_user_id is null then + raise exception 'N??o foi poss??vel determinar user_id para assinatura %.', v_target; + end if; + + -- cancela assinatura ativa anterior + if v_target = 'clinic' then + update public.subscriptions + set status = 'cancelled', + cancelled_at = now() + where tenant_id = v_intent.tenant_id + and plan_id = v_plan_id + and status = 'active'; + else + -- therapist ou supervisor + update public.subscriptions + set status = 'cancelled', + cancelled_at = now() + where user_id = v_user_id + and plan_id = v_plan_id + and status = 'active' + and tenant_id is null; + end if; + + -- dura????o do plano (30 dias para mensal) + v_days := case + when lower(coalesce(v_intent.interval, 'month')) = 'year' then 365 + else 30 + end; + + -- cria nova assinatura + insert into public.subscriptions ( + user_id, + plan_id, + status, + started_at, + expires_at, + cancelled_at, + activated_at, + tenant_id, + plan_key, + interval, + source, + created_at, + updated_at + ) + values ( + case when v_target = 'clinic' then null else v_user_id end, + v_plan_id, + 'active', + now(), + now() + make_interval(days => v_days), + null, + now(), + case when v_target = 'clinic' then v_intent.tenant_id else null end, + v_intent.plan_key, + v_intent.interval, + 'manual', + now(), + now() + ) + returning * into v_sub; + + -- grava v??nculo intent ??? subscription + if v_target = 'clinic' then + update public.subscription_intents_tenant + set subscription_id = v_sub.id + where id = p_intent_id; + else + update public.subscription_intents_personal + set subscription_id = v_sub.id + where id = p_intent_id; + end if; + + return v_sub; +end; +$$; + + +-- +-- Name: admin_credit_addon(uuid, text, integer, uuid, text, text, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Cr??dito manual'::text, p_payment_method text DEFAULT 'manual'::text, p_price_cents integer DEFAULT 0) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_credit addon_credits%ROWTYPE; + v_balance_before INTEGER; + v_balance_after INTEGER; + v_tx_id UUID; +BEGIN + -- Upsert addon_credits + INSERT INTO addon_credits (tenant_id, addon_type, balance, total_purchased) + VALUES (p_tenant_id, p_addon_type, 0, 0) + ON CONFLICT (tenant_id, addon_type) DO NOTHING; + + -- Lock e leitura + SELECT * INTO v_credit + FROM addon_credits + WHERE tenant_id = p_tenant_id AND addon_type = p_addon_type + FOR UPDATE; + + v_balance_before := v_credit.balance; + v_balance_after := v_credit.balance + p_amount; + + -- Atualiza saldo + UPDATE addon_credits + SET balance = v_balance_after, + total_purchased = total_purchased + p_amount, + low_balance_notified = CASE WHEN v_balance_after > COALESCE(low_balance_threshold, 10) THEN false ELSE low_balance_notified END, + updated_at = now() + WHERE id = v_credit.id; + + -- Registra transa????o + INSERT INTO addon_transactions ( + tenant_id, addon_type, type, amount, + balance_before, balance_after, + product_id, description, + admin_user_id, payment_method, price_cents + ) VALUES ( + p_tenant_id, p_addon_type, 'purchase', p_amount, + v_balance_before, v_balance_after, + p_product_id, p_description, + auth.uid(), p_payment_method, p_price_cents + ) + RETURNING id INTO v_tx_id; + + RETURN jsonb_build_object( + 'success', true, + 'transaction_id', v_tx_id, + 'balance_before', v_balance_before, + 'balance_after', v_balance_after + ); +END; +$$; + + +-- +-- Name: FUNCTION admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) IS 'Admin adiciona cr??ditos de add-on a um tenant. Cria registro se n??o existir.'; + + +-- +-- Name: admin_delete_email_template_global(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_delete_email_template_global(p_id uuid) RETURNS boolean + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + DELETE FROM public.email_templates_global WHERE id = p_id; + IF NOT FOUND THEN + RAISE EXCEPTION 'Template com id % n??o encontrado', p_id; + END IF; + RETURN true; +END; +$$; + + +-- +-- Name: admin_fix_plan_target(text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_plan_id uuid; +begin + -- (opcional) restringe targets v??lidos + if p_new_target not in ('clinic','therapist') then + raise exception 'Target inv??lido: %', p_new_target using errcode='P0001'; + end if; + + -- trava o plano + select id into v_plan_id + from public.plans + where key = p_plan_key + for update; + + if v_plan_id is null then + raise exception 'Plano n??o encontrado: %', p_plan_key using errcode='P0001'; + end if; + + -- seguran??a: n??o mexer se existe subscription + if exists (select 1 from public.subscriptions s where s.plan_id = v_plan_id) then + raise exception 'Plano % possui subscriptions. Migra????o bloqueada.', p_plan_key using errcode='P0001'; + end if; + + -- liga bypass SOMENTE nesta transa????o + perform set_config('app.plan_migration_bypass', '1', true); + + update public.plans + set target = p_new_target + where id = v_plan_id; + +end +$$; + + +-- +-- Name: admin_upsert_email_template_global(uuid, text, text, text, text, text, text, boolean, jsonb); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_upsert_email_template_global(p_id uuid DEFAULT NULL::uuid, p_key text DEFAULT NULL::text, p_domain text DEFAULT NULL::text, p_channel text DEFAULT 'email'::text, p_subject text DEFAULT NULL::text, p_body_html text DEFAULT NULL::text, p_body_text text DEFAULT NULL::text, p_is_active boolean DEFAULT true, p_variables jsonb DEFAULT '{}'::jsonb) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_result jsonb; + v_id uuid; +BEGIN + -- UPDATE existente + IF p_id IS NOT NULL THEN + UPDATE public.email_templates_global + SET + subject = COALESCE(p_subject, subject), + body_html = COALESCE(p_body_html, body_html), + body_text = p_body_text, + is_active = p_is_active, + variables = COALESCE(p_variables, variables), + version = version + 1 + WHERE id = p_id + RETURNING to_jsonb(email_templates_global.*) INTO v_result; + + IF v_result IS NULL THEN + RAISE EXCEPTION 'Template com id % n??o encontrado', p_id; + END IF; + + RETURN v_result; + END IF; + + -- INSERT novo + IF p_key IS NULL OR p_domain IS NULL OR p_subject IS NULL OR p_body_html IS NULL THEN + RAISE EXCEPTION 'key, domain, subject e body_html s??o obrigat??rios para novo template'; + END IF; + + INSERT INTO public.email_templates_global (key, domain, channel, subject, body_html, body_text, is_active, variables) + VALUES (p_key, p_domain, p_channel, p_subject, p_body_html, p_body_text, p_is_active, p_variables) + RETURNING to_jsonb(email_templates_global.*) INTO v_result; + + RETURN v_result; +END; +$$; + + +-- +-- Name: agenda_cfg_sync(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agenda_cfg_sync() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if new.agenda_view_mode = 'custom' then + new.usar_horario_admin_custom := true; + new.admin_inicio_visualizacao := new.agenda_custom_start; + new.admin_fim_visualizacao := new.agenda_custom_end; + else + new.usar_horario_admin_custom := false; + end if; + + return new; +end; +$$; + + +-- +-- Name: agendador_dias_disponiveis(text, integer, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer) RETURNS TABLE(data date, tem_slots boolean) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_owner_id uuid; + v_antecedencia int; + v_agora timestamptz; + v_data date; + v_data_inicio date; + v_data_fim date; + v_db_dow int; + v_tem_slot boolean; + v_bloqueado boolean; +BEGIN + SELECT c.owner_id, c.antecedencia_minima_horas + INTO v_owner_id, v_antecedencia + FROM public.agendador_configuracoes c + WHERE c.link_slug = p_slug AND c.ativo = true + LIMIT 1; + + IF v_owner_id IS NULL THEN RETURN; END IF; + + v_agora := now(); + v_data_inicio := make_date(p_ano, p_mes, 1); + v_data_fim := (v_data_inicio + interval '1 month' - interval '1 day')::date; + + v_data := v_data_inicio; + WHILE v_data <= v_data_fim LOOP + v_db_dow := extract(dow from v_data::timestamp)::int; + + -- ?????? Dia inteiro bloqueado? (agenda_bloqueios) ??????????????????????????????????????????????????????????????????????????? + SELECT EXISTS ( + SELECT 1 FROM public.agenda_bloqueios b + WHERE b.owner_id = v_owner_id + AND b.data_inicio <= v_data + AND COALESCE(b.data_fim, v_data) >= v_data + AND b.hora_inicio IS NULL -- bloqueio de dia inteiro + AND ( + (NOT b.recorrente) + OR (b.recorrente AND b.dia_semana = v_db_dow) + ) + ) INTO v_bloqueado; + + IF v_bloqueado THEN + v_data := v_data + 1; + CONTINUE; + END IF; + + -- ?????? Tem slots dispon??veis no dia? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????? + SELECT EXISTS ( + SELECT 1 FROM public.agenda_online_slots s + WHERE s.owner_id = v_owner_id + AND s.weekday = v_db_dow + AND s.enabled = true + AND (v_data::text || ' ' || s.time::text)::timestamp + AT TIME ZONE 'America/Sao_Paulo' + >= v_agora + (v_antecedencia || ' hours')::interval + ) INTO v_tem_slot; + + IF v_tem_slot THEN + data := v_data; + tem_slots := true; + RETURN NEXT; + END IF; + + v_data := v_data + 1; + END LOOP; +END; +$$; + + +-- +-- Name: agendador_gerar_slug(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agendador_gerar_slug() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_slug text; + v_exists boolean; +BEGIN + -- s?? gera se ativou e n??o tem slug ainda + IF NEW.ativo = true AND (NEW.link_slug IS NULL OR NEW.link_slug = '') THEN + LOOP + v_slug := lower(substring(replace(gen_random_uuid()::text, '-', ''), 1, 8)); + SELECT EXISTS ( + SELECT 1 FROM public.agendador_configuracoes + WHERE link_slug = v_slug AND owner_id <> NEW.owner_id + ) INTO v_exists; + EXIT WHEN NOT v_exists; + END LOOP; + NEW.link_slug := v_slug; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: agendador_slots_disponiveis(text, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) RETURNS TABLE(hora time without time zone, disponivel boolean) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_owner_id uuid; + v_duracao int; + v_antecedencia int; + v_agora timestamptz; + v_db_dow int; + v_slot time; + v_slot_fim time; + v_slot_ts timestamptz; + v_ocupado boolean; + -- loop de recorr??ncias + v_rule RECORD; + v_rule_start_dow int; + v_first_occ date; + v_day_diff int; + v_ex_type text; +BEGIN + SELECT c.owner_id, c.duracao_sessao_min, c.antecedencia_minima_horas + INTO v_owner_id, v_duracao, v_antecedencia + FROM public.agendador_configuracoes c + WHERE c.link_slug = p_slug AND c.ativo = true + LIMIT 1; + + IF v_owner_id IS NULL THEN RETURN; END IF; + + v_agora := now(); + v_db_dow := extract(dow from p_data::timestamp)::int; + + -- ?????? Dia inteiro bloqueado? (agenda_bloqueios sem hora) ????????????????????????????????????????????????????????? + -- Se sim, n??o h?? nenhum slot dispon??vel ??? retorna vazio. + IF EXISTS ( + SELECT 1 FROM public.agenda_bloqueios b + WHERE b.owner_id = v_owner_id + AND b.data_inicio <= p_data + AND COALESCE(b.data_fim, p_data) >= p_data + AND b.hora_inicio IS NULL -- bloqueio de dia inteiro + AND ( + (NOT b.recorrente) + OR (b.recorrente AND b.dia_semana = v_db_dow) + ) + ) THEN + RETURN; + END IF; + + FOR v_slot IN + SELECT s.time + FROM public.agenda_online_slots s + WHERE s.owner_id = v_owner_id + AND s.weekday = v_db_dow + AND s.enabled = true + ORDER BY s.time + LOOP + v_slot_fim := v_slot + (v_duracao || ' minutes')::interval; + v_ocupado := false; + + -- ?????? Anteced??ncia m??nima ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + v_slot_ts := (p_data::text || ' ' || v_slot::text)::timestamp + AT TIME ZONE 'America/Sao_Paulo'; + IF v_slot_ts < v_agora + (v_antecedencia || ' hours')::interval THEN + v_ocupado := true; + END IF; + + -- ?????? Bloqueio de hor??rio espec??fico (agenda_bloqueios com hora) ????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 FROM public.agenda_bloqueios b + WHERE b.owner_id = v_owner_id + AND b.data_inicio <= p_data + AND COALESCE(b.data_fim, p_data) >= p_data + AND b.hora_inicio IS NOT NULL + AND b.hora_inicio < v_slot_fim + AND b.hora_fim > v_slot + AND ( + (NOT b.recorrente) + OR (b.recorrente AND b.dia_semana = v_db_dow) + ) + ) INTO v_ocupado; + END IF; + + -- ?????? Eventos avulsos internos (agenda_eventos) ???????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 FROM public.agenda_eventos e + WHERE e.owner_id = v_owner_id + AND e.status::text NOT IN ('cancelado', 'faltou') + AND (e.inicio_em AT TIME ZONE 'America/Sao_Paulo')::date = p_data + AND (e.inicio_em AT TIME ZONE 'America/Sao_Paulo')::time < v_slot_fim + AND (e.fim_em AT TIME ZONE 'America/Sao_Paulo')::time > v_slot + ) INTO v_ocupado; + END IF; + + -- ?????? Recorr??ncias ativas (recurrence_rules) ????????????????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + FOR v_rule IN + SELECT + r.id, + r.start_date::date AS start_date, + r.end_date::date AS end_date, + r.start_time::time AS start_time, + r.end_time::time AS end_time, + COALESCE(r.interval, 1)::int AS interval + FROM public.recurrence_rules r + WHERE r.owner_id = v_owner_id + AND r.status = 'ativo' + AND p_data >= r.start_date::date + AND (r.end_date IS NULL OR p_data <= r.end_date::date) + AND v_db_dow = ANY(r.weekdays) + AND r.start_time::time < v_slot_fim + AND r.end_time::time > v_slot + LOOP + v_rule_start_dow := extract(dow from v_rule.start_date)::int; + v_first_occ := v_rule.start_date + + (((v_db_dow - v_rule_start_dow + 7) % 7))::int; + v_day_diff := (p_data - v_first_occ)::int; + + IF v_day_diff >= 0 AND v_day_diff % (7 * v_rule.interval) = 0 THEN + v_ex_type := NULL; + SELECT ex.type INTO v_ex_type + FROM public.recurrence_exceptions ex + WHERE ex.recurrence_id = v_rule.id + AND ex.original_date = p_data + LIMIT 1; + + IF v_ex_type IS NULL OR v_ex_type NOT IN ( + 'cancel_session', 'patient_missed', + 'therapist_canceled', 'holiday_block', + 'reschedule_session' + ) THEN + v_ocupado := true; + EXIT; + END IF; + END IF; + END LOOP; + END IF; + + -- ?????? Recorr??ncias remarcadas para este dia ???????????????????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 + FROM public.recurrence_exceptions ex + JOIN public.recurrence_rules r ON r.id = ex.recurrence_id + WHERE r.owner_id = v_owner_id + AND r.status = 'ativo' + AND ex.type = 'reschedule_session' + AND ex.new_date = p_data + AND COALESCE(ex.new_start_time, r.start_time)::time < v_slot_fim + AND COALESCE(ex.new_end_time, r.end_time)::time > v_slot + ) INTO v_ocupado; + END IF; + + -- ?????? Solicita????es p??blicas pendentes ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 FROM public.agendador_solicitacoes sol + WHERE sol.owner_id = v_owner_id + AND sol.status = 'pendente' + AND sol.data_solicitada = p_data + AND sol.hora_solicitada = v_slot + AND (sol.reservado_ate IS NULL OR sol.reservado_ate > v_agora) + ) INTO v_ocupado; + END IF; + + hora := v_slot; + disponivel := NOT v_ocupado; + RETURN NEXT; + END LOOP; +END; +$$; + + +-- +-- Name: auto_create_financial_record_from_session(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.auto_create_financial_record_from_session() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_price NUMERIC(10,2); + v_services_total NUMERIC(10,2); + v_already_billed BOOLEAN; +BEGIN + -- ?????? Guards de sa??da r??pida ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + + -- S?? processa quando o status muda PARA 'realizado' + IF NEW.status::TEXT <> 'realizado' THEN + RETURN NEW; + END IF; + + -- S?? processa quando houve mudan??a real de status + IF OLD.status IS NOT DISTINCT FROM NEW.status THEN + RETURN NEW; + END IF; + + -- S?? sess??es (n??o bloqueios, feriados, etc.) + IF NEW.tipo::TEXT <> 'sessao' THEN + RETURN NEW; + END IF; + + -- Paciente obrigat??rio para vincular a cobran??a + IF NEW.patient_id IS NULL THEN + RETURN NEW; + END IF; + + -- Sess??es de pacote t??m cobran??a gerenciada por billing_contract + IF NEW.billing_contract_id IS NOT NULL THEN + RETURN NEW; + END IF; + + -- Idempot??ncia: j?? existe financial_record para este evento? + SELECT billed INTO v_already_billed + FROM public.agenda_eventos + WHERE id = NEW.id; + + IF v_already_billed = TRUE THEN + -- Confirma no financial_records tamb??m (dupla verifica????o) + IF EXISTS ( + SELECT 1 FROM public.financial_records + WHERE agenda_evento_id = NEW.id AND deleted_at IS NULL + ) THEN + RETURN NEW; + END IF; + END IF; + + -- ?????? Busca do pre??o ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + + v_price := NULL; + + -- Prioridade 1: soma dos servi??os da regra de recorr??ncia + IF NEW.recurrence_id IS NOT NULL THEN + SELECT COALESCE(SUM(rrs.final_price), 0) + INTO v_services_total + FROM public.recurrence_rule_services rrs + WHERE rrs.rule_id = NEW.recurrence_id; + + IF v_services_total > 0 THEN + v_price := v_services_total; + END IF; + + -- Prioridade 2: price direto da regra (fallback se sem servi??os) + IF v_price IS NULL OR v_price = 0 THEN + SELECT price INTO v_price + FROM public.recurrence_rules + WHERE id = NEW.recurrence_id; + END IF; + END IF; + + -- Prioridade 3: price do pr??prio evento de agenda + IF v_price IS NULL OR v_price = 0 THEN + v_price := NEW.price; + END IF; + + -- Sem pre??o ??? n??o criar registro (n??o ?? erro, apenas skip silencioso) + IF v_price IS NULL OR v_price <= 0 THEN + RETURN NEW; + END IF; + + -- ?????? Cria????o do financial_record ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + + INSERT INTO public.financial_records ( + owner_id, + tenant_id, + patient_id, + agenda_evento_id, + type, + amount, + discount_amount, + final_amount, + clinic_fee_pct, + clinic_fee_amount, + status, + due_date + -- payment_method: NULL at?? o momento do pagamento (mark_as_paid preenche) + ) VALUES ( + NEW.owner_id, + NEW.tenant_id, + NEW.patient_id, + NEW.id, + 'receita', + v_price, + 0, + v_price, + 0, -- clinic_fee_pct: sem campo de configura????o global no schema atual. + 0, -- clinic_fee_amount: calculado manualmente ou via update posterior. + 'pending', + (NEW.inicio_em::DATE + 7) -- vencimento padr??o: 7 dias ap??s a sess??o + ); + + -- ?????? Marca sess??o como billed ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + -- UPDATE em billed (n??o em status) ??? n??o re-dispara este trigger + UPDATE public.agenda_eventos + SET billed = TRUE + WHERE id = NEW.id; + + RETURN NEW; + +EXCEPTION + WHEN OTHERS THEN + -- Log silencioso: nunca bloquear a agenda por falha financeira + RAISE WARNING '[auto_create_financial_record_from_session] evento=% erro=%', + NEW.id, SQLERRM; + RETURN NEW; +END; +$$; + + +-- +-- Name: FUNCTION auto_create_financial_record_from_session(); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.auto_create_financial_record_from_session() IS 'Trigger que cria automaticamente um financial_record (receita, pending) quando uma sess??o de agenda ?? marcada como realizada. Prioridade de pre??o: recurrence_rule_services > recurrence_rules.price > agenda_eventos.price. Skip silencioso se sem pre??o, pacote ou registro j?? existente.'; + + +-- +-- Name: can_delete_patient(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.can_delete_patient(p_patient_id uuid) RETURNS boolean + LANGUAGE sql STABLE SECURITY DEFINER + AS $$ + SELECT NOT EXISTS ( + SELECT 1 FROM public.agenda_eventos WHERE patient_id = p_patient_id + UNION ALL + SELECT 1 FROM public.recurrence_rules WHERE patient_id = p_patient_id + UNION ALL + SELECT 1 FROM public.billing_contracts WHERE patient_id = p_patient_id + ); +$$; + + +-- +-- Name: cancel_notifications_on_opt_out(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_notifications_on_opt_out() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + -- WhatsApp opt-out + IF OLD.whatsapp_opt_in = true AND NEW.whatsapp_opt_in = false THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, 'whatsapp' + ); + END IF; + -- Email opt-out + IF OLD.email_opt_in = true AND NEW.email_opt_in = false THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, 'email' + ); + END IF; + -- SMS opt-out + IF OLD.sms_opt_in = true AND NEW.sms_opt_in = false THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, 'sms' + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: cancel_notifications_on_session_cancel(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_notifications_on_session_cancel() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF NEW.status IN ('cancelado', 'excluido') + AND OLD.status NOT IN ('cancelado', 'excluido') + THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, NULL, NEW.id + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: cancel_patient_pending_notifications(uuid, text, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text DEFAULT NULL::text, p_evento_id uuid DEFAULT NULL::uuid) RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_canceled integer; +BEGIN + UPDATE public.notification_queue + SET status = 'cancelado', + updated_at = now() + WHERE patient_id = p_patient_id + AND status IN ('pendente', 'processando') + AND (p_channel IS NULL OR channel = p_channel) + AND (p_evento_id IS NULL OR agenda_evento_id = p_evento_id); + + GET DIAGNOSTICS v_canceled = ROW_COUNT; + RETURN v_canceled; +END; +$$; + + +-- +-- Name: cancel_recurrence_from(uuid, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + UPDATE public.recurrence_rules + SET + end_date = p_from_date - INTERVAL '1 day', + open_ended = false, + status = CASE + WHEN p_from_date <= start_date THEN 'cancelado' + ELSE status + END, + updated_at = now() + WHERE id = p_recurrence_id; +END; +$$; + + +-- +-- Name: cancel_subscription(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_subscription(p_subscription_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_owner_type text; + v_owner_ref uuid; +begin + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + if v_sub.status = 'canceled' then + return v_sub; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set status = 'canceled', + cancel_at_period_end = false, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'canceled', + v_sub.plan_id, + v_sub.plan_id, + auth.uid(), + 'Cancelamento manual via admin', + 'admin_panel', + jsonb_build_object('previous_status', 'active') + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation(owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; + +end; +$$; + + +-- +-- Name: cancelar_eventos_serie(uuid, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone DEFAULT now()) RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_count integer; +BEGIN + UPDATE public.agenda_eventos + SET status = 'cancelado', + updated_at = now() + WHERE serie_id = p_serie_id + AND inicio_em >= p_a_partir_de + AND status NOT IN ('realizado', 'cancelado'); + + GET DIAGNOSTICS v_count = ROW_COUNT; + RETURN v_count; +END; +$$; + + +-- +-- Name: FUNCTION cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone) IS 'Cancela todos os eventos futuros de uma s??rie a partir de p_a_partir_de (inclusive). + N??o cancela eventos j?? realizados.'; + + +-- +-- Name: change_subscription_plan(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_old_plan uuid; + v_new_key text; + + v_owner_type text; + v_owner_ref uuid; + + v_new_target text; + v_sub_target text; +begin + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + v_old_plan := v_sub.plan_id; + + if v_old_plan = p_new_plan_id then + return v_sub; + end if; + + select key, target + into v_new_key, v_new_target + from public.plans + where id = p_new_plan_id; + + if v_new_key is null then + raise exception 'Plano n??o encontrado'; + end if; + + v_new_target := lower(coalesce(v_new_target, '')); + + v_sub_target := case + when v_sub.tenant_id is not null then 'clinic' + else 'therapist' + end; + + if v_new_target <> v_sub_target then + raise exception 'Plano inv??lido para este tipo de assinatura. Assinatura ?? % e o plano ?? %.', + v_sub_target, v_new_target + using errcode = 'P0001'; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set plan_id = p_new_plan_id, + plan_key = v_new_key, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'plan_changed', + v_old_plan, + p_new_plan_id, + auth.uid(), + 'Plan change via DEV menu', + 'dev_menu', + jsonb_build_object( + 'previous_plan', v_old_plan, + 'new_plan', p_new_plan_id, + 'new_plan_key', v_new_key, + 'new_plan_target', v_new_target + ) + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation (owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; +end; +$$; + + +-- +-- Name: cleanup_notification_queue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cleanup_notification_queue() RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_deleted integer; +BEGIN + DELETE FROM public.notification_queue + WHERE status IN ('enviado', 'cancelado', 'ignorado') + AND created_at < now() - interval '90 days'; + + GET DIAGNOSTICS v_deleted = ROW_COUNT; + RETURN v_deleted; +END; +$$; + + +-- +-- Name: create_clinic_tenant(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_clinic_tenant(p_name text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_uid uuid; + v_tenant uuid; + v_name text; +begin + v_uid := auth.uid(); + if v_uid is null then + raise exception 'Not authenticated'; + end if; + + v_name := nullif(trim(coalesce(p_name, '')), ''); + if v_name is null then + v_name := 'Cl??nica'; + end if; + + insert into public.tenants (name, kind, created_at) + values (v_name, 'clinic', now()) + returning id into v_tenant; + + insert into public.tenant_members (tenant_id, user_id, role, status, created_at) + values (v_tenant, v_uid, 'tenant_admin', 'active', now()); + + return v_tenant; +end; +$$; + + +-- +-- Name: financial_records; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.financial_records ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + type public.financial_record_type DEFAULT 'receita'::public.financial_record_type NOT NULL, + amount numeric(10,2) NOT NULL, + description text, + category text, + payment_method text, + paid_at timestamp with time zone, + due_date date, + installments smallint DEFAULT 1, + installment_number smallint DEFAULT 1, + installment_group uuid, + agenda_evento_id uuid, + patient_id uuid, + clinic_fee_pct numeric(5,2) DEFAULT 0, + clinic_fee_amount numeric(10,2) DEFAULT 0, + net_amount numeric(10,2) GENERATED ALWAYS AS ((amount - clinic_fee_amount)) STORED, + insurance_plan_id uuid, + notes text, + tags text[], + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + discount_amount numeric(10,2) DEFAULT 0 NOT NULL, + final_amount numeric(10,2) DEFAULT 0 NOT NULL, + status text DEFAULT 'pending'::text NOT NULL, + category_id uuid, + CONSTRAINT financial_records_amount_check CHECK ((amount >= (0)::numeric)), + CONSTRAINT financial_records_clinic_fee_amount_check CHECK ((clinic_fee_amount >= (0)::numeric)), + CONSTRAINT financial_records_clinic_fee_pct_check CHECK (((clinic_fee_pct >= (0)::numeric) AND (clinic_fee_pct <= (100)::numeric))), + CONSTRAINT financial_records_discount_amount_check CHECK ((discount_amount >= (0)::numeric)), + CONSTRAINT financial_records_final_amount_check CHECK ((final_amount >= (0)::numeric)), + CONSTRAINT financial_records_installments_check CHECK ((installments >= 1)), + CONSTRAINT financial_records_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'paid'::text, 'partial'::text, 'overdue'::text, 'cancelled'::text, 'refunded'::text]))) +); + + +-- +-- Name: create_financial_record_for_session(uuid, uuid, uuid, uuid, numeric, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_financial_record_for_session(p_tenant_id uuid, p_owner_id uuid, p_patient_id uuid, p_agenda_evento_id uuid, p_amount numeric, p_due_date date) RETURNS SETOF public.financial_records + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_existing public.financial_records%ROWTYPE; + v_new public.financial_records%ROWTYPE; +BEGIN + -- Idempot??ncia: retorna o registro existente se j?? foi criado + SELECT * INTO v_existing + FROM public.financial_records + WHERE agenda_evento_id = p_agenda_evento_id + AND deleted_at IS NULL + LIMIT 1; + + IF FOUND THEN + RETURN NEXT v_existing; + RETURN; + END IF; + + -- Cria o novo registro + INSERT INTO public.financial_records ( + tenant_id, + owner_id, + patient_id, + agenda_evento_id, + amount, + discount_amount, + final_amount, + status, + due_date + ) VALUES ( + p_tenant_id, + p_owner_id, + p_patient_id, + p_agenda_evento_id, + p_amount, + 0, + p_amount, + 'pending', + p_due_date + ) + RETURNING * INTO v_new; + + -- Marca o evento da agenda como billed = true + UPDATE public.agenda_eventos + SET billed = TRUE + WHERE id = p_agenda_evento_id; + + RETURN NEXT v_new; +END; +$$; + + +-- +-- Name: create_patient_intake_request(text, text, text, text, text, boolean); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text DEFAULT NULL::text, p_phone text DEFAULT NULL::text, p_notes text DEFAULT NULL::text, p_consent boolean DEFAULT false) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_owner uuid; + v_active boolean; + v_expires timestamptz; + v_max_uses int; + v_uses int; + v_id uuid; +begin + select owner_id, active, expires_at, max_uses, uses + into v_owner, v_active, v_expires, v_max_uses, v_uses + from public.patient_invites + where token = p_token + limit 1; + + if v_owner is null then + raise exception 'Token inv??lido'; + end if; + + if v_active is not true then + raise exception 'Link desativado'; + end if; + + if v_expires is not null and now() > v_expires then + raise exception 'Link expirado'; + end if; + + if v_max_uses is not null and v_uses >= v_max_uses then + raise exception 'Limite de uso atingido'; + end if; + + if p_name is null or length(trim(p_name)) = 0 then + raise exception 'Nome ?? obrigat??rio'; + end if; + + insert into public.patient_intake_requests + (owner_id, token, name, email, phone, notes, consent, status) + values + (v_owner, p_token, trim(p_name), + nullif(lower(trim(p_email)), ''), + nullif(trim(p_phone), ''), + nullif(trim(p_notes), ''), + coalesce(p_consent, false), + 'new') + returning id into v_id; + + update public.patient_invites + set uses = uses + 1 + where token = p_token; + + return v_id; +end; +$$; + + +-- +-- Name: create_patient_intake_request_v2(text, jsonb); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $_$ +declare + v_owner_id uuid; + v_intake_id uuid; + v_birth_raw text; + v_birth date; +begin + select owner_id + into v_owner_id + from public.patient_invites + where token = p_token; + + if v_owner_id is null then + raise exception 'Token inv??lido ou expirado'; + end if; + + v_birth_raw := nullif(trim(coalesce( + p_payload->>'data_nascimento', + '' + )), ''); + + v_birth := case + when v_birth_raw is null then null + when v_birth_raw ~ '^\d{4}-\d{2}-\d{2}$' then v_birth_raw::date + when v_birth_raw ~ '^\d{2}-\d{2}-\d{4}$' then to_date(v_birth_raw, 'DD-MM-YYYY') + else null + end; + + insert into public.patient_intake_requests ( + owner_id, + token, + status, + consent, + + nome_completo, + email_principal, + telefone, + + avatar_url, -- ???? AQUI + + data_nascimento, + cpf, + rg, + genero, + estado_civil, + profissao, + escolaridade, + nacionalidade, + naturalidade, + + cep, + pais, + cidade, + estado, + endereco, + numero, + complemento, + bairro, + + observacoes, + notas_internas, + + encaminhado_por, + onde_nos_conheceu + ) + values ( + v_owner_id, + p_token, + 'new', + coalesce((p_payload->>'consent')::boolean, false), + + nullif(trim(p_payload->>'nome_completo'), ''), + nullif(trim(p_payload->>'email_principal'), ''), + nullif(regexp_replace(coalesce(p_payload->>'telefone',''), '\D', '', 'g'), ''), + + nullif(trim(p_payload->>'avatar_url'), ''), -- ???? AQUI + + v_birth, + nullif(regexp_replace(coalesce(p_payload->>'cpf',''), '\D', '', 'g'), ''), + nullif(trim(p_payload->>'rg'), ''), + nullif(trim(p_payload->>'genero'), ''), + nullif(trim(p_payload->>'estado_civil'), ''), + nullif(trim(p_payload->>'profissao'), ''), + nullif(trim(p_payload->>'escolaridade'), ''), + nullif(trim(p_payload->>'nacionalidade'), ''), + nullif(trim(p_payload->>'naturalidade'), ''), + + nullif(regexp_replace(coalesce(p_payload->>'cep',''), '\D', '', 'g'), ''), + nullif(trim(p_payload->>'pais'), ''), + nullif(trim(p_payload->>'cidade'), ''), + nullif(trim(p_payload->>'estado'), ''), + nullif(trim(p_payload->>'endereco'), ''), + nullif(trim(p_payload->>'numero'), ''), + nullif(trim(p_payload->>'complemento'), ''), + nullif(trim(p_payload->>'bairro'), ''), + + nullif(trim(p_payload->>'observacoes'), ''), + nullif(trim(p_payload->>'notas_internas'), ''), + + nullif(trim(p_payload->>'encaminhado_por'), ''), + nullif(trim(p_payload->>'onde_nos_conheceu'), '') + ) + returning id into v_intake_id; + + return v_intake_id; +end; +$_$; + + +-- +-- Name: create_support_session(uuid, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer DEFAULT 60) RETURNS json + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_admin_id uuid; + v_role text; + v_token text; + v_expires timestamp with time zone; + v_session support_sessions; +BEGIN + -- Verifica autentica????o + v_admin_id := auth.uid(); + IF v_admin_id IS NULL THEN + RAISE EXCEPTION 'N??o autenticado.' USING ERRCODE = 'P0001'; + END IF; + + -- Verifica role saas_admin + SELECT role INTO v_role + FROM public.profiles + WHERE id = v_admin_id; + + IF v_role <> 'saas_admin' THEN + RAISE EXCEPTION 'Acesso negado. Somente saas_admin pode criar sess??es de suporte.' + USING ERRCODE = 'P0002'; + END IF; + + -- Valida TTL (1 a 120 minutos) + IF p_ttl_minutes < 1 OR p_ttl_minutes > 120 THEN + RAISE EXCEPTION 'TTL inv??lido. Use entre 1 e 120 minutos.' + USING ERRCODE = 'P0003'; + END IF; + + -- Valida tenant + IF NOT EXISTS (SELECT 1 FROM public.tenants WHERE id = p_tenant_id) THEN + RAISE EXCEPTION 'Tenant n??o encontrado.' + USING ERRCODE = 'P0004'; + END IF; + + -- Gera token ??nico (64 chars hex, sem pgcrypto) + v_token := replace(gen_random_uuid()::text, '-', '') || replace(gen_random_uuid()::text, '-', ''); + v_expires := now() + (p_ttl_minutes || ' minutes')::interval; + + -- Insere sess??o + INSERT INTO public.support_sessions (tenant_id, admin_id, token, expires_at) + VALUES (p_tenant_id, v_admin_id, v_token, v_expires) + RETURNING * INTO v_session; + + RETURN json_build_object( + 'token', v_session.token, + 'expires_at', v_session.expires_at, + 'session_id', v_session.id + ); +END; +$$; + + +-- +-- Name: therapist_payouts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.therapist_payouts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + period_start date NOT NULL, + period_end date NOT NULL, + total_sessions integer DEFAULT 0 NOT NULL, + gross_amount numeric(10,2) DEFAULT 0 NOT NULL, + clinic_fee_total numeric(10,2) DEFAULT 0 NOT NULL, + net_amount numeric(10,2) DEFAULT 0 NOT NULL, + status text DEFAULT 'pending'::text NOT NULL, + paid_at timestamp with time zone, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT therapist_payouts_clinic_fee_total_check CHECK ((clinic_fee_total >= (0)::numeric)), + CONSTRAINT therapist_payouts_gross_amount_check CHECK ((gross_amount >= (0)::numeric)), + CONSTRAINT therapist_payouts_net_amount_check CHECK ((net_amount >= (0)::numeric)), + CONSTRAINT therapist_payouts_period_chk CHECK ((period_end >= period_start)), + CONSTRAINT therapist_payouts_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'paid'::text, 'cancelled'::text]))) +); + + +-- +-- Name: create_therapist_payout(uuid, uuid, date, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) RETURNS public.therapist_payouts + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_payout public.therapist_payouts%ROWTYPE; + v_total_sessions INTEGER; + v_gross NUMERIC(10,2); + v_clinic_fee NUMERIC(10,2); + v_net NUMERIC(10,2); +BEGIN + -- ?????? Verifica????o de permiss??o ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + -- Apenas o pr??prio terapeuta ou o tenant_admin pode criar o repasse + IF auth.uid() <> p_therapist_id AND NOT public.is_tenant_admin(p_tenant_id) THEN + RAISE EXCEPTION 'Sem permiss??o para criar repasse para este terapeuta.'; + END IF; + + -- ?????? Verifica se j?? existe repasse para o mesmo per??odo ??????????????????????????????????????????????????? + IF EXISTS ( + SELECT 1 FROM public.therapist_payouts + WHERE owner_id = p_therapist_id + AND tenant_id = p_tenant_id + AND period_start = p_period_start + AND period_end = p_period_end + AND status <> 'cancelled' + ) THEN + RAISE EXCEPTION + 'J?? existe um repasse ativo para o per??odo % a % deste terapeuta.', + p_period_start, p_period_end; + END IF; + + -- ?????? Agrega os financial_records eleg??veis ?????????????????????????????????????????????????????????????????????????????????????????? + -- Eleg??veis: paid, receita, owner=terapeuta, tenant correto, paid_at no per??odo, + -- n??o soft-deleted, ainda n??o vinculados a nenhum payout. + SELECT + COUNT(*) AS total_sessions, + COALESCE(SUM(amount), 0) AS gross_amount, + COALESCE(SUM(clinic_fee_amount), 0) AS clinic_fee_total, + COALESCE(SUM(net_amount), 0) AS net_amount + INTO + v_total_sessions, v_gross, v_clinic_fee, v_net + FROM public.financial_records fr + WHERE fr.owner_id = p_therapist_id + AND fr.tenant_id = p_tenant_id + AND fr.type = 'receita' + AND fr.status = 'paid' + AND fr.deleted_at IS NULL + AND fr.paid_at::DATE BETWEEN p_period_start AND p_period_end + AND NOT EXISTS ( + SELECT 1 FROM public.therapist_payout_records tpr + WHERE tpr.financial_record_id = fr.id + ); + + -- Sem registros eleg??veis ??? n??o criar payout vazio + IF v_total_sessions = 0 THEN + RAISE EXCEPTION + 'Nenhum registro financeiro eleg??vel encontrado para o per??odo % a %.', + p_period_start, p_period_end; + END IF; + + -- ?????? Cria o repasse ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + INSERT INTO public.therapist_payouts ( + owner_id, + tenant_id, + period_start, + period_end, + total_sessions, + gross_amount, + clinic_fee_total, + net_amount, + status + ) VALUES ( + p_therapist_id, + p_tenant_id, + p_period_start, + p_period_end, + v_total_sessions, + v_gross, + v_clinic_fee, + v_net, + 'pending' + ) + RETURNING * INTO v_payout; + + -- ?????? Vincula os financial_records ao repasse ???????????????????????????????????????????????????????????????????????????????????? + INSERT INTO public.therapist_payout_records (payout_id, financial_record_id) + SELECT v_payout.id, fr.id + FROM public.financial_records fr + WHERE fr.owner_id = p_therapist_id + AND fr.tenant_id = p_tenant_id + AND fr.type = 'receita' + AND fr.status = 'paid' + AND fr.deleted_at IS NULL + AND fr.paid_at::DATE BETWEEN p_period_start AND p_period_end + AND NOT EXISTS ( + SELECT 1 FROM public.therapist_payout_records tpr + WHERE tpr.financial_record_id = fr.id + ); + + RETURN v_payout; +END; +$$; + + +-- +-- Name: FUNCTION create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) IS 'Cria um repasse para o terapeuta com todos os financial_records paid+receita do per??odo que ainda n??o estejam vinculados a outro repasse. Lan??a exce????o se n??o houver registros eleg??veis ou se j?? houver repasse ativo no per??odo.'; + + +-- +-- Name: current_member_id(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.current_member_id(p_tenant_id uuid) RETURNS uuid + LANGUAGE sql STABLE + AS $$ + select tm.id + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + limit 1 +$$; + + +-- +-- Name: current_member_role(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.current_member_role(p_tenant_id uuid) RETURNS text + LANGUAGE sql STABLE + AS $$ + select tm.role + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + limit 1 +$$; + + +-- +-- Name: debit_addon_credit(uuid, text, uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Consumo'::text) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_credit addon_credits%ROWTYPE; + v_balance_before INTEGER; + v_balance_after INTEGER; +BEGIN + -- Lock e leitura + SELECT * INTO v_credit + FROM addon_credits + WHERE tenant_id = p_tenant_id AND addon_type = p_addon_type AND is_active = true + FOR UPDATE; + + IF NOT FOUND THEN + RETURN jsonb_build_object('success', false, 'reason', 'no_credits', 'balance', 0); + END IF; + + -- Verifica saldo + IF v_credit.balance <= 0 THEN + RETURN jsonb_build_object('success', false, 'reason', 'insufficient_balance', 'balance', 0); + END IF; + + -- Verifica rate limit di??rio + IF v_credit.daily_limit IS NOT NULL THEN + -- Reset se passou do dia + IF v_credit.daily_reset_at IS NULL OR v_credit.daily_reset_at < date_trunc('day', now()) THEN + UPDATE addon_credits SET daily_used = 0, daily_reset_at = date_trunc('day', now()) + interval '1 day' WHERE id = v_credit.id; + v_credit.daily_used := 0; + END IF; + + IF v_credit.daily_used >= v_credit.daily_limit THEN + RETURN jsonb_build_object('success', false, 'reason', 'daily_limit_reached', 'balance', v_credit.balance); + END IF; + END IF; + + -- Verifica rate limit hor??rio + IF v_credit.hourly_limit IS NOT NULL THEN + IF v_credit.hourly_reset_at IS NULL OR v_credit.hourly_reset_at < date_trunc('hour', now()) THEN + UPDATE addon_credits SET hourly_used = 0, hourly_reset_at = date_trunc('hour', now()) + interval '1 hour' WHERE id = v_credit.id; + v_credit.hourly_used := 0; + END IF; + + IF v_credit.hourly_used >= v_credit.hourly_limit THEN + RETURN jsonb_build_object('success', false, 'reason', 'hourly_limit_reached', 'balance', v_credit.balance); + END IF; + END IF; + + -- Verifica expira????o + IF v_credit.expires_at IS NOT NULL AND v_credit.expires_at < now() THEN + RETURN jsonb_build_object('success', false, 'reason', 'credits_expired', 'balance', v_credit.balance); + END IF; + + v_balance_before := v_credit.balance; + v_balance_after := v_credit.balance - 1; + + -- Debita + UPDATE addon_credits + SET balance = v_balance_after, + total_consumed = total_consumed + 1, + daily_used = COALESCE(daily_used, 0) + 1, + hourly_used = COALESCE(hourly_used, 0) + 1, + updated_at = now() + WHERE id = v_credit.id; + + -- Registra transa????o + INSERT INTO addon_transactions ( + tenant_id, addon_type, type, amount, + balance_before, balance_after, + queue_id, description + ) VALUES ( + p_tenant_id, p_addon_type, 'consume', -1, + v_balance_before, v_balance_after, + p_queue_id, p_description + ); + + RETURN jsonb_build_object( + 'success', true, + 'balance_before', v_balance_before, + 'balance_after', v_balance_after + ); +END; +$$; + + +-- +-- Name: FUNCTION debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) IS 'Debita 1 cr??dito de add-on. Verifica saldo, rate limits e expira????o.'; + + +-- +-- Name: delete_commitment_full(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_is_native boolean; + v_fields int := 0; + v_logs int := 0; + v_parent int := 0; +begin + if auth.uid() is null then + raise exception 'Not authenticated'; + end if; + + if not exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.status = 'active' + ) then + raise exception 'Not allowed'; + end if; + + select dc.is_native + into v_is_native + from public.determined_commitments dc + where dc.tenant_id = p_tenant_id + and dc.id = p_commitment_id; + + if v_is_native is null then + raise exception 'Commitment not found'; + end if; + + if v_is_native = true then + raise exception 'Cannot delete native commitment'; + end if; + + delete from public.determined_commitment_fields + where tenant_id = p_tenant_id + and commitment_id = p_commitment_id; + get diagnostics v_fields = row_count; + + delete from public.commitment_time_logs + where tenant_id = p_tenant_id + and commitment_id = p_commitment_id; + get diagnostics v_logs = row_count; + + delete from public.determined_commitments + where tenant_id = p_tenant_id + and id = p_commitment_id; + get diagnostics v_parent = row_count; + + if v_parent <> 1 then + raise exception 'Parent not deleted (RLS/owner issue).'; + end if; + + return jsonb_build_object( + 'ok', true, + 'deleted', jsonb_build_object( + 'fields', v_fields, + 'logs', v_logs, + 'commitment', v_parent + ) + ); +end; +$$; + + +-- +-- Name: delete_determined_commitment(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_is_native boolean; + v_fields_deleted int := 0; + v_logs_deleted int := 0; + v_commitment_deleted int := 0; +begin + if auth.uid() is null then + raise exception 'Not authenticated'; + end if; + + if not exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.status = 'active' + ) then + raise exception 'Not allowed'; + end if; + + select dc.is_native + into v_is_native + from public.determined_commitments dc + where dc.tenant_id = p_tenant_id + and dc.id = p_commitment_id; + + if v_is_native is null then + raise exception 'Commitment not found for tenant'; + end if; + + if v_is_native = true then + raise exception 'Cannot delete native commitment'; + end if; + + delete from public.determined_commitment_fields f + where f.tenant_id = p_tenant_id + and f.commitment_id = p_commitment_id; + get diagnostics v_fields_deleted = row_count; + + delete from public.commitment_time_logs l + where l.tenant_id = p_tenant_id + and l.commitment_id = p_commitment_id; + get diagnostics v_logs_deleted = row_count; + + delete from public.determined_commitments dc + where dc.tenant_id = p_tenant_id + and dc.id = p_commitment_id; + get diagnostics v_commitment_deleted = row_count; + + if v_commitment_deleted <> 1 then + raise exception 'Delete did not remove the commitment (tenant mismatch?)'; + end if; + + return jsonb_build_object( + 'ok', true, + 'tenant_id', p_tenant_id, + 'commitment_id', p_commitment_id, + 'deleted', jsonb_build_object( + 'fields', v_fields_deleted, + 'logs', v_logs_deleted, + 'commitment', v_commitment_deleted + ) + ); +end; +$$; + + +-- +-- Name: dev_list_auth_users(integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_list_auth_users(p_limit integer DEFAULT 50) RETURNS TABLE(id uuid, email text, created_at timestamp with time zone) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +begin + -- s?? saas_admin pode ver + if not exists ( + select 1 + from public.profiles p + where p.id = auth.uid() + and p.role = 'saas_admin' + ) then + return; + end if; + + return query + select + u.id, + u.email, + u.created_at + from auth.users u + order by u.created_at desc + limit greatest(1, least(coalesce(p_limit, 50), 500)); +end; +$$; + + +-- +-- Name: dev_list_custom_users(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_list_custom_users() RETURNS TABLE(user_id uuid, email text, created_at timestamp with time zone, global_role text, tenant_role text, tenant_id uuid, password_dev text, kind text) + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + with base as ( + select + u.id as user_id, + lower(u.email) as email, + u.created_at + from auth.users u + where lower(u.email) not in ( + 'clinic@agenciapsi.com.br', + 'therapist@agenciapsi.com.br', + 'patient@agenciapsi.com.br', + 'saas@agenciapsi.com.br' + ) + ), + prof as ( + select p.id, p.role as global_role + from public.profiles p + ), + last_membership as ( + select distinct on (tm.user_id) + tm.user_id, + tm.tenant_id, + tm.role as tenant_role, + tm.created_at + from public.tenant_members tm + where tm.status = 'active' + order by tm.user_id, tm.created_at desc + ) + select + b.user_id, + b.email, + b.created_at, + pr.global_role, + lm.tenant_role, + lm.tenant_id, + dc.password_dev, + dc.kind + from base b + left join prof pr on pr.id = b.user_id + left join last_membership lm on lm.user_id = b.user_id + left join public.dev_user_credentials dc on lower(dc.email) = b.email + order by b.created_at desc; +$$; + + +-- +-- Name: dev_list_intent_leads(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_list_intent_leads() RETURNS TABLE(email text, last_intent_at timestamp with time zone, plan_key text, billing_interval text, status text, tenant_id uuid) + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + select + lower(si.email) as email, + max(si.created_at) as last_intent_at, + (array_agg(si.plan_key order by si.created_at desc))[1] as plan_key, + (array_agg(si.interval order by si.created_at desc))[1] as billing_interval, + (array_agg(si.status order by si.created_at desc))[1] as status, + (array_agg(si.tenant_id order by si.created_at desc))[1] as tenant_id + from public.subscription_intents si + where si.email is not null + and not exists ( + select 1 + from auth.users au + where lower(au.email) = lower(si.email) + ) + group by lower(si.email) + order by max(si.created_at) desc; +$$; + + +-- +-- Name: dev_public_debug_snapshot(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_public_debug_snapshot() RETURNS TABLE(users_total integer, tenants_total integer, intents_new_total integer, latest_intents jsonb) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $_$ +declare + v_latest jsonb; +begin + select jsonb_agg( + jsonb_build_object( + 'created_at', si.created_at, + 'email_masked', + regexp_replace(lower(si.email), '(^.).*(@.*$)', '\1***\2'), + 'plan_key', si.plan_key, + 'status', si.status + ) + order by si.created_at desc + ) + into v_latest + from ( + select si.* + from public.subscription_intents si + where si.email is not null + order by si.created_at desc + limit 5 + ) si; + + return query + select + (select count(*)::int from auth.users) as users_total, + (select count(*)::int from public.tenants) as tenants_total, + (select count(*)::int from public.subscription_intents where status = 'new') as intents_new_total, + coalesce(v_latest, '[]'::jsonb) as latest_intents; +end; +$_$; + + +-- +-- Name: ensure_personal_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.ensure_personal_tenant() RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_uid uuid; + v_existing uuid; +BEGIN + v_uid := auth.uid(); + IF v_uid IS NULL THEN + RAISE EXCEPTION 'Not authenticated'; + END IF; + + SELECT tm.tenant_id INTO v_existing + FROM public.tenant_members tm + JOIN public.tenants t ON t.id = tm.tenant_id + WHERE tm.user_id = v_uid + AND tm.status = 'active' + AND t.kind IN ('therapist', 'saas') + ORDER BY tm.created_at DESC + LIMIT 1; + + IF v_existing IS NOT NULL THEN + RETURN v_existing; + END IF; + + RETURN public.provision_account_tenant(v_uid, 'therapist'); +END; +$$; + + +-- +-- Name: ensure_personal_tenant_for_user(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_uid uuid; + v_existing uuid; + v_tenant uuid; + v_email text; + v_name text; +begin + v_uid := p_user_id; + if v_uid is null then + raise exception 'Missing user id'; + end if; + + -- s?? considera tenant pessoal (kind='saas') + select tm.tenant_id + into v_existing + from public.tenant_members tm + join public.tenants t on t.id = tm.tenant_id + where tm.user_id = v_uid + and tm.status = 'active' + and t.kind = 'saas' + order by tm.created_at desc + limit 1; + + if v_existing is not null then + return v_existing; + end if; + + select email into v_email + from auth.users + where id = v_uid; + + v_name := coalesce(split_part(v_email, '@', 1), 'Conta'); + + insert into public.tenants (name, kind, created_at) + values (v_name || ' (Pessoal)', 'saas', now()) + returning id into v_tenant; + + insert into public.tenant_members (tenant_id, user_id, role, status, created_at) + values (v_tenant, v_uid, 'tenant_admin', 'active', now()); + + return v_tenant; +end; +$$; + + +-- +-- Name: faq_votar(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.faq_votar(faq_id uuid) RETURNS void + LANGUAGE sql SECURITY DEFINER + AS $$ + update public.saas_faq + set votos = votos + 1, + updated_at = now() + where id = faq_id + and ativo = true; +$$; + + +-- +-- Name: fix_all_subscription_mismatches(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.fix_all_subscription_mismatches() RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + r record; +begin + for r in + select distinct s.user_id as owner_id + from public.subscriptions s + where s.status = 'active' + and s.user_id is not null + loop + perform public.rebuild_owner_entitlements(r.owner_id); + end loop; +end; +$$; + + +-- +-- Name: fn_agenda_regras_semanais_no_overlap(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.fn_agenda_regras_semanais_no_overlap() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_count int; +begin + if new.ativo is false then + return new; + end if; + + select count(*) into v_count + from public.agenda_regras_semanais r + where r.owner_id = new.owner_id + and r.dia_semana = new.dia_semana + and r.ativo is true + and (tg_op = 'INSERT' or r.id <> new.id) + and (new.hora_inicio < r.hora_fim and new.hora_fim > r.hora_inicio); + + if v_count > 0 then + raise exception 'Janela sobreposta: j?? existe uma regra ativa nesse intervalo.'; + end if; + + return new; +end; +$$; + + +-- +-- Name: get_financial_report(uuid, date, date, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text DEFAULT 'month'::text) RETURNS TABLE(group_key text, group_label text, total_receitas numeric, total_despesas numeric, saldo numeric, total_pendente numeric, total_overdue numeric, count_records bigint) + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + AS $$ + + -- ?????? Valida p_group_by antes de executar ?????????????????????????????????????????????????????????????????????????????????????????????????????? + -- (lan??a erro se valor inv??lido; plpgsql seria necess??rio para isso em SQL puro, + -- ent??o usamos um CTE de valida????o com CASE WHEN para retornar vazio em vez de erro) + + WITH base AS ( + SELECT + fr.type, + fr.amount, + fr.final_amount, + fr.status, + fr.deleted_at, + -- Chave de agrupamento calculada conforme p_group_by + CASE p_group_by + WHEN 'month' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'YYYY-MM' + ) + WHEN 'week' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'IYYY-"W"IW' + ) + WHEN 'category' THEN COALESCE(fr.category_id::TEXT, fr.category, 'sem_categoria') + WHEN 'patient' THEN COALESCE(fr.patient_id::TEXT, 'sem_paciente') + ELSE NULL -- group_by inv??lido ??? group_key NULL ??? retorno vazio + END AS gkey, + -- Label leg??vel (enriquecido via JOIN abaixo quando poss??vel) + CASE p_group_by + WHEN 'month' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'YYYY-MM' + ) + WHEN 'week' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'IYYY-"W"IW' + ) + WHEN 'category' THEN COALESCE(fc.name, fr.category, 'Sem categoria') + WHEN 'patient' THEN COALESCE(p.nome_completo, fr.patient_id::TEXT, 'Sem paciente') + ELSE NULL + END AS glabel + FROM public.financial_records fr + LEFT JOIN public.financial_categories fc + ON fc.id = fr.category_id + LEFT JOIN public.patients p + ON p.id = fr.patient_id + WHERE fr.owner_id = p_owner_id + AND fr.deleted_at IS NULL + AND COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE) + BETWEEN p_start_date AND p_end_date + ) + + SELECT + gkey AS group_key, + glabel AS group_label, + + COALESCE(SUM(final_amount) FILTER (WHERE type = 'receita' AND status = 'paid'), 0) + AS total_receitas, + + COALESCE(SUM(final_amount) FILTER (WHERE type = 'despesa' AND status = 'paid'), 0) + AS total_despesas, + + COALESCE(SUM(final_amount) FILTER (WHERE type = 'receita' AND status = 'paid'), 0) + - COALESCE(SUM(final_amount) FILTER (WHERE type = 'despesa' AND status = 'paid'), 0) + AS saldo, + + COALESCE(SUM(final_amount) FILTER (WHERE status = 'pending'), 0) AS total_pendente, + + COALESCE(SUM(final_amount) FILTER (WHERE status = 'overdue'), 0) AS total_overdue, + + COUNT(*) AS count_records + + FROM base + WHERE gkey IS NOT NULL -- descarta p_group_by inv??lido + GROUP BY gkey, glabel + ORDER BY gkey ASC; + +$$; + + +-- +-- Name: FUNCTION get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text) IS 'Relat??rio financeiro agrupado por m??s, semana ISO, categoria ou paciente. p_group_by aceita: ''month'' | ''week'' | ''category'' | ''patient''. Totais de receita/despesa consideram apenas registros com status=paid. total_pendente e total_overdue incluem todos os tipos (receita + despesa).'; + + +-- +-- Name: get_financial_summary(uuid, integer, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_month integer) RETURNS TABLE(total_receitas numeric, total_despesas numeric, total_pendente numeric, saldo_liquido numeric, total_repasse numeric, count_receitas bigint, count_despesas bigint) + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + AS $$ + SELECT + -- Receitas pagas no per??odo + COALESCE(SUM(amount) FILTER ( + WHERE type = 'receita' AND status = 'paid' + ), 0) AS total_receitas, + + -- Despesas pagas no per??odo + COALESCE(SUM(amount) FILTER ( + WHERE type = 'despesa' AND status = 'paid' + ), 0) AS total_despesas, + + -- Tudo pendente ou vencido (receitas + despesas) + COALESCE(SUM(amount) FILTER ( + WHERE status IN ('pending', 'overdue') + ), 0) AS total_pendente, + + -- Saldo l??quido (receitas pagas ??? despesas pagas) + COALESCE(SUM(amount) FILTER ( + WHERE type = 'receita' AND status = 'paid' + ), 0) + - COALESCE(SUM(amount) FILTER ( + WHERE type = 'despesa' AND status = 'paid' + ), 0) AS saldo_liquido, + + -- Total repassado ?? cl??nica (apenas receitas pagas) + COALESCE(SUM(clinic_fee_amount) FILTER ( + WHERE type = 'receita' AND status = 'paid' + ), 0) AS total_repasse, + + -- Contadores (excluindo soft-deleted) + COUNT(*) FILTER (WHERE type = 'receita' AND deleted_at IS NULL) AS count_receitas, + COUNT(*) FILTER (WHERE type = 'despesa' AND deleted_at IS NULL) AS count_despesas + + FROM public.financial_records + WHERE owner_id = p_owner_id + AND deleted_at IS NULL + AND EXTRACT(YEAR FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_year + AND EXTRACT(MONTH FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_month; +$$; + + +-- +-- Name: get_my_email(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.get_my_email() RETURNS text + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ + select lower(email) + from auth.users + where id = auth.uid(); +$$; + + +-- +-- Name: guard_account_type_immutable(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_account_type_immutable() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF OLD.account_type <> 'free' AND NEW.account_type IS DISTINCT FROM OLD.account_type THEN + RAISE EXCEPTION 'account_type ?? imut??vel ap??s escolha (atual: "%" para tentativa: "%"). Para mudar de perfil, crie uma nova conta.', OLD.account_type, NEW.account_type + USING ERRCODE = 'P0001'; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: guard_locked_commitment(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_locked_commitment() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if (old.is_locked = true) then + if (tg_op = 'DELETE') then + raise exception 'Compromisso bloqueado n??o pode ser exclu??do.'; + end if; + + if (tg_op = 'UPDATE') then + if (new.active = false) then + raise exception 'Compromisso bloqueado n??o pode ser desativado.'; + end if; + + -- trava renomear (mant??m o "Sess??o" sempre igual) + if (new.name is distinct from old.name) then + raise exception 'Compromisso bloqueado n??o pode ser renomeado.'; + end if; + + -- se quiser travar descri????o tamb??m, descomente: + -- if (new.description is distinct from old.description) then + -- raise exception 'Compromisso bloqueado n??o pode alterar descri????o.'; + -- end if; + end if; + end if; + + return new; +end; +$$; + + +-- +-- Name: guard_no_change_core_plan_key(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_no_change_core_plan_key() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') + and new.key is distinct from old.key then + raise exception 'N??o ?? permitido alterar a key do plano padr??o (%).', old.key + using errcode = 'P0001'; + end if; + + return new; +end $$; + + +-- +-- Name: guard_no_change_plan_target(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_no_change_plan_target() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_bypass text; +begin + -- bypass controlado por sess??o/transa????o: + -- s?? passa se app.plan_migration_bypass = '1' + v_bypass := current_setting('app.plan_migration_bypass', true); + + if v_bypass = '1' then + return new; + end if; + + -- comportamento original (bloqueia qualquer mudan??a) + if new.target is distinct from old.target then + raise exception 'N??o ?? permitido alterar target do plano (%) de % para %.', + old.key, old.target, new.target + using errcode = 'P0001'; + end if; + + return new; +end +$$; + + +-- +-- Name: guard_no_delete_core_plans(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_no_delete_core_plans() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') then + raise exception 'Plano padr??o (%) n??o pode ser removido.', old.key + using errcode = 'P0001'; + end if; + + return old; +end $$; + + +-- +-- Name: guard_patient_cannot_own_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_patient_cannot_own_tenant() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_account_type text; +BEGIN + SELECT account_type INTO v_account_type + FROM public.profiles + WHERE id = NEW.user_id; + + IF v_account_type = 'patient' AND NEW.role IN ('tenant_admin', 'therapist') THEN + RAISE EXCEPTION 'Usu??rio com perfil "patient" n??o pode ser propriet??rio ou terapeuta de um tenant. Se tornou profissional? Crie uma nova conta.' + USING ERRCODE = 'P0001'; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: guard_tenant_kind_immutable(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_tenant_kind_immutable() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF NEW.kind IS DISTINCT FROM OLD.kind THEN + RAISE EXCEPTION 'tenants.kind ?? imut??vel ap??s cria????o. Tentativa de alterar "%" para "%".', OLD.kind, NEW.kind + USING ERRCODE = 'P0001'; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: handle_new_user(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.handle_new_user() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + INSERT INTO public.profiles (id, role, account_type) + VALUES (NEW.id, 'portal_user', 'free') + ON CONFLICT (id) DO NOTHING; + RETURN NEW; +END; +$$; + + +-- +-- Name: handle_new_user_create_personal_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.handle_new_user_create_personal_tenant() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + -- Desabilitado. Tenant criado no onboarding via provision_account_tenant(). + RETURN NEW; +END; +$$; + + +-- +-- Name: has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.owner_feature_entitlements e + where e.owner_id = p_owner_id + and e.feature_key = p_feature_key + ); +$$; + + +-- +-- Name: is_clinic_tenant(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_clinic_tenant(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + SELECT EXISTS ( + SELECT 1 FROM public.tenants t + WHERE t.id = _tenant_id + AND t.kind IN ('clinic', 'clinic_coworking', 'clinic_reception', 'clinic_full') + ); +$$; + + +-- +-- Name: is_saas_admin(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_saas_admin() RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 from public.saas_admins sa + where sa.user_id = auth.uid() + ); +$$; + + +-- +-- Name: is_tenant_admin(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_tenant_admin(p_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ + select exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.role = 'tenant_admin' + and tm.status = 'active' + ); +$$; + + +-- +-- Name: is_tenant_member(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_tenant_member(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.tenant_members m + where m.tenant_id = _tenant_id + and m.user_id = auth.uid() + and m.status = 'active' + ); +$$; + + +-- +-- Name: is_therapist_tenant(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_therapist_tenant(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + SELECT EXISTS ( + SELECT 1 FROM public.tenants t + WHERE t.id = _tenant_id AND t.kind = 'therapist' + ); +$$; + + +-- +-- Name: jwt_email(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.jwt_email() RETURNS text + LANGUAGE sql STABLE + AS $$ + select nullif(lower(current_setting('request.jwt.claim.email', true)), ''); +$$; + + +-- +-- Name: list_financial_records(uuid, integer, integer, text, text, uuid, integer, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.list_financial_records(p_owner_id uuid, p_year integer DEFAULT NULL::integer, p_month integer DEFAULT NULL::integer, p_type text DEFAULT NULL::text, p_status text DEFAULT NULL::text, p_patient_id uuid DEFAULT NULL::uuid, p_limit integer DEFAULT 50, p_offset integer DEFAULT 0) RETURNS SETOF public.financial_records + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + AS $$ + SELECT * + FROM public.financial_records + WHERE owner_id = p_owner_id + AND deleted_at IS NULL + AND (p_type IS NULL OR type::TEXT = p_type) + AND (p_status IS NULL OR status = p_status) + AND (p_patient_id IS NULL OR patient_id = p_patient_id) + AND (p_year IS NULL OR EXTRACT(YEAR FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_year) + AND (p_month IS NULL OR EXTRACT(MONTH FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_month) + ORDER BY COALESCE(paid_at, due_date::TIMESTAMPTZ, created_at) DESC + LIMIT p_limit + OFFSET p_offset; +$$; + + +-- +-- Name: mark_as_paid(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method text) RETURNS SETOF public.financial_records + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_record public.financial_records%ROWTYPE; +BEGIN + -- Garante que o registro pertence ao usu??rio autenticado (RLS n??o aplica em SECURITY DEFINER) + SELECT * INTO v_record + FROM public.financial_records + WHERE id = p_financial_record_id + AND owner_id = auth.uid() + AND deleted_at IS NULL; + + IF NOT FOUND THEN + RAISE EXCEPTION 'Registro financeiro n??o encontrado ou sem permiss??o.'; + END IF; + + IF v_record.status NOT IN ('pending', 'overdue') THEN + RAISE EXCEPTION 'Apenas cobran??as pendentes ou vencidas podem ser marcadas como pagas.'; + END IF; + + UPDATE public.financial_records + SET status = 'paid', + paid_at = NOW(), + payment_method = p_payment_method, + updated_at = NOW() + WHERE id = p_financial_record_id + RETURNING * INTO v_record; + + RETURN NEXT v_record; +END; +$$; + + +-- +-- Name: mark_payout_as_paid(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.mark_payout_as_paid(p_payout_id uuid) RETURNS public.therapist_payouts + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_payout public.therapist_payouts%ROWTYPE; +BEGIN + -- Busca o payout + SELECT * INTO v_payout + FROM public.therapist_payouts + WHERE id = p_payout_id; + + IF NOT FOUND THEN + RAISE EXCEPTION 'Repasse n??o encontrado: %', p_payout_id; + END IF; + + -- Verifica permiss??o: apenas tenant_admin do tenant do repasse + IF NOT public.is_tenant_admin(v_payout.tenant_id) THEN + RAISE EXCEPTION 'Apenas o administrador da cl??nica pode marcar repasses como pagos.'; + END IF; + + -- Verifica status + IF v_payout.status <> 'pending' THEN + RAISE EXCEPTION + 'Repasse j?? est?? com status ''%''. Apenas repasses pendentes podem ser pagos.', + v_payout.status; + END IF; + + -- Atualiza + UPDATE public.therapist_payouts + SET + status = 'paid', + paid_at = NOW(), + updated_at = NOW() + WHERE id = p_payout_id + RETURNING * INTO v_payout; + + RETURN v_payout; +END; +$$; + + +-- +-- Name: FUNCTION mark_payout_as_paid(p_payout_id uuid); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.mark_payout_as_paid(p_payout_id uuid) IS 'Marca um repasse de terapeuta como pago. Apenas o tenant_admin pode chamar. Apenas repasses com status=pending podem ser finalizados.'; + + +-- +-- Name: my_tenants(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.my_tenants() RETURNS TABLE(tenant_id uuid, role text, status text, kind text) + LANGUAGE sql STABLE + AS $$ + select + tm.tenant_id, + tm.role, + tm.status, + t.kind + from public.tenant_members tm + join public.tenants t on t.id = tm.tenant_id + where tm.user_id = auth.uid(); +$$; + + +-- +-- Name: notice_track_click(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notice_track_click(p_notice_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +begin + update public.global_notices + set clicks_count = clicks_count + 1 + where id = p_notice_id; +end; +$$; + + +-- +-- Name: notice_track_view(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notice_track_view(p_notice_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +begin + update public.global_notices + set views_count = views_count + 1 + where id = p_notice_id; +end; +$$; + + +-- +-- Name: notify_on_intake(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notify_on_intake() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF NEW.status = 'new' THEN + INSERT INTO public.notifications ( + owner_id, + tenant_id, + type, + ref_id, + ref_table, + payload + ) + VALUES ( + NEW.owner_id, + NEW.tenant_id, + 'new_patient', + NEW.id, + 'patient_intake_requests', + jsonb_build_object( + 'title', 'Novo cadastro externo', + 'detail', COALESCE(NEW.nome_completo, 'Paciente'), + 'deeplink', '/therapist/patients/cadastro/recebidos', + 'avatar_initials', upper(left(COALESCE(NEW.nome_completo, '?'), 2)) + ) + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: notify_on_scheduling(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notify_on_scheduling() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ BEGIN IF NEW.status = 'pendente' THEN + INSERT INTO public.notifications ( owner_id, tenant_id, type, ref_id, ref_table, payload ) VALUES ( + NEW.owner_id, NEW.tenant_id, + 'new_scheduling', NEW.id, 'agendador_solicitacoes', jsonb_build_object( 'title', 'Nova solicita????o de agendamento', 'detail', COALESCE(NEW.paciente_nome, 'Paciente') || ' ' || COALESCE(NEW.paciente_sobrenome, '') || ' ??? ' || COALESCE(NEW.tipo, ''), 'deeplink', '/therapist/agendamentos-recebidos', 'avatar_initials', upper(left(COALESCE(NEW.paciente_nome, '?'), 1) || left(COALESCE(NEW.paciente_sobrenome, ''), 1)) ) ); END IF; RETURN NEW; END; $$; + + +-- +-- Name: notify_on_session_status(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notify_on_session_status() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_nome text; +BEGIN + IF NEW.status IN ('faltou', 'cancelado') AND OLD.status IS DISTINCT FROM NEW.status THEN + + SELECT nome_completo + INTO v_nome + FROM public.patients + WHERE id = NEW.patient_id + LIMIT 1; + + INSERT INTO public.notifications ( + owner_id, + tenant_id, + type, + ref_id, + ref_table, + payload + ) + VALUES ( + NEW.owner_id, + NEW.tenant_id, + 'session_status', + NEW.id, + 'agenda_eventos', + jsonb_build_object( + 'title', CASE WHEN NEW.status = 'faltou' THEN 'Paciente faltou' ELSE 'Sess??o cancelada' END, + 'detail', COALESCE(v_nome, 'Paciente') || ' ??? ' || to_char(NEW.inicio_em, 'DD/MM HH24:MI'), + 'deeplink', '/therapist/agenda', + 'avatar_initials', upper(left(COALESCE(v_nome, '?'), 2)) + ) + ); + + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: on_new_user_seed_patient_groups(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.on_new_user_seed_patient_groups() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + BEGIN + PERFORM public.seed_default_patient_groups(NEW.id); + RETURN NEW; + END; + $$; + + +-- +-- Name: patients_validate_member_consistency(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.patients_validate_member_consistency() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_tenant_responsible uuid; + v_tenant_therapist uuid; +BEGIN + -- responsible_member sempre deve existir e ser do tenant + SELECT tenant_id INTO v_tenant_responsible + FROM public.tenant_members + WHERE id = NEW.responsible_member_id; + + IF v_tenant_responsible IS NULL THEN + RAISE EXCEPTION 'Responsible member not found'; + END IF; + + IF NEW.tenant_id IS NULL THEN + RAISE EXCEPTION 'tenant_id is required'; + END IF; + + IF v_tenant_responsible <> NEW.tenant_id THEN + RAISE EXCEPTION 'Responsible member must belong to the same tenant'; + END IF; + + -- therapist scope: therapist_member_id deve existir e ser do mesmo tenant + IF NEW.patient_scope = 'therapist' THEN + IF NEW.therapist_member_id IS NULL THEN + RAISE EXCEPTION 'therapist_member_id is required when patient_scope=therapist'; + END IF; + + SELECT tenant_id INTO v_tenant_therapist + FROM public.tenant_members + WHERE id = NEW.therapist_member_id; + + IF v_tenant_therapist IS NULL THEN + RAISE EXCEPTION 'Therapist member not found'; + END IF; + + IF v_tenant_therapist <> NEW.tenant_id THEN + RAISE EXCEPTION 'Therapist member must belong to the same tenant'; + END IF; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: patients_validate_responsible_member_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.patients_validate_responsible_member_tenant() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + m_tenant uuid; +begin + select tenant_id into m_tenant + from public.tenant_members + where id = new.responsible_member_id; + + if m_tenant is null then + raise exception 'Responsible member not found'; + end if; + + if new.tenant_id is null then + raise exception 'tenant_id is required'; + end if; + + if m_tenant <> new.tenant_id then + raise exception 'Responsible member must belong to the same tenant'; + end if; + + return new; +end; +$$; + + +-- +-- Name: populate_notification_queue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.populate_notification_queue() RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + INSERT INTO public.notification_queue ( + tenant_id, owner_id, agenda_evento_id, patient_id, + channel, template_key, schedule_key, + resolved_vars, recipient_address, + scheduled_at, idempotency_key + ) + SELECT + ae.tenant_id, + ae.owner_id, + ae.id AS agenda_evento_id, + ae.patient_id, + ch.channel, + 'session.' || REPLACE(ns.event_type, '_sessao', '') || '.' || ch.channel, + ns.schedule_key, + jsonb_build_object( + 'nome_paciente', COALESCE(p.nome_completo, 'Paciente'), + 'data_sessao', TO_CHAR(ae.inicio_em AT TIME ZONE 'America/Sao_Paulo', 'DD/MM/YYYY'), + 'hora_sessao', TO_CHAR(ae.inicio_em AT TIME ZONE 'America/Sao_Paulo', 'HH24:MI'), + 'nome_terapeuta', COALESCE(prof.full_name, 'Terapeuta'), + 'modalidade', COALESCE(ae.modalidade, 'Presencial'), + 'titulo', COALESCE(ae.titulo, 'Sess??o') + ), + CASE ch.channel + WHEN 'whatsapp' THEN COALESCE(p.telefone, '') + WHEN 'sms' THEN COALESCE(p.telefone, '') + WHEN 'email' THEN COALESCE(p.email_principal, '') + END, + CASE + WHEN (ae.inicio_em - (ns.offset_minutes || ' minutes')::interval)::time + < ns.allowed_time_start + THEN DATE_TRUNC('day', ae.inicio_em - (ns.offset_minutes || ' minutes')::interval) + + ns.allowed_time_start + WHEN (ae.inicio_em - (ns.offset_minutes || ' minutes')::interval)::time + > ns.allowed_time_end + THEN DATE_TRUNC('day', ae.inicio_em - (ns.offset_minutes || ' minutes')::interval) + + ns.allowed_time_start + ELSE ae.inicio_em - (ns.offset_minutes || ' minutes')::interval + END, + ae.id::text || ':' || ns.schedule_key || ':' || ch.channel || ':' + || ae.inicio_em::date::text + FROM public.agenda_eventos ae + JOIN public.patients p ON p.id = ae.patient_id + LEFT JOIN public.profiles prof ON prof.id = ae.owner_id + JOIN public.notification_schedules ns + ON ns.owner_id = ae.owner_id + AND ns.is_active = true + AND ns.deleted_at IS NULL + AND ns.trigger_type = 'before_event' + AND ns.event_type = 'lembrete_sessao' + JOIN public.notification_channels nc + ON nc.owner_id = ae.owner_id + AND nc.is_active = true + AND nc.deleted_at IS NULL + CROSS JOIN LATERAL ( + SELECT 'whatsapp' AS channel WHERE ns.whatsapp_enabled AND nc.channel = 'whatsapp' + UNION ALL + SELECT 'email' AS channel WHERE ns.email_enabled AND nc.channel = 'email' + UNION ALL + SELECT 'sms' AS channel WHERE ns.sms_enabled AND nc.channel = 'sms' + ) ch + LEFT JOIN public.notification_preferences np + ON np.patient_id = ae.patient_id + AND np.owner_id = ae.owner_id + AND np.deleted_at IS NULL + WHERE + ae.inicio_em > now() + AND ae.inicio_em <= now() + interval '48 hours' + AND ae.status NOT IN ('cancelado', 'faltou') + AND CASE ch.channel + WHEN 'whatsapp' THEN COALESCE(p.telefone, '') != '' + WHEN 'sms' THEN COALESCE(p.telefone, '') != '' + WHEN 'email' THEN COALESCE(p.email_principal, '') != '' + END + AND CASE ch.channel + WHEN 'whatsapp' THEN COALESCE(np.whatsapp_opt_in, true) + WHEN 'email' THEN COALESCE(np.email_opt_in, true) + WHEN 'sms' THEN COALESCE(np.sms_opt_in, false) + END + AND EXISTS ( + SELECT 1 FROM public.profiles tp + WHERE tp.id = ae.owner_id + AND COALESCE(tp.notify_reminders, true) = true + ) + ON CONFLICT (idempotency_key) DO NOTHING; +END; +$$; + + +-- +-- Name: prevent_promoting_to_system(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.prevent_promoting_to_system() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if new.is_system = true and old.is_system is distinct from true then + raise exception 'N??o ?? permitido transformar um grupo comum em grupo do sistema.'; + end if; + return new; +end; +$$; + + +-- +-- Name: prevent_saas_membership(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.prevent_saas_membership() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM public.profiles + WHERE id = NEW.user_id + AND role = 'saas_admin' + ) THEN + RAISE EXCEPTION 'SaaS admin cannot belong to tenant'; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: prevent_system_group_changes(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.prevent_system_group_changes() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + -- Se for grupo do sistema, regras r??gidas: + if old.is_system = true then + + -- nunca pode deletar + if tg_op = 'DELETE' then + raise exception 'Grupos padr??o do sistema n??o podem ser alterados ou exclu??dos.'; + end if; + + if tg_op = 'UPDATE' then + -- permite SOMENTE mudar tenant_id e/ou updated_at + -- qualquer mudan??a de conte??do permanece proibida + if + new.nome is distinct from old.nome or + new.descricao is distinct from old.descricao or + new.cor is distinct from old.cor or + new.is_active is distinct from old.is_active or + new.is_system is distinct from old.is_system or + new.owner_id is distinct from old.owner_id or + new.therapist_id is distinct from old.therapist_id or + new.created_at is distinct from old.created_at + then + raise exception 'Grupos padr??o do sistema n??o podem ser alterados ou exclu??dos.'; + end if; + + -- chegou aqui: s?? tenant_id/updated_at mudaram -> ok + return new; + end if; + + end if; + + -- n??o-system: deixa passar + if tg_op = 'DELETE' then + return old; + end if; + + return new; +end; +$$; + + +-- +-- Name: provision_account_tenant(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text DEFAULT NULL::text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_tenant_id uuid; + v_account_type text; + v_name text; +BEGIN + IF p_kind NOT IN ('therapist', 'clinic_coworking', 'clinic_reception', 'clinic_full') THEN + RAISE EXCEPTION 'kind inv??lido: "%". Use: therapist, clinic_coworking, clinic_reception, clinic_full.', p_kind + USING ERRCODE = 'P0001'; + END IF; + + v_account_type := CASE WHEN p_kind = 'therapist' THEN 'therapist' ELSE 'clinic' END; + + IF EXISTS ( + SELECT 1 + FROM public.tenant_members tm + JOIN public.tenants t ON t.id = tm.tenant_id + WHERE tm.user_id = p_user_id + AND tm.role = 'tenant_admin' + AND tm.status = 'active' + AND t.kind = p_kind + ) THEN + RAISE EXCEPTION 'Usu??rio j?? possui um tenant do tipo "%".', p_kind + USING ERRCODE = 'P0001'; + END IF; + + v_name := COALESCE( + NULLIF(TRIM(p_name), ''), + ( + SELECT COALESCE(NULLIF(TRIM(pr.full_name), ''), SPLIT_PART(au.email, '@', 1)) + FROM public.profiles pr + JOIN auth.users au ON au.id = pr.id + WHERE pr.id = p_user_id + ), + 'Conta' + ); + + INSERT INTO public.tenants (name, kind, created_at) + VALUES (v_name, p_kind, now()) + RETURNING id INTO v_tenant_id; + + INSERT INTO public.tenant_members (tenant_id, user_id, role, status, created_at) + VALUES (v_tenant_id, p_user_id, 'tenant_admin', 'active', now()); + + UPDATE public.profiles + SET account_type = v_account_type + WHERE id = p_user_id; + + PERFORM public.seed_determined_commitments(v_tenant_id); + + RETURN v_tenant_id; +END; +$$; + + +-- +-- Name: FUNCTION provision_account_tenant(p_user_id uuid, p_kind text, p_name text); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) IS 'Cria o tenant do tipo correto e atualiza account_type no profile. Chamar no onboarding ap??s escolha/pagamento de plano therapist ou clinic. p_kind: therapist | clinic_coworking | clinic_reception | clinic_full'; + + +-- +-- Name: reactivate_subscription(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.reactivate_subscription(p_subscription_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_owner_type text; + v_owner_ref uuid; +begin + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + if v_sub.status = 'active' then + return v_sub; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set status = 'active', + cancel_at_period_end = false, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'reactivated', + v_sub.plan_id, + v_sub.plan_id, + auth.uid(), + 'Reativa????o manual via admin', + 'admin_panel', + jsonb_build_object('previous_status', 'canceled') + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation(owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; + +end; +$$; + + +-- +-- Name: rebuild_owner_entitlements(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_plan_id uuid; +begin + -- Plano ativo do owner (owner = subscriptions.user_id) + select s.plan_id + into v_plan_id + from public.subscriptions s + where s.user_id = p_owner_id + and s.status = 'active' + order by s.created_at desc + limit 1; + + -- Sempre zera entitlements do owner (rebuild) + delete from public.owner_feature_entitlements e + where e.owner_id = p_owner_id; + + -- Se n??o tem assinatura ativa, acabou + if v_plan_id is null then + return; + end if; + + -- Recria entitlements esperados pelo plano + insert into public.owner_feature_entitlements (owner_id, feature_key, sources, limits_list) + select + p_owner_id as owner_id, + f.key as feature_key, + array['plan'::text] as sources, + '{}'::jsonb as limits_list + from public.plan_features pf + join public.features f on f.id = pf.feature_id + where pf.plan_id = v_plan_id; + +end; +$$; + + +-- +-- Name: revoke_support_session(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.revoke_support_session(p_token text) RETURNS boolean + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_admin_id uuid; + v_role text; +BEGIN + v_admin_id := auth.uid(); + IF v_admin_id IS NULL THEN + RAISE EXCEPTION 'N??o autenticado.' USING ERRCODE = 'P0001'; + END IF; + + SELECT role INTO v_role FROM public.profiles WHERE id = v_admin_id; + IF v_role <> 'saas_admin' THEN + RAISE EXCEPTION 'Acesso negado.' USING ERRCODE = 'P0002'; + END IF; + + DELETE FROM public.support_sessions + WHERE token = p_token + AND admin_id = v_admin_id; + + RETURN FOUND; +END; +$$; + + +-- +-- Name: rotate_patient_invite_token(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.rotate_patient_invite_token(p_new_token text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_uid uuid; + v_id uuid; +begin + -- pega o usu??rio logado + v_uid := auth.uid(); + if v_uid is null then + raise exception 'Usu??rio n??o autenticado'; + end if; + + -- desativa tokens antigos ativos do usu??rio + update public.patient_invites + set active = false + where owner_id = v_uid + and active = true; + + -- cria novo token + insert into public.patient_invites (owner_id, token, active) + values (v_uid, p_new_token, true) + returning id into v_id; + + return v_id; +end; +$$; + + +-- +-- Name: saas_votar_doc(uuid, boolean); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_uid uuid := auth.uid(); + v_voto_antigo boolean; +begin + if v_uid is null then + raise exception 'N??o autenticado'; + end if; + + -- Verifica se j?? votou + select util into v_voto_antigo + from public.saas_doc_votos + where doc_id = p_doc_id and user_id = v_uid; + + if found then + -- J?? votou igual ??? cancela o voto (toggle) + if v_voto_antigo = p_util then + delete from public.saas_doc_votos + where doc_id = p_doc_id and user_id = v_uid; + + update public.saas_docs set + votos_util = greatest(0, votos_util - (case when p_util then 1 else 0 end)), + votos_nao_util = greatest(0, votos_nao_util - (case when not p_util then 1 else 0 end)), + updated_at = now() + where id = p_doc_id; + + return jsonb_build_object('acao', 'removido', 'util', null); + else + -- Mudou de voto + update public.saas_doc_votos set util = p_util, updated_at = now() + where doc_id = p_doc_id and user_id = v_uid; + + update public.saas_docs set + votos_util = greatest(0, votos_util + (case when p_util then 1 else -1 end)), + votos_nao_util = greatest(0, votos_nao_util + (case when not p_util then 1 else -1 end)), + updated_at = now() + where id = p_doc_id; + + return jsonb_build_object('acao', 'atualizado', 'util', p_util); + end if; + else + -- Primeiro voto + insert into public.saas_doc_votos (doc_id, user_id, util) + values (p_doc_id, v_uid, p_util); + + update public.saas_docs set + votos_util = votos_util + (case when p_util then 1 else 0 end), + votos_nao_util = votos_nao_util + (case when not p_util then 1 else 0 end), + updated_at = now() + where id = p_doc_id; + + return jsonb_build_object('acao', 'registrado', 'util', p_util); + end if; +end; +$$; + + +-- +-- Name: safe_delete_patient(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.safe_delete_patient(p_patient_id uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + -- Bloqueia se houver hist??rico + IF NOT public.can_delete_patient(p_patient_id) THEN + RETURN jsonb_build_object( + 'ok', false, + 'error', 'has_history', + 'message', 'Este paciente possui hist??rico cl??nico ou financeiro e n??o pode ser removido. Voc?? pode desativar ou arquivar o paciente.' + ); + END IF; + + -- Verifica ownership via RLS (owner_id ou responsible_member_id) + IF NOT EXISTS ( + SELECT 1 FROM public.patients + WHERE id = p_patient_id + AND ( + owner_id = auth.uid() + OR responsible_member_id IN ( + SELECT id FROM public.tenant_members WHERE user_id = auth.uid() + ) + ) + ) THEN + RETURN jsonb_build_object( + 'ok', false, + 'error', 'forbidden', + 'message', 'Sem permiss??o para excluir este paciente.' + ); + END IF; + + DELETE FROM public.patients WHERE id = p_patient_id; + + RETURN jsonb_build_object('ok', true); +END; +$$; + + +-- +-- Name: sanitize_phone_br(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.sanitize_phone_br(raw_phone text) RETURNS text + LANGUAGE plpgsql IMMUTABLE + AS $$ DECLARE digits text; + BEGIN + digits := regexp_replace(COALESCE(raw_phone, ''), '[^0-9]', '', 'g'); + IF digits = '' THEN RETURN ''; END IF; + IF length(digits) = 10 OR length(digits) = 11 THEN + digits := '55' || digits; + END IF; + RETURN digits; + END; $$; + + +-- +-- Name: seed_default_financial_categories(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.seed_default_financial_categories(p_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + INSERT INTO public.financial_categories (user_id, name, type, color, icon, sort_order) + VALUES + (p_user_id, 'Sess??o', 'receita', '#22c55e', 'pi pi-heart', 1), + (p_user_id, 'Supervis??o', 'receita', '#6366f1', 'pi pi-users', 2), + (p_user_id, 'Conv??nio', 'receita', '#3b82f6', 'pi pi-building', 3), + (p_user_id, 'Grupo terap??utico', 'receita', '#f59e0b', 'pi pi-sitemap', 4), + (p_user_id, 'Outro (receita)', 'receita', '#8b5cf6', 'pi pi-plus-circle', 5), + (p_user_id, 'Aluguel sala', 'despesa', '#ef4444', 'pi pi-home', 1), + (p_user_id, 'Plataforma/SaaS', 'despesa', '#f97316', 'pi pi-desktop', 2), + (p_user_id, 'Repasse cl??nica', 'despesa', '#64748b', 'pi pi-arrow-right-arrow-left', 3), + (p_user_id, 'Supervis??o (custo)', 'despesa', '#6366f1', 'pi pi-users', 4), + (p_user_id, 'Outro (despesa)', 'despesa', '#94a3b8', 'pi pi-minus-circle', 5) + ON CONFLICT DO NOTHING; +END; +$$; + + +-- +-- Name: seed_default_patient_groups(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.seed_default_patient_groups(p_tenant_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_owner_id uuid; +BEGIN + -- busca o owner (tenant_admin) do tenant + SELECT user_id INTO v_owner_id + FROM public.tenant_members + WHERE tenant_id = p_tenant_id + AND role = 'tenant_admin' + AND status = 'active' + LIMIT 1; + + IF v_owner_id IS NULL THEN + RETURN; + END IF; + + INSERT INTO public.patient_groups (owner_id, nome, cor, is_system, tenant_id) + VALUES + (v_owner_id, 'Crian??as', '#60a5fa', true, p_tenant_id), + (v_owner_id, 'Adolescentes', '#a78bfa', true, p_tenant_id), + (v_owner_id, 'Idosos', '#34d399', true, p_tenant_id) + ON CONFLICT (owner_id, nome) DO NOTHING; +END; +$$; + + +-- +-- Name: seed_determined_commitments(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.seed_determined_commitments(p_tenant_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_id uuid; +begin + -- Sess??o (locked + sempre ativa) + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'session' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'session', true, true, 'Sess??o', 'Sess??o com paciente'); + end if; + + -- Leitura + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'reading' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'reading', false, true, 'Leitura', 'Praticar leitura'); + end if; + + -- Supervis??o + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'supervision' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'supervision', false, true, 'Supervis??o', 'Supervis??o'); + end if; + + -- Aula ??? (corrigido) + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'class' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'class', false, false, 'Aula', 'Dar aula'); + end if; + + -- An??lise pessoal + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'analysis' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'analysis', false, true, 'An??lise Pessoal', 'Minha an??lise pessoal'); + end if; + + -- ------------------------------------------------------- + -- Campos padr??o (idempotentes por (commitment_id, key)) + -- ------------------------------------------------------- + + -- Leitura + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'reading' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'book') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'book', 'Livro', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'author') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'author', 'Autor', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; + + -- Supervis??o + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'supervision' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'supervisor') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'supervisor', 'Supervisor', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'topic') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'topic', 'Assunto', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; + + -- Aula + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'class' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'theme') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'theme', 'Tema', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'group') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'group', 'Turma', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; + + -- An??lise + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'analysis' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'analyst') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'analyst', 'Analista', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'focus') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'focus', 'Foco', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; +end; +$$; + + +-- +-- Name: set_insurance_plans_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_insurance_plans_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN NEW.updated_at = now(); RETURN NEW; END; $$; + + +-- +-- Name: set_medicos_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_medicos_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: set_owner_id(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_owner_id() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if new.owner_id is null then + new.owner_id := auth.uid(); + end if; + return new; +end; +$$; + + +-- +-- Name: set_services_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_services_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: set_tenant_feature_exception(uuid, text, boolean, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text DEFAULT NULL::text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +begin + -- ??? S?? owner ou admin do tenant podem alterar features + if not exists ( + select 1 from public.tenant_members + where tenant_id = p_tenant_id + and user_id = auth.uid() + and role in ('owner', 'admin') + and status = 'active' + ) then + raise exception 'Acesso negado: apenas owner/admin pode alterar features do tenant.'; + end if; + + insert into public.tenant_features (tenant_id, feature_key, enabled) + values (p_tenant_id, p_feature_key, p_enabled) + on conflict (tenant_id, feature_key) + do update set enabled = excluded.enabled; + + insert into public.tenant_feature_exceptions_log ( + tenant_id, feature_key, enabled, reason, created_by + ) values ( + p_tenant_id, p_feature_key, p_enabled, p_reason, auth.uid() + ); +end; +$$; + + +-- +-- Name: set_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: set_updated_at_recurrence(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_updated_at_recurrence() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN NEW.updated_at = now(); RETURN NEW; END; +$$; + + +-- +-- Name: split_recurrence_at(uuid, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.split_recurrence_at(p_recurrence_id uuid, p_from_date date) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_old public.recurrence_rules; + v_new_id uuid; +BEGIN + -- busca a regra original + SELECT * INTO v_old + FROM public.recurrence_rules + WHERE id = p_recurrence_id; + + IF NOT FOUND THEN + RAISE EXCEPTION 'recurrence_rule % n??o encontrada', p_recurrence_id; + END IF; + + -- encerra a regra antiga na data anterior + UPDATE public.recurrence_rules + SET + end_date = p_from_date - INTERVAL '1 day', + open_ended = false, + updated_at = now() + WHERE id = p_recurrence_id; + + -- cria nova regra a partir de p_from_date + INSERT INTO public.recurrence_rules ( + tenant_id, owner_id, therapist_id, patient_id, + determined_commitment_id, type, interval, weekdays, + start_time, end_time, timezone, duration_min, + start_date, end_date, max_occurrences, open_ended, + modalidade, titulo_custom, observacoes, extra_fields, status + ) + SELECT + tenant_id, owner_id, therapist_id, patient_id, + determined_commitment_id, type, interval, weekdays, + start_time, end_time, timezone, duration_min, + p_from_date, v_old.end_date, v_old.max_occurrences, v_old.open_ended, + modalidade, titulo_custom, observacoes, extra_fields, status + FROM public.recurrence_rules + WHERE id = p_recurrence_id + RETURNING id INTO v_new_id; + + RETURN v_new_id; +END; +$$; + + +-- +-- Name: subscription_intents_view_insert(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.subscription_intents_view_insert() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_target text; + v_plan_id uuid; +begin + select p.id, p.target into v_plan_id, v_target + from public.plans p + where p.key = new.plan_key; + + if v_plan_id is null then + raise exception 'Plano inv??lido: plan_key=%', new.plan_key; + end if; + + if lower(v_target) = 'clinic' then + if new.tenant_id is null then + raise exception 'Inten????o clinic exige tenant_id.'; + end if; + + insert into public.subscription_intents_tenant ( + id, tenant_id, created_by_user_id, email, + plan_id, plan_key, interval, amount_cents, currency, + status, source, notes, created_at, paid_at + ) values ( + coalesce(new.id, gen_random_uuid()), + new.tenant_id, new.created_by_user_id, new.email, + v_plan_id, new.plan_key, coalesce(new.interval,'month'), + new.amount_cents, coalesce(new.currency,'BRL'), + coalesce(new.status,'pending'), coalesce(new.source,'manual'), + new.notes, coalesce(new.created_at, now()), new.paid_at + ); + + new.plan_target := 'clinic'; + return new; + end if; + + -- therapist ou supervisor ??? tabela personal + if lower(v_target) in ('therapist', 'supervisor') then + insert into public.subscription_intents_personal ( + id, user_id, created_by_user_id, email, + plan_id, plan_key, interval, amount_cents, currency, + status, source, notes, created_at, paid_at + ) values ( + coalesce(new.id, gen_random_uuid()), + new.user_id, new.created_by_user_id, new.email, + v_plan_id, new.plan_key, coalesce(new.interval,'month'), + new.amount_cents, coalesce(new.currency,'BRL'), + coalesce(new.status,'pending'), coalesce(new.source,'manual'), + new.notes, coalesce(new.created_at, now()), new.paid_at + ); + + new.plan_target := lower(v_target); -- 'therapist' ou 'supervisor' + return new; + end if; + + raise exception 'Target de plano n??o suportado: %', v_target; +end; +$$; + + +-- +-- Name: subscriptions_validate_scope(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.subscriptions_validate_scope() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_target text; +BEGIN + SELECT lower(p.target) INTO v_target + FROM public.plans p + WHERE p.id = NEW.plan_id; + + IF v_target IS NULL THEN + RAISE EXCEPTION 'Plano inv??lido (target nulo).'; + END IF; + + IF v_target = 'clinic' THEN + IF NEW.tenant_id IS NULL THEN + RAISE EXCEPTION 'Assinatura clinic exige tenant_id.'; + END IF; + IF NEW.user_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura clinic n??o pode ter user_id (XOR).'; + END IF; + + ELSIF v_target IN ('therapist', 'supervisor') THEN + -- supervisor ?? pessoal como therapist + IF NEW.tenant_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura % n??o deve ter tenant_id.', v_target; + END IF; + IF NEW.user_id IS NULL THEN + RAISE EXCEPTION 'Assinatura % exige user_id.', v_target; + END IF; + + ELSIF v_target = 'patient' THEN + IF NEW.tenant_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura patient n??o deve ter tenant_id.'; + END IF; + IF NEW.user_id IS NULL THEN + RAISE EXCEPTION 'Assinatura patient exige user_id.'; + END IF; + + ELSE + RAISE EXCEPTION 'Target de plano inv??lido: %', v_target; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: sync_busy_mirror_agenda_eventos(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.sync_busy_mirror_agenda_eventos() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + clinic_tenant uuid; + is_personal boolean; + should_mirror boolean; +begin + -- Anti-recurs??o: espelho n??o espelha + if (tg_op <> 'DELETE') then + if new.mirror_of_event_id is not null then + return new; + end if; + else + if old.mirror_of_event_id is not null then + return old; + end if; + end if; + + -- Define se ?? pessoal e se deve espelhar + if (tg_op = 'DELETE') then + is_personal := (old.tenant_id = old.owner_id); + should_mirror := (old.visibility_scope in ('busy_only','private')); + else + is_personal := (new.tenant_id = new.owner_id); + should_mirror := (new.visibility_scope in ('busy_only','private')); + end if; + + -- Se n??o ?? pessoal, n??o faz nada + if not is_personal then + if (tg_op = 'DELETE') then + return old; + end if; + return new; + end if; + + -- DELETE: remove espelhos existentes + if (tg_op = 'DELETE') then + delete from public.agenda_eventos e + where e.mirror_of_event_id = old.id + and e.mirror_source = 'personal_busy_mirror'; + + return old; + end if; + + -- INSERT/UPDATE: + -- Se n??o deve espelhar, remove espelhos e sai + if not should_mirror then + delete from public.agenda_eventos e + where e.mirror_of_event_id = new.id + and e.mirror_source = 'personal_busy_mirror'; + + return new; + end if; + + -- Para cada cl??nica onde o usu??rio ?? therapist active, cria/atualiza o "Ocupado" + for clinic_tenant in + select tm.tenant_id + from public.tenant_members tm + where tm.user_id = new.owner_id + and tm.role = 'therapist' + and tm.status = 'active' + and tm.tenant_id <> new.owner_id + loop + insert into public.agenda_eventos ( + tenant_id, + owner_id, + terapeuta_id, + paciente_id, + tipo, + status, + titulo, + observacoes, + inicio_em, + fim_em, + mirror_of_event_id, + mirror_source, + visibility_scope, + created_at, + updated_at + ) values ( + clinic_tenant, + new.owner_id, + new.owner_id, + null, + 'bloqueio'::public.tipo_evento_agenda, + 'agendado'::public.status_evento_agenda, + 'Ocupado', + null, + new.inicio_em, + new.fim_em, + new.id, + 'personal_busy_mirror', + 'public', + now(), + now() + ) + on conflict (tenant_id, mirror_of_event_id) where mirror_of_event_id is not null + do update set + owner_id = excluded.owner_id, + terapeuta_id = excluded.terapeuta_id, + tipo = excluded.tipo, + status = excluded.status, + titulo = excluded.titulo, + observacoes = excluded.observacoes, + inicio_em = excluded.inicio_em, + fim_em = excluded.fim_em, + updated_at = now(); + end loop; + + -- Limpa espelhos de cl??nicas onde o v??nculo therapist active n??o existe mais + delete from public.agenda_eventos e + where e.mirror_of_event_id = new.id + and e.mirror_source = 'personal_busy_mirror' + and not exists ( + select 1 + from public.tenant_members tm + where tm.user_id = new.owner_id + and tm.role = 'therapist' + and tm.status = 'active' + and tm.tenant_id = e.tenant_id + ); + + return new; +end; +$$; + + +-- +-- Name: sync_overdue_financial_records(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.sync_overdue_financial_records() RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_count integer; +BEGIN + UPDATE public.financial_records + SET + status = 'overdue', + updated_at = NOW() + WHERE status = 'pending' + AND due_date IS NOT NULL + AND due_date < CURRENT_DATE + AND deleted_at IS NULL; + + GET DIAGNOSTICS v_count = ROW_COUNT; + RETURN v_count; +END; +$$; + + +-- +-- Name: FUNCTION sync_overdue_financial_records(); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.sync_overdue_financial_records() IS 'Marca como overdue todos os financial_records pendentes com due_date vencido. Pode ser chamada manualmente, via pg_cron ou via Supabase Edge Function agendada.'; + + +-- +-- Name: tenant_accept_invite(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_accept_invite(p_token uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_uid uuid; + v_email text; + v_invite public.tenant_invites%rowtype; +begin + -- 1) precisa estar autenticado + v_uid := auth.uid(); + if v_uid is null then + raise exception 'not_authenticated' using errcode = 'P0001'; + end if; + + -- 2) pega email real do usu??rio logado sem depender do JWT claim + select u.email + into v_email + from auth.users u + where u.id = v_uid; + + if v_email is null or length(trim(v_email)) = 0 then + raise exception 'missing_user_email' using errcode = 'P0001'; + end if; + + -- 3) carrega o invite e trava linha (evita 2 aceites concorrentes) + select * + into v_invite + from public.tenant_invites i + where i.token = p_token + for update; + + if not found then + raise exception 'invite_not_found' using errcode = 'P0001'; + end if; + + -- 4) valida????es de estado + if v_invite.revoked_at is not null then + raise exception 'invite_revoked' using errcode = 'P0001'; + end if; + + if v_invite.accepted_at is not null then + raise exception 'invite_already_accepted' using errcode = 'P0001'; + end if; + + if v_invite.expires_at is not null and v_invite.expires_at <= now() then + raise exception 'invite_expired' using errcode = 'P0001'; + end if; + + -- 5) valida email (case-insensitive) + if lower(trim(v_invite.email)) <> lower(trim(v_email)) then + raise exception 'email_mismatch' using errcode = 'P0001'; + end if; + + -- 6) consome o invite + update public.tenant_invites + set accepted_at = now(), + accepted_by = v_uid + where id = v_invite.id; + + -- 7) cria ou reativa o membership + insert into public.tenant_members (tenant_id, user_id, role, status, created_at) + values (v_invite.tenant_id, v_uid, v_invite.role, 'active', now()) + on conflict (tenant_id, user_id) + do update set + role = excluded.role, + status = 'active'; + + -- 8) retorno ??til pro front (voc?? j?? tenta ler tenant_id no AcceptInvitePage) + return jsonb_build_object( + 'ok', true, + 'tenant_id', v_invite.tenant_id, + 'role', v_invite.role + ); +end; +$$; + + +-- +-- Name: tenant_members; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_members ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + user_id uuid NOT NULL, + role text NOT NULL, + status text DEFAULT 'active'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_add_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text DEFAULT 'therapist'::text) RETURNS public.tenant_members + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_target_uid uuid; + v_member public.tenant_members%rowtype; + v_is_admin boolean; + v_email text; +begin + if p_tenant_id is null then + raise exception 'tenant_id ?? obrigat??rio'; + end if; + + v_email := lower(trim(coalesce(p_email, ''))); + if v_email = '' then + raise exception 'email ?? obrigat??rio'; + end if; + + -- valida role permitida + if p_role not in ('tenant_admin','therapist','secretary','patient') then + raise exception 'role inv??lida: %', p_role; + end if; + + -- apenas admin do tenant (role real no banco) + select exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.role = 'tenant_admin' + and coalesce(tm.status,'active') = 'active' + ) into v_is_admin; + + if not v_is_admin then + raise exception 'sem permiss??o: apenas admin da cl??nica pode adicionar membros'; + end if; + + -- acha usu??rio pelo e-mail no Supabase Auth + select u.id + into v_target_uid + from auth.users u + where lower(u.email) = v_email + limit 1; + + if v_target_uid is null then + raise exception 'nenhum usu??rio encontrado com este e-mail'; + end if; + + -- cria ou reativa membro + insert into public.tenant_members (tenant_id, user_id, role, status) + values (p_tenant_id, v_target_uid, p_role, 'active') + on conflict (tenant_id, user_id) + do update set + role = excluded.role, + status = 'active' + returning * into v_member; + + return v_member; +end; +$$; + + +-- +-- Name: tenant_feature_allowed(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.v_tenant_entitlements v + where v.tenant_id = p_tenant_id + and v.feature_key = p_feature_key + and coalesce(v.allowed, false) = true + ); +$$; + + +-- +-- Name: tenant_feature_enabled(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select coalesce( + (select tf.enabled + from public.tenant_features tf + where tf.tenant_id = p_tenant_id and tf.feature_key = p_feature_key), + false + ); +$$; + + +-- +-- Name: tenant_features_guard_with_plan(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_features_guard_with_plan() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_allowed boolean; +begin + -- s?? valida quando est?? habilitando + if new.enabled is distinct from true then + return new; + end if; + + -- permitido pelo plano do tenant? + select exists ( + select 1 + from public.v_tenant_entitlements_full v + where v.tenant_id = new.tenant_id + and v.feature_key = new.feature_key + and v.allowed = true + ) + into v_allowed; + + if not v_allowed then + raise exception 'Feature % n??o permitida pelo plano atual do tenant %.', + new.feature_key, new.tenant_id + using errcode = 'P0001'; + end if; + + return new; +end; +$$; + + +-- +-- Name: tenant_has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select + exists ( + select 1 + from public.v_tenant_entitlements e + where e.tenant_id = _tenant_id + and e.feature_key = _feature + and e.allowed = true + ) + or exists ( + select 1 + from public.tenant_features tf + where tf.tenant_id = _tenant_id + and tf.feature_key = _feature + and tf.enabled = true + ); +$$; + + +-- +-- Name: tenant_invite_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_email text; + v_my_email text; + v_token uuid; + v_updated int; +begin + -- valida????es b??sicas + if p_tenant_id is null then + raise exception 'tenant_id inv??lido' using errcode = 'P0001'; + end if; + + v_email := lower(trim(coalesce(p_email, ''))); + if v_email = '' then + raise exception 'Informe um email' using errcode = 'P0001'; + end if; + + -- role permitido (ajuste se quiser) + if p_role is null or p_role not in ('therapist', 'secretary') then + raise exception 'Role inv??lido (use therapist/secretary)' using errcode = 'P0001'; + end if; + + -- ??? bloqueio: auto-convite + v_my_email := public.get_my_email(); + if v_my_email is not null and v_email = v_my_email then + raise exception 'Voc?? n??o pode convidar o seu pr??prio email.' using errcode = 'P0001'; + end if; + + -- ??? bloqueio: j?? ?? membro ativo do tenant + if exists ( + select 1 + from tenant_members tm + join auth.users au on au.id = tm.user_id + where tm.tenant_id = p_tenant_id + and tm.status = 'active' + and lower(au.email) = v_email + ) then + raise exception 'Este email j?? est?? vinculado a esta cl??nica.' using errcode = 'P0001'; + end if; + + -- ??? permiss??o: s?? admin do tenant pode convidar + if not exists ( + select 1 + from tenant_members me + where me.tenant_id = p_tenant_id + and me.user_id = auth.uid() + and me.status = 'active' + and me.role in ('tenant_admin','clinic_admin') + ) then + raise exception 'Sem permiss??o para convidar membros.' using errcode = 'P0001'; + end if; + + -- Gera token (reenvio simples / regenera????o) + v_token := gen_random_uuid(); + + -- 1) tenta "regerar" um convite pendente existente (mesmo email) + update tenant_invites + set token = v_token, + role = p_role, + created_at = now(), + expires_at = now() + interval '7 days', + accepted_at = null, + revoked_at = null + where tenant_id = p_tenant_id + and lower(email) = v_email + and accepted_at is null + and revoked_at is null; + + get diagnostics v_updated = row_count; + + -- 2) se n??o atualizou nada, cria convite novo + if v_updated = 0 then + insert into tenant_invites (tenant_id, email, role, token, created_at, expires_at) + values (p_tenant_id, v_email, p_role, v_token, now(), now() + interval '7 days'); + end if; + + return v_token; +end; +$$; + + +-- +-- Name: tenant_reactivate_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + update public.tenant_members + set status = 'active' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_remove_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +declare + v_role text; +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + if p_member_user_id = auth.uid() then + raise exception 'cannot_remove_self'; + end if; + + -- pega role atual do membro (se n??o existir, erro) + select role into v_role + from public.tenant_members + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if v_role is null then + raise exception 'membership_not_found'; + end if; + + -- trava: se for therapist, n??o pode remover com eventos futuros + if v_role = 'therapist' then + if exists ( + select 1 + from public.agenda_eventos e + where e.owner_id = p_tenant_id + and e.terapeuta_id = p_member_user_id + and e.inicio_em >= now() + and e.status::text not in ('cancelado','cancelled','canceled') + limit 1 + ) then + raise exception 'cannot_remove_therapist_with_future_events'; + end if; + end if; + + update public.tenant_members + set status = 'inactive' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_remove_member_soft(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + if p_member_user_id = auth.uid() then + raise exception 'cannot_remove_self'; + end if; + + update public.tenant_members + set status = 'inactive' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_revoke_invite(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +declare + v_email text; +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + v_email := lower(trim(p_email)); + + update public.tenant_invites + set revoked_at = now(), + revoked_by = auth.uid() + where tenant_id = p_tenant_id + and lower(email) = v_email + and role = p_role + and accepted_at is null + and revoked_at is null; + + if not found then + raise exception 'invite_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_set_member_status(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + -- valida status (adapte aos seus valores reais) + if p_new_status not in ('active','inactive','suspended','invited') then + raise exception 'invalid_status: %', p_new_status; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + -- evita desativar a si mesmo (opcional) + if p_member_user_id = auth.uid() and p_new_status <> 'active' then + raise exception 'cannot_disable_self'; + end if; + + update public.tenant_members + set status = p_new_status + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_update_member_role(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + -- exige auth + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + -- valida role + if p_new_role not in ('tenant_admin','therapist','secretary','patient') then + raise exception 'invalid_role: %', p_new_role; + end if; + + -- somente tenant_admin ativo pode alterar role + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + -- evita o admin remover o pr??prio admin sem querer (opcional mas recomendado) + if p_member_user_id = auth.uid() and p_new_role <> 'tenant_admin' then + raise exception 'cannot_demote_self'; + end if; + + update public.tenant_members + set role = p_new_role + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: toggle_plan(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.toggle_plan(owner uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + current_key text; + new_key text; +begin + select p.key into current_key + from subscriptions s + join plans p on p.id = s.plan_id + where s.owner_id = owner + and s.status = 'active'; + + new_key := case + when current_key = 'pro' then 'free' + else 'pro' + end; + + update subscriptions s + set plan_id = p.id + from plans p + where p.key = new_key + and s.owner_id = owner + and s.status = 'active'; +end; +$$; + + +-- +-- Name: transition_subscription(uuid, text, text, jsonb); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text DEFAULT NULL::text, p_metadata jsonb DEFAULT NULL::jsonb) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_uid uuid; + v_is_allowed boolean := false; +begin + v_uid := auth.uid(); + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id; + + if not found then + raise exception 'Assinatura n??o encontrada'; + end if; + + -- ===================================================== + -- ???? BLOCO DE AUTORIZA????O + -- ===================================================== + + -- 1) SaaS admin pode tudo + if is_saas_admin() then + v_is_allowed := true; + end if; + + -- 2) Assinatura pessoal (therapist) + if not v_is_allowed + and v_sub.tenant_id is null + and v_sub.user_id = v_uid then + v_is_allowed := true; + end if; + + -- 3) Assinatura de clinic (tenant) + if not v_is_allowed + and v_sub.tenant_id is not null then + + if exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = v_sub.tenant_id + and tm.user_id = v_uid + and tm.status = 'active' + and tm.role = 'tenant_admin' + ) then + v_is_allowed := true; + end if; + + end if; + + if not v_is_allowed then + raise exception 'Sem permiss??o para transicionar esta assinatura'; + end if; + + -- ===================================================== + -- ???? TRANSI????O + -- ===================================================== + + update public.subscriptions + set status = p_to_status, + updated_at = now(), + cancelled_at = case when p_to_status = 'cancelled' then now() else cancelled_at end, + suspended_at = case when p_to_status = 'suspended' then now() else suspended_at end, + past_due_since = case when p_to_status = 'past_due' then now() else past_due_since end, + expired_at = case when p_to_status = 'expired' then now() else expired_at end, + activated_at = case when p_to_status = 'active' then now() else activated_at end + where id = p_subscription_id + returning * into v_sub; + + -- ===================================================== + -- ???? EVENT LOG + -- ===================================================== + + insert into public.subscription_events ( + subscription_id, + owner_id, + event_type, + created_at, + created_by, + source, + reason, + metadata, + owner_type, + owner_ref + ) + values ( + v_sub.id, + coalesce(v_sub.tenant_id, v_sub.user_id), + 'status_changed', + now(), + v_uid, + 'manual_transition', + p_reason, + p_metadata, + case when v_sub.tenant_id is not null then 'tenant' else 'personal' end, + coalesce(v_sub.tenant_id, v_sub.user_id) + ); + + return v_sub; +end; +$$; + + +-- +-- Name: trg_fn_financial_records_auto_overdue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.trg_fn_financial_records_auto_overdue() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + IF NEW.status = 'pending' + AND NEW.due_date IS NOT NULL + AND NEW.due_date < CURRENT_DATE + THEN + NEW.status := 'overdue'; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: trg_fn_patient_risco_timeline(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.trg_fn_patient_risco_timeline() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF OLD.risco_elevado IS DISTINCT FROM NEW.risco_elevado THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, + evento_tipo, titulo, descricao, icone_cor, + gerado_por, ocorrido_em + ) VALUES ( + NEW.id, NEW.tenant_id, + CASE WHEN NEW.risco_elevado THEN 'risco_sinalizado' ELSE 'risco_removido' END, + CASE WHEN NEW.risco_elevado THEN 'Risco elevado sinalizado' ELSE 'Sinalização de risco removida' END, + NEW.risco_nota, + CASE WHEN NEW.risco_elevado THEN 'red' ELSE 'green' END, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: trg_fn_patient_status_history(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.trg_fn_patient_status_history() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF (TG_OP = 'INSERT') OR (OLD.status IS DISTINCT FROM NEW.status) THEN + INSERT INTO public.patient_status_history ( + patient_id, tenant_id, + status_anterior, status_novo, + motivo, encaminhado_para, data_saida, + alterado_por, alterado_em + ) VALUES ( + NEW.id, NEW.tenant_id, + CASE WHEN TG_OP = 'INSERT' THEN NULL ELSE OLD.status END, + NEW.status, + NEW.motivo_saida, + NEW.encaminhado_para, + NEW.data_saida, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: trg_fn_patient_status_timeline(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.trg_fn_patient_status_timeline() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF (TG_OP = 'INSERT') OR (OLD.status IS DISTINCT FROM NEW.status) THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, + evento_tipo, titulo, descricao, icone_cor, + gerado_por, ocorrido_em + ) VALUES ( + NEW.id, NEW.tenant_id, + 'status_alterado', + 'Status alterado para ' || NEW.status, + CASE + WHEN TG_OP = 'INSERT' THEN 'Paciente cadastrado' + ELSE 'De ' || OLD.status || ' → ' || NEW.status || + CASE WHEN NEW.motivo_saida IS NOT NULL THEN ' · ' || NEW.motivo_saida ELSE '' END + END, + CASE NEW.status + WHEN 'Ativo' THEN 'green' + WHEN 'Alta' THEN 'blue' + WHEN 'Inativo' THEN 'gray' + WHEN 'Encaminhado' THEN 'amber' + WHEN 'Arquivado' THEN 'gray' + ELSE 'gray' + END, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: unstick_notification_queue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.unstick_notification_queue() RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_unstuck integer; +BEGIN + UPDATE public.notification_queue + SET status = 'pendente', + attempts = attempts + 1, + last_error = 'Timeout: preso em processando por >10min', + next_retry_at = now() + interval '2 minutes' + WHERE status = 'processando' + AND updated_at < now() - interval '10 minutes'; + + GET DIAGNOSTICS v_unstuck = ROW_COUNT; + RETURN v_unstuck; +END; +$$; + + +-- +-- Name: update_payment_settings_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.update_payment_settings_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: update_professional_pricing_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.update_professional_pricing_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: user_has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.user_has_feature(_user_id uuid, _feature text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.v_user_entitlements e + where e.user_id = _user_id + and e.feature_key = _feature + and e.allowed = true + ); +$$; + + +-- +-- Name: validate_support_session(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.validate_support_session(p_token text) RETURNS json + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_session support_sessions; +BEGIN + IF p_token IS NULL OR length(trim(p_token)) < 32 THEN + RETURN json_build_object('valid', false, 'tenant_id', null); + END IF; + + SELECT * INTO v_session + FROM public.support_sessions + WHERE token = p_token + AND expires_at > now() + LIMIT 1; + + IF NOT FOUND THEN + RETURN json_build_object('valid', false, 'tenant_id', null); + END IF; + + RETURN json_build_object( + 'valid', true, + 'tenant_id', v_session.tenant_id + ); +END; +$$; + + +-- +-- Name: whoami(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.whoami() RETURNS TABLE(uid uuid, role text) + LANGUAGE sql STABLE + AS $$ + select auth.uid() as uid, auth.role() as role; +$$; + + +-- +-- Name: apply_rls(jsonb, integer); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer DEFAULT (1024 * 1024)) RETURNS SETOF realtime.wal_rls + LANGUAGE plpgsql + AS $$ +declare +-- Regclass of the table e.g. public.notes +entity_ regclass = (quote_ident(wal ->> 'schema') || '.' || quote_ident(wal ->> 'table'))::regclass; + +-- I, U, D, T: insert, update ... +action realtime.action = ( + case wal ->> 'action' + when 'I' then 'INSERT' + when 'U' then 'UPDATE' + when 'D' then 'DELETE' + else 'ERROR' + end +); + +-- Is row level security enabled for the table +is_rls_enabled bool = relrowsecurity from pg_class where oid = entity_; + +subscriptions realtime.subscription[] = array_agg(subs) + from + realtime.subscription subs + where + subs.entity = entity_; + +-- Subscription vars +roles regrole[] = array_agg(distinct us.claims_role::text) + from + unnest(subscriptions) us; + +working_role regrole; +claimed_role regrole; +claims jsonb; + +subscription_id uuid; +subscription_has_access bool; +visible_to_subscription_ids uuid[] = '{}'; + +-- structured info for wal's columns +columns realtime.wal_column[]; +-- previous identity values for update/delete +old_columns realtime.wal_column[]; + +error_record_exceeds_max_size boolean = octet_length(wal::text) > max_record_bytes; + +-- Primary jsonb output for record +output jsonb; + +begin +perform set_config('role', null, true); + +columns = + array_agg( + ( + x->>'name', + x->>'type', + x->>'typeoid', + realtime.cast( + (x->'value') #>> '{}', + coalesce( + (x->>'typeoid')::regtype, -- null when wal2json version <= 2.4 + (x->>'type')::regtype + ) + ), + (pks ->> 'name') is not null, + true + )::realtime.wal_column + ) + from + jsonb_array_elements(wal -> 'columns') x + left join jsonb_array_elements(wal -> 'pk') pks + on (x ->> 'name') = (pks ->> 'name'); + +old_columns = + array_agg( + ( + x->>'name', + x->>'type', + x->>'typeoid', + realtime.cast( + (x->'value') #>> '{}', + coalesce( + (x->>'typeoid')::regtype, -- null when wal2json version <= 2.4 + (x->>'type')::regtype + ) + ), + (pks ->> 'name') is not null, + true + )::realtime.wal_column + ) + from + jsonb_array_elements(wal -> 'identity') x + left join jsonb_array_elements(wal -> 'pk') pks + on (x ->> 'name') = (pks ->> 'name'); + +for working_role in select * from unnest(roles) loop + + -- Update `is_selectable` for columns and old_columns + columns = + array_agg( + ( + c.name, + c.type_name, + c.type_oid, + c.value, + c.is_pkey, + pg_catalog.has_column_privilege(working_role, entity_, c.name, 'SELECT') + )::realtime.wal_column + ) + from + unnest(columns) c; + + old_columns = + array_agg( + ( + c.name, + c.type_name, + c.type_oid, + c.value, + c.is_pkey, + pg_catalog.has_column_privilege(working_role, entity_, c.name, 'SELECT') + )::realtime.wal_column + ) + from + unnest(old_columns) c; + + if action <> 'DELETE' and count(1) = 0 from unnest(columns) c where c.is_pkey then + return next ( + jsonb_build_object( + 'schema', wal ->> 'schema', + 'table', wal ->> 'table', + 'type', action + ), + is_rls_enabled, + -- subscriptions is already filtered by entity + (select array_agg(s.subscription_id) from unnest(subscriptions) as s where claims_role = working_role), + array['Error 400: Bad Request, no primary key'] + )::realtime.wal_rls; + + -- The claims role does not have SELECT permission to the primary key of entity + elsif action <> 'DELETE' and sum(c.is_selectable::int) <> count(1) from unnest(columns) c where c.is_pkey then + return next ( + jsonb_build_object( + 'schema', wal ->> 'schema', + 'table', wal ->> 'table', + 'type', action + ), + is_rls_enabled, + (select array_agg(s.subscription_id) from unnest(subscriptions) as s where claims_role = working_role), + array['Error 401: Unauthorized'] + )::realtime.wal_rls; + + else + output = jsonb_build_object( + 'schema', wal ->> 'schema', + 'table', wal ->> 'table', + 'type', action, + 'commit_timestamp', to_char( + ((wal ->> 'timestamp')::timestamptz at time zone 'utc'), + 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"' + ), + 'columns', ( + select + jsonb_agg( + jsonb_build_object( + 'name', pa.attname, + 'type', pt.typname + ) + order by pa.attnum asc + ) + from + pg_attribute pa + join pg_type pt + on pa.atttypid = pt.oid + where + attrelid = entity_ + and attnum > 0 + and pg_catalog.has_column_privilege(working_role, entity_, pa.attname, 'SELECT') + ) + ) + -- Add "record" key for insert and update + || case + when action in ('INSERT', 'UPDATE') then + jsonb_build_object( + 'record', + ( + select + jsonb_object_agg( + -- if unchanged toast, get column name and value from old record + coalesce((c).name, (oc).name), + case + when (c).name is null then (oc).value + else (c).value + end + ) + from + unnest(columns) c + full outer join unnest(old_columns) oc + on (c).name = (oc).name + where + coalesce((c).is_selectable, (oc).is_selectable) + and ( not error_record_exceeds_max_size or (octet_length((c).value::text) <= 64)) + ) + ) + else '{}'::jsonb + end + -- Add "old_record" key for update and delete + || case + when action = 'UPDATE' then + jsonb_build_object( + 'old_record', + ( + select jsonb_object_agg((c).name, (c).value) + from unnest(old_columns) c + where + (c).is_selectable + and ( not error_record_exceeds_max_size or (octet_length((c).value::text) <= 64)) + ) + ) + when action = 'DELETE' then + jsonb_build_object( + 'old_record', + ( + select jsonb_object_agg((c).name, (c).value) + from unnest(old_columns) c + where + (c).is_selectable + and ( not error_record_exceeds_max_size or (octet_length((c).value::text) <= 64)) + and ( not is_rls_enabled or (c).is_pkey ) -- if RLS enabled, we can't secure deletes so filter to pkey + ) + ) + else '{}'::jsonb + end; + + -- Create the prepared statement + if is_rls_enabled and action <> 'DELETE' then + if (select 1 from pg_prepared_statements where name = 'walrus_rls_stmt' limit 1) > 0 then + deallocate walrus_rls_stmt; + end if; + execute realtime.build_prepared_statement_sql('walrus_rls_stmt', entity_, columns); + end if; + + visible_to_subscription_ids = '{}'; + + for subscription_id, claims in ( + select + subs.subscription_id, + subs.claims + from + unnest(subscriptions) subs + where + subs.entity = entity_ + and subs.claims_role = working_role + and ( + realtime.is_visible_through_filters(columns, subs.filters) + or ( + action = 'DELETE' + and realtime.is_visible_through_filters(old_columns, subs.filters) + ) + ) + ) loop + + if not is_rls_enabled or action = 'DELETE' then + visible_to_subscription_ids = visible_to_subscription_ids || subscription_id; + else + -- Check if RLS allows the role to see the record + perform + -- Trim leading and trailing quotes from working_role because set_config + -- doesn't recognize the role as valid if they are included + set_config('role', trim(both '"' from working_role::text), true), + set_config('request.jwt.claims', claims::text, true); + + execute 'execute walrus_rls_stmt' into subscription_has_access; + + if subscription_has_access then + visible_to_subscription_ids = visible_to_subscription_ids || subscription_id; + end if; + end if; + end loop; + + perform set_config('role', null, true); + + return next ( + output, + is_rls_enabled, + visible_to_subscription_ids, + case + when error_record_exceeds_max_size then array['Error 413: Payload Too Large'] + else '{}' + end + )::realtime.wal_rls; + + end if; +end loop; + +perform set_config('role', null, true); +end; +$$; + + +-- +-- Name: broadcast_changes(text, text, text, text, text, record, record, text); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.broadcast_changes(topic_name text, event_name text, operation text, table_name text, table_schema text, new record, old record, level text DEFAULT 'ROW'::text) RETURNS void + LANGUAGE plpgsql + AS $$ +DECLARE + -- Declare a variable to hold the JSONB representation of the row + row_data jsonb := '{}'::jsonb; +BEGIN + IF level = 'STATEMENT' THEN + RAISE EXCEPTION 'function can only be triggered for each row, not for each statement'; + END IF; + -- Check the operation type and handle accordingly + IF operation = 'INSERT' OR operation = 'UPDATE' OR operation = 'DELETE' THEN + row_data := jsonb_build_object('old_record', OLD, 'record', NEW, 'operation', operation, 'table', table_name, 'schema', table_schema); + PERFORM realtime.send (row_data, event_name, topic_name); + ELSE + RAISE EXCEPTION 'Unexpected operation type: %', operation; + END IF; +EXCEPTION + WHEN OTHERS THEN + RAISE EXCEPTION 'Failed to process the row: %', SQLERRM; +END; + +$$; + + +-- +-- Name: build_prepared_statement_sql(text, regclass, realtime.wal_column[]); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) RETURNS text + LANGUAGE sql + AS $$ + /* + Builds a sql string that, if executed, creates a prepared statement to + tests retrive a row from *entity* by its primary key columns. + Example + select realtime.build_prepared_statement_sql('public.notes', '{"id"}'::text[], '{"bigint"}'::text[]) + */ + select + 'prepare ' || prepared_statement_name || ' as + select + exists( + select + 1 + from + ' || entity || ' + where + ' || string_agg(quote_ident(pkc.name) || '=' || quote_nullable(pkc.value #>> '{}') , ' and ') || ' + )' + from + unnest(columns) pkc + where + pkc.is_pkey + group by + entity + $$; + + +-- +-- Name: cast(text, regtype); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime."cast"(val text, type_ regtype) RETURNS jsonb + LANGUAGE plpgsql IMMUTABLE + AS $$ + declare + res jsonb; + begin + execute format('select to_jsonb(%L::'|| type_::text || ')', val) into res; + return res; + end + $$; + + +-- +-- Name: check_equality_op(realtime.equality_op, regtype, text, text); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) RETURNS boolean + LANGUAGE plpgsql IMMUTABLE + AS $$ + /* + Casts *val_1* and *val_2* as type *type_* and check the *op* condition for truthiness + */ + declare + op_symbol text = ( + case + when op = 'eq' then '=' + when op = 'neq' then '!=' + when op = 'lt' then '<' + when op = 'lte' then '<=' + when op = 'gt' then '>' + when op = 'gte' then '>=' + when op = 'in' then '= any' + else 'UNKNOWN OP' + end + ); + res boolean; + begin + execute format( + 'select %L::'|| type_::text || ' ' || op_symbol + || ' ( %L::' + || ( + case + when op = 'in' then type_::text || '[]' + else type_::text end + ) + || ')', val_1, val_2) into res; + return res; + end; + $$; + + +-- +-- Name: is_visible_through_filters(realtime.wal_column[], realtime.user_defined_filter[]); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) RETURNS boolean + LANGUAGE sql IMMUTABLE + AS $_$ + /* + Should the record be visible (true) or filtered out (false) after *filters* are applied + */ + select + -- Default to allowed when no filters present + $2 is null -- no filters. this should not happen because subscriptions has a default + or array_length($2, 1) is null -- array length of an empty array is null + or bool_and( + coalesce( + realtime.check_equality_op( + op:=f.op, + type_:=coalesce( + col.type_oid::regtype, -- null when wal2json version <= 2.4 + col.type_name::regtype + ), + -- cast jsonb to text + val_1:=col.value #>> '{}', + val_2:=f.value + ), + false -- if null, filter does not match + ) + ) + from + unnest(filters) f + join unnest(columns) col + on f.column_name = col.name; + $_$; + + +-- +-- Name: list_changes(name, name, integer, integer); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) RETURNS SETOF realtime.wal_rls + LANGUAGE sql + SET log_min_messages TO 'fatal' + AS $$ + with pub as ( + select + concat_ws( + ',', + case when bool_or(pubinsert) then 'insert' else null end, + case when bool_or(pubupdate) then 'update' else null end, + case when bool_or(pubdelete) then 'delete' else null end + ) as w2j_actions, + coalesce( + string_agg( + realtime.quote_wal2json(format('%I.%I', schemaname, tablename)::regclass), + ',' + ) filter (where ppt.tablename is not null and ppt.tablename not like '% %'), + '' + ) w2j_add_tables + from + pg_publication pp + left join pg_publication_tables ppt + on pp.pubname = ppt.pubname + where + pp.pubname = publication + group by + pp.pubname + limit 1 + ), + w2j as ( + select + x.*, pub.w2j_add_tables + from + pub, + pg_logical_slot_get_changes( + slot_name, null, max_changes, + 'include-pk', 'true', + 'include-transaction', 'false', + 'include-timestamp', 'true', + 'include-type-oids', 'true', + 'format-version', '2', + 'actions', pub.w2j_actions, + 'add-tables', pub.w2j_add_tables + ) x + ) + select + xyz.wal, + xyz.is_rls_enabled, + xyz.subscription_ids, + xyz.errors + from + w2j, + realtime.apply_rls( + wal := w2j.data::jsonb, + max_record_bytes := max_record_bytes + ) xyz(wal, is_rls_enabled, subscription_ids, errors) + where + w2j.w2j_add_tables <> '' + and xyz.subscription_ids[1] is not null + $$; + + +-- +-- Name: quote_wal2json(regclass); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.quote_wal2json(entity regclass) RETURNS text + LANGUAGE sql IMMUTABLE STRICT + AS $$ + select + ( + select string_agg('' || ch,'') + from unnest(string_to_array(nsp.nspname::text, null)) with ordinality x(ch, idx) + where + not (x.idx = 1 and x.ch = '"') + and not ( + x.idx = array_length(string_to_array(nsp.nspname::text, null), 1) + and x.ch = '"' + ) + ) + || '.' + || ( + select string_agg('' || ch,'') + from unnest(string_to_array(pc.relname::text, null)) with ordinality x(ch, idx) + where + not (x.idx = 1 and x.ch = '"') + and not ( + x.idx = array_length(string_to_array(nsp.nspname::text, null), 1) + and x.ch = '"' + ) + ) + from + pg_class pc + join pg_namespace nsp + on pc.relnamespace = nsp.oid + where + pc.oid = entity + $$; + + +-- +-- Name: send(jsonb, text, text, boolean); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.send(payload jsonb, event text, topic text, private boolean DEFAULT true) RETURNS void + LANGUAGE plpgsql + AS $$ +DECLARE + generated_id uuid; + final_payload jsonb; +BEGIN + BEGIN + -- Generate a new UUID for the id + generated_id := gen_random_uuid(); + + -- Check if payload has an 'id' key, if not, add the generated UUID + IF payload ? 'id' THEN + final_payload := payload; + ELSE + final_payload := jsonb_set(payload, '{id}', to_jsonb(generated_id)); + END IF; + + -- Set the topic configuration + EXECUTE format('SET LOCAL realtime.topic TO %L', topic); + + -- Attempt to insert the message + INSERT INTO realtime.messages (id, payload, event, topic, private, extension) + VALUES (generated_id, final_payload, event, topic, private, 'broadcast'); + EXCEPTION + WHEN OTHERS THEN + -- Capture and notify the error + RAISE WARNING 'ErrorSendingBroadcastMessage: %', SQLERRM; + END; +END; +$$; + + +-- +-- Name: subscription_check_filters(); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.subscription_check_filters() RETURNS trigger + LANGUAGE plpgsql + AS $$ + /* + Validates that the user defined filters for a subscription: + - refer to valid columns that the claimed role may access + - values are coercable to the correct column type + */ + declare + col_names text[] = coalesce( + array_agg(c.column_name order by c.ordinal_position), + '{}'::text[] + ) + from + information_schema.columns c + where + format('%I.%I', c.table_schema, c.table_name)::regclass = new.entity + and pg_catalog.has_column_privilege( + (new.claims ->> 'role'), + format('%I.%I', c.table_schema, c.table_name)::regclass, + c.column_name, + 'SELECT' + ); + filter realtime.user_defined_filter; + col_type regtype; + + in_val jsonb; + begin + for filter in select * from unnest(new.filters) loop + -- Filtered column is valid + if not filter.column_name = any(col_names) then + raise exception 'invalid column for filter %', filter.column_name; + end if; + + -- Type is sanitized and safe for string interpolation + col_type = ( + select atttypid::regtype + from pg_catalog.pg_attribute + where attrelid = new.entity + and attname = filter.column_name + ); + if col_type is null then + raise exception 'failed to lookup type for column %', filter.column_name; + end if; + + -- Set maximum number of entries for in filter + if filter.op = 'in'::realtime.equality_op then + in_val = realtime.cast(filter.value, (col_type::text || '[]')::regtype); + if coalesce(jsonb_array_length(in_val), 0) > 100 then + raise exception 'too many values for `in` filter. Maximum 100'; + end if; + else + -- raises an exception if value is not coercable to type + perform realtime.cast(filter.value, col_type); + end if; + + end loop; + + -- Apply consistent order to filters so the unique constraint on + -- (subscription_id, entity, filters) can't be tricked by a different filter order + new.filters = coalesce( + array_agg(f order by f.column_name, f.op, f.value), + '{}' + ) from unnest(new.filters) f; + + return new; + end; + $$; + + +-- +-- Name: to_regrole(text); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.to_regrole(role_name text) RETURNS regrole + LANGUAGE sql IMMUTABLE + AS $$ select role_name::regrole $$; + + +-- +-- Name: topic(); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.topic() RETURNS text + LANGUAGE sql STABLE + AS $$ +select nullif(current_setting('realtime.topic', true), '')::text; +$$; + + +-- +-- Name: can_insert_object(text, text, uuid, jsonb); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.can_insert_object(bucketid text, name text, owner uuid, metadata jsonb) RETURNS void + LANGUAGE plpgsql + AS $$ +BEGIN + INSERT INTO "storage"."objects" ("bucket_id", "name", "owner", "metadata") VALUES (bucketid, name, owner, metadata); + -- hack to rollback the successful insert + RAISE sqlstate 'PT200' using + message = 'ROLLBACK', + detail = 'rollback successful insert'; +END +$$; + + +-- +-- Name: enforce_bucket_name_length(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.enforce_bucket_name_length() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if length(new.name) > 100 then + raise exception 'bucket name "%" is too long (% characters). Max is 100.', new.name, length(new.name); + end if; + return new; +end; +$$; + + +-- +-- Name: extension(text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.extension(name text) RETURNS text + LANGUAGE plpgsql + AS $$ +DECLARE +_parts text[]; +_filename text; +BEGIN + select string_to_array(name, '/') into _parts; + select _parts[array_length(_parts,1)] into _filename; + -- @todo return the last part instead of 2 + return reverse(split_part(reverse(_filename), '.', 1)); +END +$$; + + +-- +-- Name: filename(text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.filename(name text) RETURNS text + LANGUAGE plpgsql + AS $$ +DECLARE +_parts text[]; +BEGIN + select string_to_array(name, '/') into _parts; + return _parts[array_length(_parts,1)]; +END +$$; + + +-- +-- Name: foldername(text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.foldername(name text) RETURNS text[] + LANGUAGE plpgsql + AS $$ +DECLARE +_parts text[]; +BEGIN + select string_to_array(name, '/') into _parts; + return _parts[1:array_length(_parts,1)-1]; +END +$$; + + +-- +-- Name: get_common_prefix(text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.get_common_prefix(p_key text, p_prefix text, p_delimiter text) RETURNS text + LANGUAGE sql IMMUTABLE + AS $$ +SELECT CASE + WHEN position(p_delimiter IN substring(p_key FROM length(p_prefix) + 1)) > 0 + THEN left(p_key, length(p_prefix) + position(p_delimiter IN substring(p_key FROM length(p_prefix) + 1))) + ELSE NULL +END; +$$; + + +-- +-- Name: get_size_by_bucket(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.get_size_by_bucket() RETURNS TABLE(size bigint, bucket_id text) + LANGUAGE plpgsql + AS $$ +BEGIN + return query + select sum((metadata->>'size')::int) as size, obj.bucket_id + from "storage".objects as obj + group by obj.bucket_id; +END +$$; + + +-- +-- Name: list_multipart_uploads_with_delimiter(text, text, text, integer, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.list_multipart_uploads_with_delimiter(bucket_id text, prefix_param text, delimiter_param text, max_keys integer DEFAULT 100, next_key_token text DEFAULT ''::text, next_upload_token text DEFAULT ''::text) RETURNS TABLE(key text, id text, created_at timestamp with time zone) + LANGUAGE plpgsql + AS $_$ +BEGIN + RETURN QUERY EXECUTE + 'SELECT DISTINCT ON(key COLLATE "C") * from ( + SELECT + CASE + WHEN position($2 IN substring(key from length($1) + 1)) > 0 THEN + substring(key from 1 for length($1) + position($2 IN substring(key from length($1) + 1))) + ELSE + key + END AS key, id, created_at + FROM + storage.s3_multipart_uploads + WHERE + bucket_id = $5 AND + key ILIKE $1 || ''%'' AND + CASE + WHEN $4 != '''' AND $6 = '''' THEN + CASE + WHEN position($2 IN substring(key from length($1) + 1)) > 0 THEN + substring(key from 1 for length($1) + position($2 IN substring(key from length($1) + 1))) COLLATE "C" > $4 + ELSE + key COLLATE "C" > $4 + END + ELSE + true + END AND + CASE + WHEN $6 != '''' THEN + id COLLATE "C" > $6 + ELSE + true + END + ORDER BY + key COLLATE "C" ASC, created_at ASC) as e order by key COLLATE "C" LIMIT $3' + USING prefix_param, delimiter_param, max_keys, next_key_token, bucket_id, next_upload_token; +END; +$_$; + + +-- +-- Name: list_objects_with_delimiter(text, text, text, integer, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.list_objects_with_delimiter(_bucket_id text, prefix_param text, delimiter_param text, max_keys integer DEFAULT 100, start_after text DEFAULT ''::text, next_token text DEFAULT ''::text, sort_order text DEFAULT 'asc'::text) RETURNS TABLE(name text, id uuid, metadata jsonb, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone) + LANGUAGE plpgsql STABLE + AS $_$ +DECLARE + v_peek_name TEXT; + v_current RECORD; + v_common_prefix TEXT; + + -- Configuration + v_is_asc BOOLEAN; + v_prefix TEXT; + v_start TEXT; + v_upper_bound TEXT; + v_file_batch_size INT; + + -- Seek state + v_next_seek TEXT; + v_count INT := 0; + + -- Dynamic SQL for batch query only + v_batch_query TEXT; + +BEGIN + -- ======================================================================== + -- INITIALIZATION + -- ======================================================================== + v_is_asc := lower(coalesce(sort_order, 'asc')) = 'asc'; + v_prefix := coalesce(prefix_param, ''); + v_start := CASE WHEN coalesce(next_token, '') <> '' THEN next_token ELSE coalesce(start_after, '') END; + v_file_batch_size := LEAST(GREATEST(max_keys * 2, 100), 1000); + + -- Calculate upper bound for prefix filtering (bytewise, using COLLATE "C") + IF v_prefix = '' THEN + v_upper_bound := NULL; + ELSIF right(v_prefix, 1) = delimiter_param THEN + v_upper_bound := left(v_prefix, -1) || chr(ascii(delimiter_param) + 1); + ELSE + v_upper_bound := left(v_prefix, -1) || chr(ascii(right(v_prefix, 1)) + 1); + END IF; + + -- Build batch query (dynamic SQL - called infrequently, amortized over many rows) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" >= $2 ' || + 'AND o.name COLLATE "C" < $3 ORDER BY o.name COLLATE "C" ASC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" >= $2 ' || + 'ORDER BY o.name COLLATE "C" ASC LIMIT $4'; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" < $2 ' || + 'AND o.name COLLATE "C" >= $3 ORDER BY o.name COLLATE "C" DESC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" < $2 ' || + 'ORDER BY o.name COLLATE "C" DESC LIMIT $4'; + END IF; + END IF; + + -- ======================================================================== + -- SEEK INITIALIZATION: Determine starting position + -- ======================================================================== + IF v_start = '' THEN + IF v_is_asc THEN + v_next_seek := v_prefix; + ELSE + -- DESC without cursor: find the last item in range + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_next_seek FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_prefix AND o.name COLLATE "C" < v_upper_bound + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix <> '' THEN + SELECT o.name INTO v_next_seek FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_prefix + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_next_seek FROM storage.objects o + WHERE o.bucket_id = _bucket_id + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + END IF; + + IF v_next_seek IS NOT NULL THEN + v_next_seek := v_next_seek || delimiter_param; + ELSE + RETURN; + END IF; + END IF; + ELSE + -- Cursor provided: determine if it refers to a folder or leaf + IF EXISTS ( + SELECT 1 FROM storage.objects o + WHERE o.bucket_id = _bucket_id + AND o.name COLLATE "C" LIKE v_start || delimiter_param || '%' + LIMIT 1 + ) THEN + -- Cursor refers to a folder + IF v_is_asc THEN + v_next_seek := v_start || chr(ascii(delimiter_param) + 1); + ELSE + v_next_seek := v_start || delimiter_param; + END IF; + ELSE + -- Cursor refers to a leaf object + IF v_is_asc THEN + v_next_seek := v_start || delimiter_param; + ELSE + v_next_seek := v_start; + END IF; + END IF; + END IF; + + -- ======================================================================== + -- MAIN LOOP: Hybrid peek-then-batch algorithm + -- Uses STATIC SQL for peek (hot path) and DYNAMIC SQL for batch + -- ======================================================================== + LOOP + EXIT WHEN v_count >= max_keys; + + -- STEP 1: PEEK using STATIC SQL (plan cached, very fast) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_next_seek AND o.name COLLATE "C" < v_upper_bound + ORDER BY o.name COLLATE "C" ASC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_next_seek + ORDER BY o.name COLLATE "C" ASC LIMIT 1; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" < v_next_seek AND o.name COLLATE "C" >= v_prefix + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix <> '' THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" < v_next_seek AND o.name COLLATE "C" >= v_prefix + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" < v_next_seek + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + END IF; + END IF; + + EXIT WHEN v_peek_name IS NULL; + + -- STEP 2: Check if this is a FOLDER or FILE + v_common_prefix := storage.get_common_prefix(v_peek_name, v_prefix, delimiter_param); + + IF v_common_prefix IS NOT NULL THEN + -- FOLDER: Emit and skip to next folder (no heap access needed) + name := rtrim(v_common_prefix, delimiter_param); + id := NULL; + updated_at := NULL; + created_at := NULL; + last_accessed_at := NULL; + metadata := NULL; + RETURN NEXT; + v_count := v_count + 1; + + -- Advance seek past the folder range + IF v_is_asc THEN + v_next_seek := left(v_common_prefix, -1) || chr(ascii(delimiter_param) + 1); + ELSE + v_next_seek := v_common_prefix; + END IF; + ELSE + -- FILE: Batch fetch using DYNAMIC SQL (overhead amortized over many rows) + -- For ASC: upper_bound is the exclusive upper limit (< condition) + -- For DESC: prefix is the inclusive lower limit (>= condition) + FOR v_current IN EXECUTE v_batch_query USING _bucket_id, v_next_seek, + CASE WHEN v_is_asc THEN COALESCE(v_upper_bound, v_prefix) ELSE v_prefix END, v_file_batch_size + LOOP + v_common_prefix := storage.get_common_prefix(v_current.name, v_prefix, delimiter_param); + + IF v_common_prefix IS NOT NULL THEN + -- Hit a folder: exit batch, let peek handle it + v_next_seek := v_current.name; + EXIT; + END IF; + + -- Emit file + name := v_current.name; + id := v_current.id; + updated_at := v_current.updated_at; + created_at := v_current.created_at; + last_accessed_at := v_current.last_accessed_at; + metadata := v_current.metadata; + RETURN NEXT; + v_count := v_count + 1; + + -- Advance seek past this file + IF v_is_asc THEN + v_next_seek := v_current.name || delimiter_param; + ELSE + v_next_seek := v_current.name; + END IF; + + EXIT WHEN v_count >= max_keys; + END LOOP; + END IF; + END LOOP; +END; +$_$; + + +-- +-- Name: operation(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.operation() RETURNS text + LANGUAGE plpgsql STABLE + AS $$ +BEGIN + RETURN current_setting('storage.operation', true); +END; +$$; + + +-- +-- Name: protect_delete(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.protect_delete() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + -- Check if storage.allow_delete_query is set to 'true' + IF COALESCE(current_setting('storage.allow_delete_query', true), 'false') != 'true' THEN + RAISE EXCEPTION 'Direct deletion from storage tables is not allowed. Use the Storage API instead.' + USING HINT = 'This prevents accidental data loss from orphaned objects.', + ERRCODE = '42501'; + END IF; + RETURN NULL; +END; +$$; + + +-- +-- Name: search(text, text, integer, integer, integer, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.search(prefix text, bucketname text, limits integer DEFAULT 100, levels integer DEFAULT 1, offsets integer DEFAULT 0, search text DEFAULT ''::text, sortcolumn text DEFAULT 'name'::text, sortorder text DEFAULT 'asc'::text) RETURNS TABLE(name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb) + LANGUAGE plpgsql STABLE + AS $_$ +DECLARE + v_peek_name TEXT; + v_current RECORD; + v_common_prefix TEXT; + v_delimiter CONSTANT TEXT := '/'; + + -- Configuration + v_limit INT; + v_prefix TEXT; + v_prefix_lower TEXT; + v_is_asc BOOLEAN; + v_order_by TEXT; + v_sort_order TEXT; + v_upper_bound TEXT; + v_file_batch_size INT; + + -- Dynamic SQL for batch query only + v_batch_query TEXT; + + -- Seek state + v_next_seek TEXT; + v_count INT := 0; + v_skipped INT := 0; +BEGIN + -- ======================================================================== + -- INITIALIZATION + -- ======================================================================== + v_limit := LEAST(coalesce(limits, 100), 1500); + v_prefix := coalesce(prefix, '') || coalesce(search, ''); + v_prefix_lower := lower(v_prefix); + v_is_asc := lower(coalesce(sortorder, 'asc')) = 'asc'; + v_file_batch_size := LEAST(GREATEST(v_limit * 2, 100), 1000); + + -- Validate sort column + CASE lower(coalesce(sortcolumn, 'name')) + WHEN 'name' THEN v_order_by := 'name'; + WHEN 'updated_at' THEN v_order_by := 'updated_at'; + WHEN 'created_at' THEN v_order_by := 'created_at'; + WHEN 'last_accessed_at' THEN v_order_by := 'last_accessed_at'; + ELSE v_order_by := 'name'; + END CASE; + + v_sort_order := CASE WHEN v_is_asc THEN 'asc' ELSE 'desc' END; + + -- ======================================================================== + -- NON-NAME SORTING: Use path_tokens approach (unchanged) + -- ======================================================================== + IF v_order_by != 'name' THEN + RETURN QUERY EXECUTE format( + $sql$ + WITH folders AS ( + SELECT path_tokens[$1] AS folder + FROM storage.objects + WHERE objects.name ILIKE $2 || '%%' + AND bucket_id = $3 + AND array_length(objects.path_tokens, 1) <> $1 + GROUP BY folder + ORDER BY folder %s + ) + (SELECT folder AS "name", + NULL::uuid AS id, + NULL::timestamptz AS updated_at, + NULL::timestamptz AS created_at, + NULL::timestamptz AS last_accessed_at, + NULL::jsonb AS metadata FROM folders) + UNION ALL + (SELECT path_tokens[$1] AS "name", + id, updated_at, created_at, last_accessed_at, metadata + FROM storage.objects + WHERE objects.name ILIKE $2 || '%%' + AND bucket_id = $3 + AND array_length(objects.path_tokens, 1) = $1 + ORDER BY %I %s) + LIMIT $4 OFFSET $5 + $sql$, v_sort_order, v_order_by, v_sort_order + ) USING levels, v_prefix, bucketname, v_limit, offsets; + RETURN; + END IF; + + -- ======================================================================== + -- NAME SORTING: Hybrid skip-scan with batch optimization + -- ======================================================================== + + -- Calculate upper bound for prefix filtering + IF v_prefix_lower = '' THEN + v_upper_bound := NULL; + ELSIF right(v_prefix_lower, 1) = v_delimiter THEN + v_upper_bound := left(v_prefix_lower, -1) || chr(ascii(v_delimiter) + 1); + ELSE + v_upper_bound := left(v_prefix_lower, -1) || chr(ascii(right(v_prefix_lower, 1)) + 1); + END IF; + + -- Build batch query (dynamic SQL - called infrequently, amortized over many rows) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" >= $2 ' || + 'AND lower(o.name) COLLATE "C" < $3 ORDER BY lower(o.name) COLLATE "C" ASC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" >= $2 ' || + 'ORDER BY lower(o.name) COLLATE "C" ASC LIMIT $4'; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" < $2 ' || + 'AND lower(o.name) COLLATE "C" >= $3 ORDER BY lower(o.name) COLLATE "C" DESC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" < $2 ' || + 'ORDER BY lower(o.name) COLLATE "C" DESC LIMIT $4'; + END IF; + END IF; + + -- Initialize seek position + IF v_is_asc THEN + v_next_seek := v_prefix_lower; + ELSE + -- DESC: find the last item in range first (static SQL) + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_prefix_lower AND lower(o.name) COLLATE "C" < v_upper_bound + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix_lower <> '' THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_prefix_lower + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + END IF; + + IF v_peek_name IS NOT NULL THEN + v_next_seek := lower(v_peek_name) || v_delimiter; + ELSE + RETURN; + END IF; + END IF; + + -- ======================================================================== + -- MAIN LOOP: Hybrid peek-then-batch algorithm + -- Uses STATIC SQL for peek (hot path) and DYNAMIC SQL for batch + -- ======================================================================== + LOOP + EXIT WHEN v_count >= v_limit; + + -- STEP 1: PEEK using STATIC SQL (plan cached, very fast) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_next_seek AND lower(o.name) COLLATE "C" < v_upper_bound + ORDER BY lower(o.name) COLLATE "C" ASC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_next_seek + ORDER BY lower(o.name) COLLATE "C" ASC LIMIT 1; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" < v_next_seek AND lower(o.name) COLLATE "C" >= v_prefix_lower + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix_lower <> '' THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" < v_next_seek AND lower(o.name) COLLATE "C" >= v_prefix_lower + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" < v_next_seek + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + END IF; + END IF; + + EXIT WHEN v_peek_name IS NULL; + + -- STEP 2: Check if this is a FOLDER or FILE + v_common_prefix := storage.get_common_prefix(lower(v_peek_name), v_prefix_lower, v_delimiter); + + IF v_common_prefix IS NOT NULL THEN + -- FOLDER: Handle offset, emit if needed, skip to next folder + IF v_skipped < offsets THEN + v_skipped := v_skipped + 1; + ELSE + name := split_part(rtrim(v_common_prefix, v_delimiter), v_delimiter, levels); + id := NULL; + updated_at := NULL; + created_at := NULL; + last_accessed_at := NULL; + metadata := NULL; + RETURN NEXT; + v_count := v_count + 1; + END IF; + + -- Advance seek past the folder range + IF v_is_asc THEN + v_next_seek := lower(left(v_common_prefix, -1)) || chr(ascii(v_delimiter) + 1); + ELSE + v_next_seek := lower(v_common_prefix); + END IF; + ELSE + -- FILE: Batch fetch using DYNAMIC SQL (overhead amortized over many rows) + -- For ASC: upper_bound is the exclusive upper limit (< condition) + -- For DESC: prefix_lower is the inclusive lower limit (>= condition) + FOR v_current IN EXECUTE v_batch_query + USING bucketname, v_next_seek, + CASE WHEN v_is_asc THEN COALESCE(v_upper_bound, v_prefix_lower) ELSE v_prefix_lower END, v_file_batch_size + LOOP + v_common_prefix := storage.get_common_prefix(lower(v_current.name), v_prefix_lower, v_delimiter); + + IF v_common_prefix IS NOT NULL THEN + -- Hit a folder: exit batch, let peek handle it + v_next_seek := lower(v_current.name); + EXIT; + END IF; + + -- Handle offset skipping + IF v_skipped < offsets THEN + v_skipped := v_skipped + 1; + ELSE + -- Emit file + name := split_part(v_current.name, v_delimiter, levels); + id := v_current.id; + updated_at := v_current.updated_at; + created_at := v_current.created_at; + last_accessed_at := v_current.last_accessed_at; + metadata := v_current.metadata; + RETURN NEXT; + v_count := v_count + 1; + END IF; + + -- Advance seek past this file + IF v_is_asc THEN + v_next_seek := lower(v_current.name) || v_delimiter; + ELSE + v_next_seek := lower(v_current.name); + END IF; + + EXIT WHEN v_count >= v_limit; + END LOOP; + END IF; + END LOOP; +END; +$_$; + + +-- +-- Name: search_by_timestamp(text, text, integer, integer, text, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.search_by_timestamp(p_prefix text, p_bucket_id text, p_limit integer, p_level integer, p_start_after text, p_sort_order text, p_sort_column text, p_sort_column_after text) RETURNS TABLE(key text, name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb) + LANGUAGE plpgsql STABLE + AS $_$ +DECLARE + v_cursor_op text; + v_query text; + v_prefix text; +BEGIN + v_prefix := coalesce(p_prefix, ''); + + IF p_sort_order = 'asc' THEN + v_cursor_op := '>'; + ELSE + v_cursor_op := '<'; + END IF; + + v_query := format($sql$ + WITH raw_objects AS ( + SELECT + o.name AS obj_name, + o.id AS obj_id, + o.updated_at AS obj_updated_at, + o.created_at AS obj_created_at, + o.last_accessed_at AS obj_last_accessed_at, + o.metadata AS obj_metadata, + storage.get_common_prefix(o.name, $1, '/') AS common_prefix + FROM storage.objects o + WHERE o.bucket_id = $2 + AND o.name COLLATE "C" LIKE $1 || '%%' + ), + -- Aggregate common prefixes (folders) + -- Both created_at and updated_at use MIN(obj_created_at) to match the old prefixes table behavior + aggregated_prefixes AS ( + SELECT + rtrim(common_prefix, '/') AS name, + NULL::uuid AS id, + MIN(obj_created_at) AS updated_at, + MIN(obj_created_at) AS created_at, + NULL::timestamptz AS last_accessed_at, + NULL::jsonb AS metadata, + TRUE AS is_prefix + FROM raw_objects + WHERE common_prefix IS NOT NULL + GROUP BY common_prefix + ), + leaf_objects AS ( + SELECT + obj_name AS name, + obj_id AS id, + obj_updated_at AS updated_at, + obj_created_at AS created_at, + obj_last_accessed_at AS last_accessed_at, + obj_metadata AS metadata, + FALSE AS is_prefix + FROM raw_objects + WHERE common_prefix IS NULL + ), + combined AS ( + SELECT * FROM aggregated_prefixes + UNION ALL + SELECT * FROM leaf_objects + ), + filtered AS ( + SELECT * + FROM combined + WHERE ( + $5 = '' + OR ROW( + date_trunc('milliseconds', %I), + name COLLATE "C" + ) %s ROW( + COALESCE(NULLIF($6, '')::timestamptz, 'epoch'::timestamptz), + $5 + ) + ) + ) + SELECT + split_part(name, '/', $3) AS key, + name, + id, + updated_at, + created_at, + last_accessed_at, + metadata + FROM filtered + ORDER BY + COALESCE(date_trunc('milliseconds', %I), 'epoch'::timestamptz) %s, + name COLLATE "C" %s + LIMIT $4 + $sql$, + p_sort_column, + v_cursor_op, + p_sort_column, + p_sort_order, + p_sort_order + ); + + RETURN QUERY EXECUTE v_query + USING v_prefix, p_bucket_id, p_level, p_limit, p_start_after, p_sort_column_after; +END; +$_$; + + +-- +-- Name: search_v2(text, text, integer, integer, text, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.search_v2(prefix text, bucket_name text, limits integer DEFAULT 100, levels integer DEFAULT 1, start_after text DEFAULT ''::text, sort_order text DEFAULT 'asc'::text, sort_column text DEFAULT 'name'::text, sort_column_after text DEFAULT ''::text) RETURNS TABLE(key text, name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb) + LANGUAGE plpgsql STABLE + AS $$ +DECLARE + v_sort_col text; + v_sort_ord text; + v_limit int; +BEGIN + -- Cap limit to maximum of 1500 records + v_limit := LEAST(coalesce(limits, 100), 1500); + + -- Validate and normalize sort_order + v_sort_ord := lower(coalesce(sort_order, 'asc')); + IF v_sort_ord NOT IN ('asc', 'desc') THEN + v_sort_ord := 'asc'; + END IF; + + -- Validate and normalize sort_column + v_sort_col := lower(coalesce(sort_column, 'name')); + IF v_sort_col NOT IN ('name', 'updated_at', 'created_at') THEN + v_sort_col := 'name'; + END IF; + + -- Route to appropriate implementation + IF v_sort_col = 'name' THEN + -- Use list_objects_with_delimiter for name sorting (most efficient: O(k * log n)) + RETURN QUERY + SELECT + split_part(l.name, '/', levels) AS key, + l.name AS name, + l.id, + l.updated_at, + l.created_at, + l.last_accessed_at, + l.metadata + FROM storage.list_objects_with_delimiter( + bucket_name, + coalesce(prefix, ''), + '/', + v_limit, + start_after, + '', + v_sort_ord + ) l; + ELSE + -- Use aggregation approach for timestamp sorting + -- Not efficient for large datasets but supports correct pagination + RETURN QUERY SELECT * FROM storage.search_by_timestamp( + prefix, bucket_name, v_limit, levels, start_after, + v_sort_ord, v_sort_col, sort_column_after + ); + END IF; +END; +$$; + + +-- +-- Name: update_updated_at_column(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.update_updated_at_column() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: http_request(); Type: FUNCTION; Schema: supabase_functions; Owner: - +-- + +CREATE FUNCTION supabase_functions.http_request() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'supabase_functions' + AS $$ + DECLARE + request_id bigint; + payload jsonb; + url text := TG_ARGV[0]::text; + method text := TG_ARGV[1]::text; + headers jsonb DEFAULT '{}'::jsonb; + params jsonb DEFAULT '{}'::jsonb; + timeout_ms integer DEFAULT 1000; + BEGIN + IF url IS NULL OR url = 'null' THEN + RAISE EXCEPTION 'url argument is missing'; + END IF; + + IF method IS NULL OR method = 'null' THEN + RAISE EXCEPTION 'method argument is missing'; + END IF; + + IF TG_ARGV[2] IS NULL OR TG_ARGV[2] = 'null' THEN + headers = '{"Content-Type": "application/json"}'::jsonb; + ELSE + headers = TG_ARGV[2]::jsonb; + END IF; + + IF TG_ARGV[3] IS NULL OR TG_ARGV[3] = 'null' THEN + params = '{}'::jsonb; + ELSE + params = TG_ARGV[3]::jsonb; + END IF; + + IF TG_ARGV[4] IS NULL OR TG_ARGV[4] = 'null' THEN + timeout_ms = 1000; + ELSE + timeout_ms = TG_ARGV[4]::integer; + END IF; + + CASE + WHEN method = 'GET' THEN + SELECT http_get INTO request_id FROM net.http_get( + url, + params, + headers, + timeout_ms + ); + WHEN method = 'POST' THEN + payload = jsonb_build_object( + 'old_record', OLD, + 'record', NEW, + 'type', TG_OP, + 'table', TG_TABLE_NAME, + 'schema', TG_TABLE_SCHEMA + ); + + SELECT http_post INTO request_id FROM net.http_post( + url, + payload, + params, + headers, + timeout_ms + ); + ELSE + RAISE EXCEPTION 'method argument % is invalid', method; + END CASE; + + INSERT INTO supabase_functions.hooks + (hook_table_id, hook_name, request_id) + VALUES + (TG_RELID, TG_NAME, request_id); + + RETURN NEW; + END +$$; + + +-- +-- Name: extensions; Type: TABLE; Schema: _realtime; Owner: - +-- + +CREATE TABLE _realtime.extensions ( + id uuid NOT NULL, + type text, + settings jsonb, + tenant_external_id text, + inserted_at timestamp(0) without time zone NOT NULL, + updated_at timestamp(0) without time zone NOT NULL +); + + +-- +-- Name: schema_migrations; Type: TABLE; Schema: _realtime; Owner: - +-- + +CREATE TABLE _realtime.schema_migrations ( + version bigint NOT NULL, + inserted_at timestamp(0) without time zone +); + + +-- +-- Name: tenants; Type: TABLE; Schema: _realtime; Owner: - +-- + +CREATE TABLE _realtime.tenants ( + id uuid NOT NULL, + name text, + external_id text, + jwt_secret text, + max_concurrent_users integer DEFAULT 200 NOT NULL, + inserted_at timestamp(0) without time zone NOT NULL, + updated_at timestamp(0) without time zone NOT NULL, + max_events_per_second integer DEFAULT 100 NOT NULL, + postgres_cdc_default text DEFAULT 'postgres_cdc_rls'::text, + max_bytes_per_second integer DEFAULT 100000 NOT NULL, + max_channels_per_client integer DEFAULT 100 NOT NULL, + max_joins_per_second integer DEFAULT 500 NOT NULL, + suspend boolean DEFAULT false, + jwt_jwks jsonb, + notify_private_alpha boolean DEFAULT false, + private_only boolean DEFAULT false NOT NULL, + migrations_ran integer DEFAULT 0, + broadcast_adapter character varying(255) DEFAULT 'gen_rpc'::character varying, + max_presence_events_per_second integer DEFAULT 1000, + max_payload_size_in_kb integer DEFAULT 3000, + CONSTRAINT jwt_secret_or_jwt_jwks_required CHECK (((jwt_secret IS NOT NULL) OR (jwt_jwks IS NOT NULL))) +); + + +-- +-- Name: audit_log_entries; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.audit_log_entries ( + instance_id uuid, + id uuid NOT NULL, + payload json, + created_at timestamp with time zone, + ip_address character varying(64) DEFAULT ''::character varying NOT NULL +); + + +-- +-- Name: TABLE audit_log_entries; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.audit_log_entries IS 'Auth: Audit trail for user actions.'; + + +-- +-- Name: flow_state; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.flow_state ( + id uuid NOT NULL, + user_id uuid, + auth_code text, + code_challenge_method auth.code_challenge_method, + code_challenge text, + provider_type text NOT NULL, + provider_access_token text, + provider_refresh_token text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + authentication_method text NOT NULL, + auth_code_issued_at timestamp with time zone, + invite_token text, + referrer text, + oauth_client_state_id uuid, + linking_target_id uuid, + email_optional boolean DEFAULT false NOT NULL +); + + +-- +-- Name: TABLE flow_state; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.flow_state IS 'Stores metadata for all OAuth/SSO login flows'; + + +-- +-- Name: identities; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.identities ( + provider_id text NOT NULL, + user_id uuid NOT NULL, + identity_data jsonb NOT NULL, + provider text NOT NULL, + last_sign_in_at timestamp with time zone, + created_at timestamp with time zone, + updated_at timestamp with time zone, + email text GENERATED ALWAYS AS (lower((identity_data ->> 'email'::text))) STORED, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: TABLE identities; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.identities IS 'Auth: Stores identities associated to a user.'; + + +-- +-- Name: COLUMN identities.email; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.identities.email IS 'Auth: Email is a generated column that references the optional email property in the identity_data'; + + +-- +-- Name: instances; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.instances ( + id uuid NOT NULL, + uuid uuid, + raw_base_config text, + created_at timestamp with time zone, + updated_at timestamp with time zone +); + + +-- +-- Name: TABLE instances; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.instances IS 'Auth: Manages users across multiple sites.'; + + +-- +-- Name: mfa_amr_claims; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.mfa_amr_claims ( + session_id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + authentication_method text NOT NULL, + id uuid NOT NULL +); + + +-- +-- Name: TABLE mfa_amr_claims; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.mfa_amr_claims IS 'auth: stores authenticator method reference claims for multi factor authentication'; + + +-- +-- Name: mfa_challenges; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.mfa_challenges ( + id uuid NOT NULL, + factor_id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + verified_at timestamp with time zone, + ip_address inet NOT NULL, + otp_code text, + web_authn_session_data jsonb +); + + +-- +-- Name: TABLE mfa_challenges; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.mfa_challenges IS 'auth: stores metadata about challenge requests made'; + + +-- +-- Name: mfa_factors; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.mfa_factors ( + id uuid NOT NULL, + user_id uuid NOT NULL, + friendly_name text, + factor_type auth.factor_type NOT NULL, + status auth.factor_status NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + secret text, + phone text, + last_challenged_at timestamp with time zone, + web_authn_credential jsonb, + web_authn_aaguid uuid, + last_webauthn_challenge_data jsonb +); + + +-- +-- Name: TABLE mfa_factors; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.mfa_factors IS 'auth: stores metadata about factors'; + + +-- +-- Name: COLUMN mfa_factors.last_webauthn_challenge_data; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.mfa_factors.last_webauthn_challenge_data IS 'Stores the latest WebAuthn challenge data including attestation/assertion for customer verification'; + + +-- +-- Name: oauth_authorizations; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_authorizations ( + id uuid NOT NULL, + authorization_id text NOT NULL, + client_id uuid NOT NULL, + user_id uuid, + redirect_uri text NOT NULL, + scope text NOT NULL, + state text, + resource text, + code_challenge text, + code_challenge_method auth.code_challenge_method, + response_type auth.oauth_response_type DEFAULT 'code'::auth.oauth_response_type NOT NULL, + status auth.oauth_authorization_status DEFAULT 'pending'::auth.oauth_authorization_status NOT NULL, + authorization_code text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + expires_at timestamp with time zone DEFAULT (now() + '00:03:00'::interval) NOT NULL, + approved_at timestamp with time zone, + nonce text, + CONSTRAINT oauth_authorizations_authorization_code_length CHECK ((char_length(authorization_code) <= 255)), + CONSTRAINT oauth_authorizations_code_challenge_length CHECK ((char_length(code_challenge) <= 128)), + CONSTRAINT oauth_authorizations_expires_at_future CHECK ((expires_at > created_at)), + CONSTRAINT oauth_authorizations_nonce_length CHECK ((char_length(nonce) <= 255)), + CONSTRAINT oauth_authorizations_redirect_uri_length CHECK ((char_length(redirect_uri) <= 2048)), + CONSTRAINT oauth_authorizations_resource_length CHECK ((char_length(resource) <= 2048)), + CONSTRAINT oauth_authorizations_scope_length CHECK ((char_length(scope) <= 4096)), + CONSTRAINT oauth_authorizations_state_length CHECK ((char_length(state) <= 4096)) +); + + +-- +-- Name: oauth_client_states; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_client_states ( + id uuid NOT NULL, + provider_type text NOT NULL, + code_verifier text, + created_at timestamp with time zone NOT NULL +); + + +-- +-- Name: TABLE oauth_client_states; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.oauth_client_states IS 'Stores OAuth states for third-party provider authentication flows where Supabase acts as the OAuth client.'; + + +-- +-- Name: oauth_clients; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_clients ( + id uuid NOT NULL, + client_secret_hash text, + registration_type auth.oauth_registration_type NOT NULL, + redirect_uris text NOT NULL, + grant_types text NOT NULL, + client_name text, + client_uri text, + logo_uri text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + client_type auth.oauth_client_type DEFAULT 'confidential'::auth.oauth_client_type NOT NULL, + token_endpoint_auth_method text NOT NULL, + CONSTRAINT oauth_clients_client_name_length CHECK ((char_length(client_name) <= 1024)), + CONSTRAINT oauth_clients_client_uri_length CHECK ((char_length(client_uri) <= 2048)), + CONSTRAINT oauth_clients_logo_uri_length CHECK ((char_length(logo_uri) <= 2048)), + CONSTRAINT oauth_clients_token_endpoint_auth_method_check CHECK ((token_endpoint_auth_method = ANY (ARRAY['client_secret_basic'::text, 'client_secret_post'::text, 'none'::text]))) +); + + +-- +-- Name: oauth_consents; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_consents ( + id uuid NOT NULL, + user_id uuid NOT NULL, + client_id uuid NOT NULL, + scopes text NOT NULL, + granted_at timestamp with time zone DEFAULT now() NOT NULL, + revoked_at timestamp with time zone, + CONSTRAINT oauth_consents_revoked_after_granted CHECK (((revoked_at IS NULL) OR (revoked_at >= granted_at))), + CONSTRAINT oauth_consents_scopes_length CHECK ((char_length(scopes) <= 2048)), + CONSTRAINT oauth_consents_scopes_not_empty CHECK ((char_length(TRIM(BOTH FROM scopes)) > 0)) +); + + +-- +-- Name: one_time_tokens; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.one_time_tokens ( + id uuid NOT NULL, + user_id uuid NOT NULL, + token_type auth.one_time_token_type NOT NULL, + token_hash text NOT NULL, + relates_to text NOT NULL, + created_at timestamp without time zone DEFAULT now() NOT NULL, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + CONSTRAINT one_time_tokens_token_hash_check CHECK ((char_length(token_hash) > 0)) +); + + +-- +-- Name: refresh_tokens; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.refresh_tokens ( + instance_id uuid, + id bigint NOT NULL, + token character varying(255), + user_id character varying(255), + revoked boolean, + created_at timestamp with time zone, + updated_at timestamp with time zone, + parent character varying(255), + session_id uuid +); + + +-- +-- Name: TABLE refresh_tokens; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.refresh_tokens IS 'Auth: Store of tokens used to refresh JWT tokens once they expire.'; + + +-- +-- Name: refresh_tokens_id_seq; Type: SEQUENCE; Schema: auth; Owner: - +-- + +CREATE SEQUENCE auth.refresh_tokens_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: refresh_tokens_id_seq; Type: SEQUENCE OWNED BY; Schema: auth; Owner: - +-- + +ALTER SEQUENCE auth.refresh_tokens_id_seq OWNED BY auth.refresh_tokens.id; + + +-- +-- Name: saml_providers; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.saml_providers ( + id uuid NOT NULL, + sso_provider_id uuid NOT NULL, + entity_id text NOT NULL, + metadata_xml text NOT NULL, + metadata_url text, + attribute_mapping jsonb, + created_at timestamp with time zone, + updated_at timestamp with time zone, + name_id_format text, + CONSTRAINT "entity_id not empty" CHECK ((char_length(entity_id) > 0)), + CONSTRAINT "metadata_url not empty" CHECK (((metadata_url = NULL::text) OR (char_length(metadata_url) > 0))), + CONSTRAINT "metadata_xml not empty" CHECK ((char_length(metadata_xml) > 0)) +); + + +-- +-- Name: TABLE saml_providers; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.saml_providers IS 'Auth: Manages SAML Identity Provider connections.'; + + +-- +-- Name: saml_relay_states; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.saml_relay_states ( + id uuid NOT NULL, + sso_provider_id uuid NOT NULL, + request_id text NOT NULL, + for_email text, + redirect_to text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + flow_state_id uuid, + CONSTRAINT "request_id not empty" CHECK ((char_length(request_id) > 0)) +); + + +-- +-- Name: TABLE saml_relay_states; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.saml_relay_states IS 'Auth: Contains SAML Relay State information for each Service Provider initiated login.'; + + +-- +-- Name: schema_migrations; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.schema_migrations ( + version character varying(255) NOT NULL +); + + +-- +-- Name: TABLE schema_migrations; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.schema_migrations IS 'Auth: Manages updates to the auth system.'; + + +-- +-- Name: sessions; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.sessions ( + id uuid NOT NULL, + user_id uuid NOT NULL, + created_at timestamp with time zone, + updated_at timestamp with time zone, + factor_id uuid, + aal auth.aal_level, + not_after timestamp with time zone, + refreshed_at timestamp without time zone, + user_agent text, + ip inet, + tag text, + oauth_client_id uuid, + refresh_token_hmac_key text, + refresh_token_counter bigint, + scopes text, + CONSTRAINT sessions_scopes_length CHECK ((char_length(scopes) <= 4096)) +); + + +-- +-- Name: TABLE sessions; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.sessions IS 'Auth: Stores session data associated to a user.'; + + +-- +-- Name: COLUMN sessions.not_after; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sessions.not_after IS 'Auth: Not after is a nullable column that contains a timestamp after which the session should be regarded as expired.'; + + +-- +-- Name: COLUMN sessions.refresh_token_hmac_key; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sessions.refresh_token_hmac_key IS 'Holds a HMAC-SHA256 key used to sign refresh tokens for this session.'; + + +-- +-- Name: COLUMN sessions.refresh_token_counter; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sessions.refresh_token_counter IS 'Holds the ID (counter) of the last issued refresh token.'; + + +-- +-- Name: sso_domains; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.sso_domains ( + id uuid NOT NULL, + sso_provider_id uuid NOT NULL, + domain text NOT NULL, + created_at timestamp with time zone, + updated_at timestamp with time zone, + CONSTRAINT "domain not empty" CHECK ((char_length(domain) > 0)) +); + + +-- +-- Name: TABLE sso_domains; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.sso_domains IS 'Auth: Manages SSO email address domain mapping to an SSO Identity Provider.'; + + +-- +-- Name: sso_providers; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.sso_providers ( + id uuid NOT NULL, + resource_id text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + disabled boolean, + CONSTRAINT "resource_id not empty" CHECK (((resource_id = NULL::text) OR (char_length(resource_id) > 0))) +); + + +-- +-- Name: TABLE sso_providers; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.sso_providers IS 'Auth: Manages SSO identity provider information; see saml_providers for SAML.'; + + +-- +-- Name: COLUMN sso_providers.resource_id; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sso_providers.resource_id IS 'Auth: Uniquely identifies a SSO provider according to a user-chosen resource ID (case insensitive), useful in infrastructure as code.'; + + +-- +-- Name: users; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.users ( + instance_id uuid, + id uuid NOT NULL, + aud character varying(255), + role character varying(255), + email character varying(255), + encrypted_password character varying(255), + email_confirmed_at timestamp with time zone, + invited_at timestamp with time zone, + confirmation_token character varying(255), + confirmation_sent_at timestamp with time zone, + recovery_token character varying(255), + recovery_sent_at timestamp with time zone, + email_change_token_new character varying(255), + email_change character varying(255), + email_change_sent_at timestamp with time zone, + last_sign_in_at timestamp with time zone, + raw_app_meta_data jsonb, + raw_user_meta_data jsonb, + is_super_admin boolean, + created_at timestamp with time zone, + updated_at timestamp with time zone, + phone text DEFAULT NULL::character varying, + phone_confirmed_at timestamp with time zone, + phone_change text DEFAULT ''::character varying, + phone_change_token character varying(255) DEFAULT ''::character varying, + phone_change_sent_at timestamp with time zone, + confirmed_at timestamp with time zone GENERATED ALWAYS AS (LEAST(email_confirmed_at, phone_confirmed_at)) STORED, + email_change_token_current character varying(255) DEFAULT ''::character varying, + email_change_confirm_status smallint DEFAULT 0, + banned_until timestamp with time zone, + reauthentication_token character varying(255) DEFAULT ''::character varying, + reauthentication_sent_at timestamp with time zone, + is_sso_user boolean DEFAULT false NOT NULL, + deleted_at timestamp with time zone, + is_anonymous boolean DEFAULT false NOT NULL, + CONSTRAINT users_email_change_confirm_status_check CHECK (((email_change_confirm_status >= 0) AND (email_change_confirm_status <= 2))) +); + + +-- +-- Name: TABLE users; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.users IS 'Auth: Stores user login data within a secure schema.'; + + +-- +-- Name: COLUMN users.is_sso_user; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.users.is_sso_user IS 'Auth: Set this column to true when the account comes from SSO. These accounts can have duplicate emails.'; + + +-- +-- Name: _db_migrations; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public._db_migrations ( + id integer NOT NULL, + filename text NOT NULL, + hash text NOT NULL, + category text DEFAULT 'migration'::text NOT NULL, + applied_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: _db_migrations_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public._db_migrations_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: _db_migrations_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public._db_migrations_id_seq OWNED BY public._db_migrations.id; + + +-- +-- Name: addon_credits; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.addon_credits ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid, + addon_type text NOT NULL, + balance integer DEFAULT 0 NOT NULL, + total_purchased integer DEFAULT 0 NOT NULL, + total_consumed integer DEFAULT 0 NOT NULL, + low_balance_threshold integer DEFAULT 10, + low_balance_notified boolean DEFAULT false, + daily_limit integer, + hourly_limit integer, + daily_used integer DEFAULT 0, + hourly_used integer DEFAULT 0, + daily_reset_at timestamp with time zone, + hourly_reset_at timestamp with time zone, + from_number_override text, + expires_at timestamp with time zone, + is_active boolean DEFAULT true, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: TABLE addon_credits; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.addon_credits IS 'Saldo de cr??ditos de add-ons por tenant. Um registro por (tenant_id, addon_type).'; + + +-- +-- Name: addon_products; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.addon_products ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + slug text NOT NULL, + name text NOT NULL, + description text, + addon_type text NOT NULL, + icon text DEFAULT 'pi pi-box'::text, + credits_amount integer DEFAULT 0, + price_cents integer DEFAULT 0 NOT NULL, + currency text DEFAULT 'BRL'::text, + is_active boolean DEFAULT true, + is_visible boolean DEFAULT true, + sort_order integer DEFAULT 0, + metadata jsonb DEFAULT '{}'::jsonb, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + deleted_at timestamp with time zone +); + + +-- +-- Name: TABLE addon_products; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.addon_products IS 'Cat??logo de recursos extras vendidos pela plataforma (SMS, servidor, dom??nio, etc.)'; + + +-- +-- Name: addon_transactions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.addon_transactions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid, + addon_type text NOT NULL, + type text NOT NULL, + amount integer NOT NULL, + balance_before integer DEFAULT 0 NOT NULL, + balance_after integer DEFAULT 0 NOT NULL, + product_id uuid, + queue_id uuid, + description text, + admin_user_id uuid, + payment_method text, + payment_reference text, + price_cents integer, + currency text DEFAULT 'BRL'::text, + created_at timestamp with time zone DEFAULT now(), + metadata jsonb DEFAULT '{}'::jsonb +); + + +-- +-- Name: TABLE addon_transactions; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.addon_transactions IS 'Hist??rico de todas as transa????es de cr??ditos: compras, consumo, ajustes, reembolsos.'; + + +-- +-- Name: agenda_bloqueios; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_bloqueios ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + tipo text NOT NULL, + titulo text NOT NULL, + data_inicio date NOT NULL, + data_fim date, + hora_inicio time without time zone, + hora_fim time without time zone, + recorrente boolean DEFAULT false NOT NULL, + dia_semana smallint, + observacao text, + origem text DEFAULT 'manual'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT agenda_bloqueios_tipo_check CHECK ((tipo = ANY (ARRAY['feriado_nacional'::text, 'feriado_municipal'::text, 'bloqueio'::text]))) +); + + +-- +-- Name: agenda_configuracoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_configuracoes ( + owner_id uuid NOT NULL, + duracao_padrao_minutos integer DEFAULT 50 NOT NULL, + intervalo_padrao_minutos integer DEFAULT 0 NOT NULL, + timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL, + usar_horario_admin_custom boolean DEFAULT false NOT NULL, + admin_inicio_visualizacao time without time zone, + admin_fim_visualizacao time without time zone, + admin_slot_visual_minutos integer DEFAULT 30 NOT NULL, + online_ativo boolean DEFAULT false NOT NULL, + online_min_antecedencia_horas integer DEFAULT 24 NOT NULL, + online_max_dias_futuro integer DEFAULT 60 NOT NULL, + online_cancelar_ate_horas integer DEFAULT 12 NOT NULL, + online_reagendar_ate_horas integer DEFAULT 12 NOT NULL, + online_limite_agendamentos_futuros integer DEFAULT 1 NOT NULL, + online_modo text DEFAULT 'automatico'::text NOT NULL, + online_buffer_antes_min integer DEFAULT 0 NOT NULL, + online_buffer_depois_min integer DEFAULT 0 NOT NULL, + online_modalidade text DEFAULT 'ambos'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + usar_granularidade_custom boolean DEFAULT false NOT NULL, + granularidade_min integer, + setup_concluido boolean DEFAULT false NOT NULL, + setup_concluido_em timestamp with time zone, + agenda_view_mode text DEFAULT 'full_24h'::text NOT NULL, + agenda_custom_start time without time zone, + agenda_custom_end time without time zone, + session_duration_min integer DEFAULT 50 NOT NULL, + session_break_min integer DEFAULT 10 NOT NULL, + pausas_semanais jsonb DEFAULT '[]'::jsonb NOT NULL, + setup_clinica_concluido boolean DEFAULT false NOT NULL, + setup_clinica_concluido_em timestamp with time zone, + tenant_id uuid, + jornada_igual_todos boolean DEFAULT true, + slot_mode text DEFAULT 'fixed'::text NOT NULL, + atendimento_mode text DEFAULT 'particular'::text, + CONSTRAINT agenda_configuracoes_admin_slot_visual_minutos_check CHECK ((admin_slot_visual_minutos = ANY (ARRAY[5, 10, 15, 20, 30, 60]))), + CONSTRAINT agenda_configuracoes_atendimento_mode_check CHECK (((atendimento_mode IS NULL) OR (atendimento_mode = ANY (ARRAY['particular'::text, 'convenio'::text, 'ambos'::text])))), + CONSTRAINT agenda_configuracoes_check CHECK (((usar_horario_admin_custom = false) OR ((admin_inicio_visualizacao IS NOT NULL) AND (admin_fim_visualizacao IS NOT NULL) AND (admin_fim_visualizacao > admin_inicio_visualizacao)))), + CONSTRAINT agenda_configuracoes_duracao_padrao_minutos_check CHECK (((duracao_padrao_minutos >= 10) AND (duracao_padrao_minutos <= 240))), + CONSTRAINT agenda_configuracoes_granularidade_min_check CHECK (((granularidade_min IS NULL) OR (granularidade_min = ANY (ARRAY[5, 10, 15, 20, 30, 45, 50, 60])))), + CONSTRAINT agenda_configuracoes_intervalo_padrao_minutos_check CHECK (((intervalo_padrao_minutos >= 0) AND (intervalo_padrao_minutos <= 120))), + CONSTRAINT agenda_configuracoes_online_buffer_antes_min_check CHECK (((online_buffer_antes_min >= 0) AND (online_buffer_antes_min <= 120))), + CONSTRAINT agenda_configuracoes_online_buffer_depois_min_check CHECK (((online_buffer_depois_min >= 0) AND (online_buffer_depois_min <= 120))), + CONSTRAINT agenda_configuracoes_online_cancelar_ate_horas_check CHECK (((online_cancelar_ate_horas >= 0) AND (online_cancelar_ate_horas <= 720))), + CONSTRAINT agenda_configuracoes_online_limite_agendamentos_futuros_check CHECK (((online_limite_agendamentos_futuros >= 1) AND (online_limite_agendamentos_futuros <= 10))), + CONSTRAINT agenda_configuracoes_online_max_dias_futuro_check CHECK (((online_max_dias_futuro >= 1) AND (online_max_dias_futuro <= 365))), + CONSTRAINT agenda_configuracoes_online_min_antecedencia_horas_check CHECK (((online_min_antecedencia_horas >= 0) AND (online_min_antecedencia_horas <= 720))), + CONSTRAINT agenda_configuracoes_online_modalidade_check CHECK ((online_modalidade = ANY (ARRAY['online'::text, 'presencial'::text, 'ambos'::text]))), + CONSTRAINT agenda_configuracoes_online_modo_check CHECK ((online_modo = ANY (ARRAY['automatico'::text, 'aprovacao'::text]))), + CONSTRAINT agenda_configuracoes_online_reagendar_ate_horas_check CHECK (((online_reagendar_ate_horas >= 0) AND (online_reagendar_ate_horas <= 720))), + CONSTRAINT agenda_configuracoes_slot_mode_chk CHECK ((slot_mode = ANY (ARRAY['fixed'::text, 'dynamic'::text]))), + CONSTRAINT session_break_min_chk CHECK (((session_break_min >= 0) AND (session_break_min <= 60))), + CONSTRAINT session_duration_min_chk CHECK (((session_duration_min >= 10) AND (session_duration_min <= 240))) +); + + +-- +-- Name: COLUMN agenda_configuracoes.atendimento_mode; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agenda_configuracoes.atendimento_mode IS 'Modo de atendimento: particular | convenio | ambos'; + + +-- +-- Name: agenda_eventos; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_eventos ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tipo public.tipo_evento_agenda DEFAULT 'sessao'::public.tipo_evento_agenda NOT NULL, + status public.status_evento_agenda DEFAULT 'agendado'::public.status_evento_agenda NOT NULL, + titulo text, + observacoes text, + inicio_em timestamp with time zone NOT NULL, + fim_em timestamp with time zone NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + terapeuta_id uuid, + tenant_id uuid NOT NULL, + visibility_scope text DEFAULT 'public'::text NOT NULL, + mirror_of_event_id uuid, + mirror_source text, + patient_id uuid, + determined_commitment_id uuid, + link_online text, + titulo_custom text, + extra_fields jsonb, + recurrence_id uuid, + recurrence_date date, + modalidade text DEFAULT 'presencial'::text, + price numeric(10,2), + billing_contract_id uuid, + billed boolean DEFAULT false NOT NULL, + services_customized boolean DEFAULT false NOT NULL, + insurance_plan_id uuid, + insurance_guide_number text, + insurance_value numeric(10,2), + insurance_plan_service_id uuid, + CONSTRAINT agenda_eventos_check CHECK ((fim_em > inicio_em)) +); + + +-- +-- Name: COLUMN agenda_eventos.price; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agenda_eventos.price IS 'Valor da sess??o em BRL. Preenchido automaticamente pela tabela professional_pricing do profissional.'; + + +-- +-- Name: agenda_excecoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_excecoes ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + data date NOT NULL, + hora_inicio time without time zone, + hora_fim time without time zone, + tipo public.tipo_excecao_agenda NOT NULL, + motivo text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + status public.status_excecao_agenda DEFAULT 'ativo'::public.status_excecao_agenda NOT NULL, + fonte text DEFAULT 'manual'::text NOT NULL, + aplicavel_online boolean DEFAULT true NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_excecoes_check CHECK ((((hora_inicio IS NULL) AND (hora_fim IS NULL)) OR ((hora_inicio IS NOT NULL) AND (hora_fim IS NOT NULL) AND (hora_fim > hora_inicio)))), + CONSTRAINT agenda_excecoes_fonte_check CHECK ((fonte = ANY (ARRAY['manual'::text, 'feriado_google'::text, 'sistema'::text]))) +); + + +-- +-- Name: agenda_online_slots; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_online_slots ( + id bigint NOT NULL, + owner_id uuid NOT NULL, + weekday integer NOT NULL, + "time" time without time zone NOT NULL, + enabled boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_online_slots_weekday_check CHECK ((weekday = ANY (ARRAY[0, 1, 2, 3, 4, 5, 6]))) +); + + +-- +-- Name: agenda_online_slots_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.agenda_online_slots_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: agenda_online_slots_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.agenda_online_slots_id_seq OWNED BY public.agenda_online_slots.id; + + +-- +-- Name: agenda_regras_semanais; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_regras_semanais ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + dia_semana smallint NOT NULL, + hora_inicio time without time zone NOT NULL, + hora_fim time without time zone NOT NULL, + modalidade text DEFAULT 'ambos'::text NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_regras_semanais_check CHECK ((hora_fim > hora_inicio)), + CONSTRAINT agenda_regras_semanais_dia_semana_check CHECK (((dia_semana >= 0) AND (dia_semana <= 6))), + CONSTRAINT agenda_regras_semanais_modalidade_check CHECK (((modalidade = ANY (ARRAY['online'::text, 'presencial'::text, 'ambos'::text])) OR (modalidade IS NULL))) +); + + +-- +-- Name: agenda_slots_bloqueados_semanais; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_slots_bloqueados_semanais ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + dia_semana smallint NOT NULL, + hora_inicio time without time zone NOT NULL, + motivo text, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_slots_bloqueados_semanais_dia_semana_check CHECK (((dia_semana >= 0) AND (dia_semana <= 6))) +); + + +-- +-- Name: agenda_slots_regras; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_slots_regras ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + dia_semana smallint NOT NULL, + passo_minutos integer NOT NULL, + offset_minutos integer DEFAULT 0 NOT NULL, + buffer_antes_min integer DEFAULT 0 NOT NULL, + buffer_depois_min integer DEFAULT 0 NOT NULL, + min_antecedencia_horas integer DEFAULT 0 NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_slots_regras_buffer_antes_min_check CHECK (((buffer_antes_min >= 0) AND (buffer_antes_min <= 240))), + CONSTRAINT agenda_slots_regras_buffer_depois_min_check CHECK (((buffer_depois_min >= 0) AND (buffer_depois_min <= 240))), + CONSTRAINT agenda_slots_regras_dia_semana_check CHECK (((dia_semana >= 0) AND (dia_semana <= 6))), + CONSTRAINT agenda_slots_regras_min_antecedencia_horas_check CHECK (((min_antecedencia_horas >= 0) AND (min_antecedencia_horas <= 720))), + CONSTRAINT agenda_slots_regras_offset_minutos_check CHECK (((offset_minutos >= 0) AND (offset_minutos <= 55))), + CONSTRAINT agenda_slots_regras_passo_minutos_check CHECK (((passo_minutos >= 5) AND (passo_minutos <= 240))) +); + + +-- +-- Name: agendador_configuracoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agendador_configuracoes ( + owner_id uuid NOT NULL, + tenant_id uuid, + ativo boolean DEFAULT false NOT NULL, + link_slug text, + imagem_fundo_url text, + imagem_header_url text, + logomarca_url text, + cor_primaria text DEFAULT '#4b6bff'::text, + nome_exibicao text, + endereco text, + botao_como_chegar_ativo boolean DEFAULT true NOT NULL, + maps_url text, + modo_aprovacao text DEFAULT 'aprovacao'::text NOT NULL, + modalidade text DEFAULT 'presencial'::text NOT NULL, + tipos_habilitados jsonb DEFAULT '["primeira", "retorno"]'::jsonb NOT NULL, + duracao_sessao_min integer DEFAULT 50 NOT NULL, + antecedencia_minima_horas integer DEFAULT 24 NOT NULL, + prazo_resposta_horas integer DEFAULT 2 NOT NULL, + reserva_horas integer DEFAULT 2 NOT NULL, + pagamento_obrigatorio boolean DEFAULT false NOT NULL, + pix_chave text, + pix_countdown_minutos integer DEFAULT 20 NOT NULL, + triagem_motivo boolean DEFAULT true NOT NULL, + triagem_como_conheceu boolean DEFAULT false NOT NULL, + verificacao_email boolean DEFAULT false NOT NULL, + exigir_aceite_lgpd boolean DEFAULT true NOT NULL, + mensagem_boas_vindas text, + texto_como_se_preparar text, + texto_termos_lgpd text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + pagamento_modo text DEFAULT 'sem_pagamento'::text NOT NULL, + pagamento_metodos_visiveis text[] DEFAULT '{}'::text[] NOT NULL, + CONSTRAINT agendador_configuracoes_antecedencia_check CHECK (((antecedencia_minima_horas >= 0) AND (antecedencia_minima_horas <= 720))), + CONSTRAINT agendador_configuracoes_duracao_check CHECK (((duracao_sessao_min >= 10) AND (duracao_sessao_min <= 240))), + CONSTRAINT agendador_configuracoes_modalidade_check CHECK ((modalidade = ANY (ARRAY['presencial'::text, 'online'::text, 'ambos'::text]))), + CONSTRAINT agendador_configuracoes_modo_check CHECK ((modo_aprovacao = ANY (ARRAY['automatico'::text, 'aprovacao'::text]))), + CONSTRAINT agendador_configuracoes_pix_countdown_check CHECK (((pix_countdown_minutos >= 5) AND (pix_countdown_minutos <= 120))), + CONSTRAINT agendador_configuracoes_prazo_check CHECK (((prazo_resposta_horas >= 1) AND (prazo_resposta_horas <= 72))), + CONSTRAINT agendador_configuracoes_reserva_check CHECK (((reserva_horas >= 1) AND (reserva_horas <= 48))) +); + + +-- +-- Name: COLUMN agendador_configuracoes.pagamento_modo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agendador_configuracoes.pagamento_modo IS 'sem_pagamento | pagar_na_hora | pix_antecipado'; + + +-- +-- Name: COLUMN agendador_configuracoes.pagamento_metodos_visiveis; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agendador_configuracoes.pagamento_metodos_visiveis IS 'M??todos exibidos ao paciente quando pagamento_modo = pagar_na_hora. Ex: {pix, deposito, dinheiro, cartao, convenio}'; + + +-- +-- Name: agendador_solicitacoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agendador_solicitacoes ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + paciente_nome text NOT NULL, + paciente_sobrenome text, + paciente_email text NOT NULL, + paciente_celular text, + paciente_cpf text, + tipo text NOT NULL, + modalidade text NOT NULL, + data_solicitada date NOT NULL, + hora_solicitada time without time zone NOT NULL, + reservado_ate timestamp with time zone, + motivo text, + como_conheceu text, + pix_status text DEFAULT 'pendente'::text, + pix_pago_em timestamp with time zone, + status text DEFAULT 'pendente'::text NOT NULL, + recusado_motivo text, + autorizado_em timestamp with time zone, + autorizado_por uuid, + user_id uuid, + patient_id uuid, + evento_id uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT agendador_sol_modalidade_check CHECK ((modalidade = ANY (ARRAY['presencial'::text, 'online'::text]))), + CONSTRAINT agendador_sol_pix_check CHECK (((pix_status IS NULL) OR (pix_status = ANY (ARRAY['pendente'::text, 'pago'::text, 'expirado'::text])))), + CONSTRAINT agendador_sol_status_check CHECK ((status = ANY (ARRAY['pendente'::text, 'autorizado'::text, 'recusado'::text, 'expirado'::text, 'convertido'::text]))), + CONSTRAINT agendador_sol_tipo_check CHECK ((tipo = ANY (ARRAY['primeira'::text, 'retorno'::text, 'reagendar'::text]))) +); + + +-- +-- Name: billing_contracts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.billing_contracts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + patient_id uuid NOT NULL, + type text NOT NULL, + total_sessions integer, + sessions_used integer DEFAULT 0, + package_price numeric(10,2), + amount numeric(10,2), + billing_interval text, + active_from timestamp with time zone DEFAULT now(), + active_to timestamp with time zone, + status text DEFAULT 'active'::text NOT NULL, + created_at timestamp with time zone DEFAULT now(), + CONSTRAINT billing_contracts_interval_chk CHECK (((billing_interval IS NULL) OR (billing_interval = ANY (ARRAY['monthly'::text, 'weekly'::text])))), + CONSTRAINT billing_contracts_sess_used_chk CHECK (((sessions_used IS NULL) OR (sessions_used >= 0))), + CONSTRAINT billing_contracts_status_chk CHECK ((status = ANY (ARRAY['active'::text, 'completed'::text, 'cancelled'::text]))), + CONSTRAINT billing_contracts_total_sess_chk CHECK (((total_sessions IS NULL) OR (total_sessions > 0))), + CONSTRAINT billing_contracts_type_chk CHECK ((type = ANY (ARRAY['per_session'::text, 'package'::text, 'subscription'::text]))) +); + + +-- +-- Name: commitment_services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.commitment_services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + commitment_id uuid NOT NULL, + service_id uuid NOT NULL, + quantity integer DEFAULT 1 NOT NULL, + unit_price numeric(10,2) NOT NULL, + discount_pct numeric(5,2) DEFAULT 0, + discount_flat numeric(10,2) DEFAULT 0, + final_price numeric(10,2) NOT NULL, + created_at timestamp with time zone DEFAULT now(), + CONSTRAINT commitment_services_disc_flat_chk CHECK ((discount_flat >= (0)::numeric)), + CONSTRAINT commitment_services_disc_pct_chk CHECK (((discount_pct >= (0)::numeric) AND (discount_pct <= (100)::numeric))), + CONSTRAINT commitment_services_final_price_chk CHECK ((final_price >= (0)::numeric)), + CONSTRAINT commitment_services_quantity_chk CHECK ((quantity > 0)) +); + + +-- +-- Name: commitment_time_logs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.commitment_time_logs ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + commitment_id uuid NOT NULL, + calendar_event_id uuid, + source public.commitment_log_source DEFAULT 'manual'::public.commitment_log_source NOT NULL, + started_at timestamp with time zone NOT NULL, + ended_at timestamp with time zone NOT NULL, + minutes integer NOT NULL, + created_by uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: company_profiles; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.company_profiles ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + nome_fantasia text, + razao_social text, + tipo_empresa text, + cnpj text, + ie text, + im text, + cep text, + logradouro text, + numero text, + complemento text, + bairro text, + cidade text, + estado text, + email text, + telefone text, + site text, + logo_url text, + redes_sociais jsonb DEFAULT '[]'::jsonb NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: current_tenant_id; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.current_tenant_id AS + SELECT current_setting('request.jwt.claim.tenant_id'::text, true) AS current_setting; + + +-- +-- Name: determined_commitment_fields; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.determined_commitment_fields ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + commitment_id uuid NOT NULL, + key text NOT NULL, + label text NOT NULL, + field_type public.determined_field_type DEFAULT 'text'::public.determined_field_type NOT NULL, + required boolean DEFAULT false NOT NULL, + sort_order integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: determined_commitments; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.determined_commitments ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + created_by uuid, + is_native boolean DEFAULT false NOT NULL, + native_key text, + is_locked boolean DEFAULT false NOT NULL, + active boolean DEFAULT true NOT NULL, + name text NOT NULL, + description text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + bg_color text, + text_color text +); + + +-- +-- Name: dev_user_credentials; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.dev_user_credentials ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid, + email text NOT NULL, + password_dev text NOT NULL, + kind text DEFAULT 'custom'::text NOT NULL, + note text, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: email_layout_config; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.email_layout_config ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + header_config jsonb DEFAULT '{"layout": null, "content": "", "enabled": false}'::jsonb NOT NULL, + footer_config jsonb DEFAULT '{"layout": null, "content": "", "enabled": false}'::jsonb NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: email_templates_global; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.email_templates_global ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + domain text NOT NULL, + channel text DEFAULT 'email'::text NOT NULL, + subject text NOT NULL, + body_html text NOT NULL, + body_text text, + version integer DEFAULT 1 NOT NULL, + is_active boolean DEFAULT true NOT NULL, + variables jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: email_templates_tenant; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.email_templates_tenant ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid, + template_key text NOT NULL, + subject text, + body_html text, + body_text text, + enabled boolean DEFAULT true NOT NULL, + synced_version integer, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: entitlements_invalidation; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.entitlements_invalidation ( + owner_id uuid NOT NULL, + changed_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.features ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + description text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + descricao text DEFAULT ''::text NOT NULL, + name text DEFAULT ''::text NOT NULL +); + + +-- +-- Name: COLUMN features.descricao; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.features.descricao IS 'Descri????o humana da feature (exibi????o no admin e documenta????o).'; + + +-- +-- Name: feriados; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.feriados ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid, + owner_id uuid, + tipo text DEFAULT 'municipal'::text NOT NULL, + nome text NOT NULL, + data date NOT NULL, + cidade text, + estado text, + observacao text, + bloqueia_sessoes boolean DEFAULT false NOT NULL, + criado_em timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT feriados_tipo_check CHECK ((tipo = ANY (ARRAY['municipal'::text, 'personalizado'::text]))) +); + + +-- +-- Name: financial_categories; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.financial_categories ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid NOT NULL, + name text NOT NULL, + type public.financial_record_type DEFAULT 'receita'::public.financial_record_type NOT NULL, + color text DEFAULT '#6366f1'::text, + icon text DEFAULT 'pi pi-tag'::text, + sort_order integer DEFAULT 0, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: financial_exceptions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.financial_exceptions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid, + tenant_id uuid NOT NULL, + exception_type text NOT NULL, + charge_mode text NOT NULL, + charge_value numeric(10,2), + charge_pct numeric(5,2), + min_hours_notice integer, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + CONSTRAINT financial_exceptions_charge_chk CHECK ((charge_mode = ANY (ARRAY['none'::text, 'full'::text, 'fixed_fee'::text, 'percentage'::text]))), + CONSTRAINT financial_exceptions_type_chk CHECK ((exception_type = ANY (ARRAY['patient_no_show'::text, 'patient_cancellation'::text, 'professional_cancellation'::text]))) +); + + +-- +-- Name: global_notices; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.global_notices ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + title text, + message text DEFAULT ''::text NOT NULL, + variant text DEFAULT 'info'::text NOT NULL, + roles text[] DEFAULT '{}'::text[] NOT NULL, + contexts text[] DEFAULT '{}'::text[] NOT NULL, + starts_at timestamp with time zone, + ends_at timestamp with time zone, + is_active boolean DEFAULT true NOT NULL, + priority integer DEFAULT 0 NOT NULL, + dismissible boolean DEFAULT true NOT NULL, + persist_dismiss boolean DEFAULT true NOT NULL, + dismiss_scope text DEFAULT 'device'::text NOT NULL, + show_once boolean DEFAULT false NOT NULL, + max_views integer, + cooldown_minutes integer, + version integer DEFAULT 1 NOT NULL, + action_type text DEFAULT 'none'::text NOT NULL, + action_label text, + action_url text, + action_route text, + views_count integer DEFAULT 0 NOT NULL, + clicks_count integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + created_by uuid, + content_align text DEFAULT 'left'::text NOT NULL, + link_target text DEFAULT '_blank'::text NOT NULL, + CONSTRAINT global_notices_action_type_check CHECK ((action_type = ANY (ARRAY['none'::text, 'internal'::text, 'external'::text]))), + CONSTRAINT global_notices_content_align_check CHECK ((content_align = ANY (ARRAY['left'::text, 'center'::text, 'right'::text, 'justify'::text]))), + CONSTRAINT global_notices_dismiss_scope_check CHECK ((dismiss_scope = ANY (ARRAY['session'::text, 'device'::text, 'user'::text]))), + CONSTRAINT global_notices_link_target_check CHECK ((link_target = ANY (ARRAY['_blank'::text, '_self'::text, '_parent'::text, '_top'::text]))), + CONSTRAINT global_notices_variant_check CHECK ((variant = ANY (ARRAY['info'::text, 'success'::text, 'warning'::text, 'error'::text]))) +); + + +-- +-- Name: insurance_plan_services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.insurance_plan_services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + insurance_plan_id uuid NOT NULL, + name text NOT NULL, + value numeric(10,2) NOT NULL, + active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: insurance_plans; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.insurance_plans ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + name text NOT NULL, + notes text, + default_value numeric(10,2), + active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: login_carousel_slides; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.login_carousel_slides ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + title text NOT NULL, + body text NOT NULL, + icon text DEFAULT 'pi-star'::text NOT NULL, + ordem integer DEFAULT 0 NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: medicos; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.medicos ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + nome text NOT NULL, + crm text, + especialidade text, + telefone_profissional text, + telefone_pessoal text, + email text, + clinica text, + cidade text, + estado text DEFAULT 'SP'::text, + observacoes text, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: TABLE medicos; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.medicos IS 'Médicos e profissionais de referência cadastrados pelo terapeuta.'; + + +-- +-- Name: COLUMN medicos.owner_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.owner_id IS 'Terapeuta dono do cadastro (auth.uid()).'; + + +-- +-- Name: COLUMN medicos.tenant_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.tenant_id IS 'Tenant do terapeuta.'; + + +-- +-- Name: COLUMN medicos.nome; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.nome IS 'Nome completo do médico/profissional.'; + + +-- +-- Name: COLUMN medicos.crm; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.crm IS 'CRM com UF. Ex: 123456/SP. Único por owner_id.'; + + +-- +-- Name: COLUMN medicos.especialidade; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.especialidade IS 'Especialidade médica. Ex: Psiquiatria, Neurologia.'; + + +-- +-- Name: COLUMN medicos.telefone_profissional; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.telefone_profissional IS 'Telefone do consultório ou clínica.'; + + +-- +-- Name: COLUMN medicos.telefone_pessoal; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.telefone_pessoal IS 'Telefone pessoal / WhatsApp. Campo sensível.'; + + +-- +-- Name: COLUMN medicos.email; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.email IS 'E-mail profissional.'; + + +-- +-- Name: COLUMN medicos.clinica; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.clinica IS 'Nome da clínica ou hospital onde atua.'; + + +-- +-- Name: COLUMN medicos.cidade; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.cidade IS 'Cidade de atuação.'; + + +-- +-- Name: COLUMN medicos.estado; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.estado IS 'UF de atuação. Default SP.'; + + +-- +-- Name: COLUMN medicos.observacoes; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.observacoes IS 'Notas internas do terapeuta sobre o médico.'; + + +-- +-- Name: COLUMN medicos.ativo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.ativo IS 'Soft delete: false oculta da listagem.'; + + +-- +-- Name: module_features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.module_features ( + module_id uuid NOT NULL, + feature_id uuid NOT NULL, + enabled boolean DEFAULT true NOT NULL, + limits jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: modules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.modules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + name text NOT NULL, + description text, + is_active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: notice_dismissals; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notice_dismissals ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + notice_id uuid NOT NULL, + user_id uuid NOT NULL, + version integer DEFAULT 1 NOT NULL, + dismissed_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: notification_channels; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_channels ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + channel text NOT NULL, + provider text NOT NULL, + is_active boolean DEFAULT false NOT NULL, + display_name text, + sender_address text, + credentials jsonb DEFAULT '{}'::jsonb NOT NULL, + connection_status text DEFAULT 'disconnected'::text, + last_health_check timestamp with time zone, + metadata jsonb DEFAULT '{}'::jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + twilio_subaccount_sid text, + twilio_phone_number text, + twilio_phone_sid text, + webhook_url text, + cost_per_message_usd numeric(8,6) DEFAULT 0, + price_per_message_brl numeric(8,4) DEFAULT 0, + provisioned_at timestamp with time zone, + CONSTRAINT notification_channels_channel_check CHECK ((channel = ANY (ARRAY['whatsapp'::text, 'email'::text, 'sms'::text]))), + CONSTRAINT notification_channels_connection_status_check CHECK ((connection_status = ANY (ARRAY['connected'::text, 'disconnected'::text, 'connecting'::text, 'qr_pending'::text, 'error'::text]))), + CONSTRAINT notification_channels_provider_check CHECK ((provider = ANY (ARRAY['evolution_api'::text, 'meta_official'::text, 'twilio'::text, 'zenvia'::text, 'sendgrid'::text, 'resend'::text, 'smtp'::text, 'zapi'::text]))) +); + + +-- +-- Name: COLUMN notification_channels.twilio_subaccount_sid; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.twilio_subaccount_sid IS 'SID da subconta Twilio criada para este tenant'; + + +-- +-- Name: COLUMN notification_channels.twilio_phone_number; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.twilio_phone_number IS 'Número WhatsApp provisionado (E.164, ex: +5511999990000)'; + + +-- +-- Name: COLUMN notification_channels.twilio_phone_sid; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.twilio_phone_sid IS 'SID do número de telefone na subconta Twilio'; + + +-- +-- Name: COLUMN notification_channels.webhook_url; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.webhook_url IS 'URL do webhook configurada na Twilio para receber callbacks de status'; + + +-- +-- Name: COLUMN notification_channels.cost_per_message_usd; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.cost_per_message_usd IS 'Custo real Twilio por mensagem WhatsApp (USD)'; + + +-- +-- Name: COLUMN notification_channels.price_per_message_brl; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.price_per_message_brl IS 'Valor cobrado do tenant por mensagem (BRL, inclui margem SaaS)'; + + +-- +-- Name: COLUMN notification_channels.provisioned_at; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.provisioned_at IS 'Timestamp do provisionamento da subconta'; + + +-- +-- Name: notification_logs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_logs ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + queue_id uuid, + agenda_evento_id uuid, + patient_id uuid NOT NULL, + channel text NOT NULL, + template_key text NOT NULL, + schedule_key text, + recipient_address text NOT NULL, + resolved_message text, + resolved_vars jsonb, + status text NOT NULL, + provider text, + provider_message_id text, + provider_status text, + provider_response jsonb, + sent_at timestamp with time zone, + delivered_at timestamp with time zone, + read_at timestamp with time zone, + failed_at timestamp with time zone, + failure_reason text, + estimated_cost_brl numeric(8,4) DEFAULT 0, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT notification_logs_status_check CHECK ((status = ANY (ARRAY['sent'::text, 'delivered'::text, 'read'::text, 'failed'::text, 'bounced'::text, 'opted_out'::text]))) +); + + +-- +-- Name: notification_preferences; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_preferences ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + patient_id uuid NOT NULL, + whatsapp_opt_in boolean DEFAULT true NOT NULL, + email_opt_in boolean DEFAULT true NOT NULL, + sms_opt_in boolean DEFAULT false NOT NULL, + preferred_time_start time without time zone DEFAULT '08:00:00'::time without time zone, + preferred_time_end time without time zone DEFAULT '20:00:00'::time without time zone, + lgpd_consent_given boolean DEFAULT false NOT NULL, + lgpd_consent_date timestamp with time zone, + lgpd_consent_version text, + lgpd_consent_ip inet, + lgpd_opt_out_date timestamp with time zone, + lgpd_opt_out_reason text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone +); + + +-- +-- Name: notification_queue; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_queue ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + agenda_evento_id uuid, + patient_id uuid NOT NULL, + channel text NOT NULL, + template_key text NOT NULL, + schedule_key text NOT NULL, + resolved_vars jsonb DEFAULT '{}'::jsonb NOT NULL, + recipient_address text NOT NULL, + status text DEFAULT 'pendente'::text NOT NULL, + scheduled_at timestamp with time zone NOT NULL, + sent_at timestamp with time zone, + next_retry_at timestamp with time zone, + attempts integer DEFAULT 0 NOT NULL, + max_attempts integer DEFAULT 5 NOT NULL, + last_error text, + idempotency_key text NOT NULL, + provider_message_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT notification_queue_channel_check CHECK ((channel = ANY (ARRAY['whatsapp'::text, 'email'::text, 'sms'::text]))), + CONSTRAINT notification_queue_status_check CHECK ((status = ANY (ARRAY['pendente'::text, 'processando'::text, 'enviado'::text, 'falhou'::text, 'cancelado'::text, 'ignorado'::text]))) +); + + +-- +-- Name: notification_schedules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_schedules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + schedule_key text NOT NULL, + event_type text NOT NULL, + trigger_type text NOT NULL, + offset_minutes integer DEFAULT 0, + whatsapp_enabled boolean DEFAULT true NOT NULL, + email_enabled boolean DEFAULT true NOT NULL, + sms_enabled boolean DEFAULT false NOT NULL, + allowed_time_start time without time zone DEFAULT '08:00:00'::time without time zone, + allowed_time_end time without time zone DEFAULT '20:00:00'::time without time zone, + skip_weekends boolean DEFAULT false, + skip_holidays boolean DEFAULT false, + is_active boolean DEFAULT true NOT NULL, + sort_order integer DEFAULT 0, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + CONSTRAINT notification_schedules_event_type_check CHECK ((event_type = ANY (ARRAY['lembrete_sessao'::text, 'confirmacao_sessao'::text, 'cancelamento_sessao'::text, 'reagendamento'::text, 'cobranca_pendente'::text, 'boas_vindas_paciente'::text]))), + CONSTRAINT notification_schedules_trigger_type_check CHECK ((trigger_type = ANY (ARRAY['before_event'::text, 'after_event'::text, 'immediate'::text]))) +); + + +-- +-- Name: notification_templates; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_templates ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid, + owner_id uuid, + key text NOT NULL, + domain text NOT NULL, + channel text NOT NULL, + event_type text NOT NULL, + body_text text NOT NULL, + meta_template_name text, + meta_template_namespace text, + meta_components jsonb, + meta_status text DEFAULT 'draft'::text, + variables jsonb DEFAULT '[]'::jsonb, + version integer DEFAULT 1 NOT NULL, + is_active boolean DEFAULT true NOT NULL, + is_default boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + CONSTRAINT notification_templates_channel_check CHECK ((channel = ANY (ARRAY['whatsapp'::text, 'sms'::text]))), + CONSTRAINT notification_templates_domain_check CHECK ((domain = ANY (ARRAY['session'::text, 'intake'::text, 'billing'::text, 'system'::text]))), + CONSTRAINT notification_templates_event_type_check CHECK ((event_type = ANY (ARRAY['lembrete_sessao'::text, 'confirmacao_sessao'::text, 'cancelamento_sessao'::text, 'reagendamento'::text, 'cobranca_pendente'::text, 'boas_vindas_paciente'::text, 'intake_recebido'::text, 'intake_aprovado'::text, 'intake_rejeitado'::text]))), + CONSTRAINT notification_templates_meta_status_check CHECK ((meta_status = ANY (ARRAY['draft'::text, 'pending_approval'::text, 'approved'::text, 'rejected'::text]))) +); + + +-- +-- Name: notifications; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notifications ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + type text NOT NULL, + ref_id uuid, + ref_table text, + payload jsonb DEFAULT '{}'::jsonb NOT NULL, + read_at timestamp with time zone, + archived boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT notifications_type_check CHECK ((type = ANY (ARRAY['new_scheduling'::text, 'new_patient'::text, 'recurrence_alert'::text, 'session_status'::text]))) +); + + +-- +-- Name: plan_features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_features ( + plan_id uuid NOT NULL, + feature_id uuid NOT NULL, + enabled boolean DEFAULT true NOT NULL, + limits jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_modules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_modules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + module_id uuid NOT NULL, + status text DEFAULT 'active'::text NOT NULL, + settings jsonb, + provider text DEFAULT 'manual'::text NOT NULL, + provider_item_id text, + installed_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: owner_feature_entitlements; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.owner_feature_entitlements AS + WITH base AS ( + SELECT s.user_id AS owner_id, + f.key AS feature_key, + pf.limits, + 'plan'::text AS source + FROM ((public.subscriptions s + JOIN public.plan_features pf ON (((pf.plan_id = s.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))) + WHERE ((s.status = 'active'::text) AND (s.user_id IS NOT NULL)) + UNION ALL + SELECT tm.owner_id, + f.key AS feature_key, + mf.limits, + 'module'::text AS source + FROM (((public.tenant_modules tm + JOIN public.modules m ON (((m.id = tm.module_id) AND (m.is_active = true)))) + JOIN public.module_features mf ON (((mf.module_id = m.id) AND (mf.enabled = true)))) + JOIN public.features f ON ((f.id = mf.feature_id))) + WHERE ((tm.status = 'active'::text) AND (tm.owner_id IS NOT NULL)) + ) + SELECT owner_id, + feature_key, + array_agg(DISTINCT source) AS sources, + jsonb_agg(limits) FILTER (WHERE (limits IS NOT NULL)) AS limits_list + FROM base + GROUP BY owner_id, feature_key; + + +-- +-- Name: owner_users; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.owner_users ( + owner_id uuid NOT NULL, + user_id uuid NOT NULL, + role text DEFAULT 'admin'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: patient_contacts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_contacts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + patient_id uuid NOT NULL, + tenant_id uuid NOT NULL, + nome text NOT NULL, + tipo text NOT NULL, + relacao text, + telefone text, + email text, + cpf text, + especialidade text, + registro_profissional text, + is_primario boolean DEFAULT false NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT patient_contacts_tipo_check CHECK ((tipo = ANY (ARRAY['emergencia'::text, 'responsavel_legal'::text, 'profissional_saude'::text, 'outro'::text]))) +); + + +-- +-- Name: TABLE patient_contacts; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.patient_contacts IS 'Contatos vinculados ao paciente: emergência, responsável legal, outros profissionais de saúde'; + + +-- +-- Name: COLUMN patient_contacts.tipo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_contacts.tipo IS 'Categoria do contato: emergencia | responsavel_legal | profissional_saude | outro'; + + +-- +-- Name: COLUMN patient_contacts.is_primario; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_contacts.is_primario IS 'Contato de emergência principal — exibido em destaque no cadastro'; + + +-- +-- Name: patient_discounts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_discounts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + patient_id uuid NOT NULL, + discount_pct numeric(5,2) DEFAULT 0, + discount_flat numeric(10,2) DEFAULT 0, + reason text, + active boolean DEFAULT true NOT NULL, + active_from timestamp with time zone DEFAULT now(), + active_to timestamp with time zone, + created_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: patient_group_patient; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_group_patient ( + patient_group_id uuid NOT NULL, + patient_id uuid NOT NULL, + created_at timestamp with time zone DEFAULT now(), + tenant_id uuid NOT NULL +); + + +-- +-- Name: patient_groups; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_groups ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + nome text NOT NULL, + descricao text, + cor text, + is_active boolean DEFAULT true NOT NULL, + is_system boolean DEFAULT false NOT NULL, + owner_id uuid DEFAULT auth.uid() NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + therapist_id uuid, + tenant_id uuid NOT NULL +); + + +-- +-- Name: patient_intake_requests; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_intake_requests ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + token text NOT NULL, + consent boolean DEFAULT false NOT NULL, + status text DEFAULT 'new'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + converted_patient_id uuid, + rejected_reason text, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + cpf text, + rg text, + cep text, + nome_completo text, + email_principal text, + telefone text, + pais text, + cidade text, + estado text, + endereco text, + numero text, + bairro text, + complemento text, + data_nascimento date, + naturalidade text, + genero text, + estado_civil text, + onde_nos_conheceu text, + encaminhado_por text, + observacoes text, + notas_internas text, + email_alternativo text, + telefone_alternativo text, + profissao text, + escolaridade text, + nacionalidade text, + avatar_url text, + tenant_id uuid, + CONSTRAINT chk_intakes_status CHECK ((status = ANY (ARRAY['new'::text, 'converted'::text, 'rejected'::text]))) +); + + +-- +-- Name: patient_invites; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_invites ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + token text NOT NULL, + active boolean DEFAULT true NOT NULL, + expires_at timestamp with time zone, + max_uses integer, + uses integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid +); + + +-- +-- Name: patient_patient_tag; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_patient_tag ( + owner_id uuid NOT NULL, + patient_id uuid NOT NULL, + tag_id uuid NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL +); + + +-- +-- Name: patient_status_history; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_status_history ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + patient_id uuid NOT NULL, + tenant_id uuid NOT NULL, + status_anterior text, + status_novo text NOT NULL, + motivo text, + encaminhado_para text, + data_saida date, + alterado_por uuid, + alterado_em timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT psh_status_novo_check CHECK ((status_novo = ANY (ARRAY['Ativo'::text, 'Inativo'::text, 'Alta'::text, 'Encaminhado'::text, 'Arquivado'::text]))) +); + + +-- +-- Name: TABLE patient_status_history; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.patient_status_history IS 'Histórico imutável de todas as mudanças de status do paciente — não editar, apenas inserir'; + + +-- +-- Name: patient_support_contacts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_support_contacts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + patient_id uuid NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + nome text, + relacao text, + tipo text, + telefone text, + email text, + is_primario boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: TABLE patient_support_contacts; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.patient_support_contacts IS 'Rede de suporte do paciente. Exibida no card "Contatos & rede de suporte" do perfil.'; + + +-- +-- Name: COLUMN patient_support_contacts.tipo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_support_contacts.tipo IS 'emergencia | familiar | profissional_saude | amigo | outro'; + + +-- +-- Name: COLUMN patient_support_contacts.is_primario; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_support_contacts.is_primario IS 'true = badge vermelho "emergência" no perfil do paciente.'; + + +-- +-- Name: patient_tags; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_tags ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + nome text NOT NULL, + cor text, + is_padrao boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone, + tenant_id uuid NOT NULL +); + + +-- +-- Name: patient_timeline; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_timeline ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + patient_id uuid NOT NULL, + tenant_id uuid NOT NULL, + evento_tipo text NOT NULL, + titulo text NOT NULL, + descricao text, + icone_cor text DEFAULT 'gray'::text, + link_ref_tipo text, + link_ref_id uuid, + gerado_por uuid, + ocorrido_em timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT pt_evento_tipo_check CHECK ((evento_tipo = ANY (ARRAY['primeira_sessao'::text, 'sessao_realizada'::text, 'sessao_cancelada'::text, 'falta'::text, 'status_alterado'::text, 'risco_sinalizado'::text, 'risco_removido'::text, 'documento_assinado'::text, 'documento_adicionado'::text, 'escala_respondida'::text, 'escala_enviada'::text, 'pagamento_vencido'::text, 'pagamento_recebido'::text, 'tarefa_combinada'::text, 'contato_adicionado'::text, 'prontuario_editado'::text, 'nota_adicionada'::text, 'manual'::text]))), + CONSTRAINT pt_icone_cor_check CHECK ((icone_cor = ANY (ARRAY['green'::text, 'blue'::text, 'amber'::text, 'red'::text, 'gray'::text, 'purple'::text]))) +); + + +-- +-- Name: TABLE patient_timeline; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.patient_timeline IS 'Feed cronológico de eventos do paciente — alimentado por triggers e inserções manuais'; + + +-- +-- Name: COLUMN patient_timeline.link_ref_tipo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_timeline.link_ref_tipo IS 'Tipo da entidade referenciada (polimórfico): agenda_evento | financial_record | documento | escala'; + + +-- +-- Name: COLUMN patient_timeline.link_ref_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_timeline.link_ref_id IS 'ID da entidade referenciada — sem FK formal para suportar múltiplos tipos'; + + +-- +-- Name: patients; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patients ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + nome_completo text NOT NULL, + email_principal text, + telefone text, + created_at timestamp with time zone DEFAULT now(), + owner_id uuid, + avatar_url text, + status text DEFAULT 'Ativo'::text, + last_attended_at timestamp with time zone, + is_native boolean DEFAULT false, + naturalidade text, + data_nascimento date, + rg text, + cpf text, + identification_color text, + genero text, + estado_civil text, + email_alternativo text, + pais text DEFAULT 'Brasil'::text, + cep text, + cidade text, + estado text, + endereco text, + numero text, + bairro text, + complemento text, + escolaridade text, + profissao text, + nome_parente text, + grau_parentesco text, + telefone_alternativo text, + onde_nos_conheceu text, + encaminhado_por text, + nome_responsavel text, + telefone_responsavel text, + cpf_responsavel text, + observacao_responsavel text, + cobranca_no_responsavel boolean DEFAULT false, + observacoes text, + notas_internas text, + updated_at timestamp with time zone DEFAULT now(), + telefone_parente text, + tenant_id uuid NOT NULL, + responsible_member_id uuid NOT NULL, + user_id uuid, + patient_scope text DEFAULT 'clinic'::text NOT NULL, + therapist_member_id uuid, + nome_social text, + pronomes text, + etnia text, + religiao text, + faixa_renda text, + canal_preferido text DEFAULT 'whatsapp'::text, + horario_contato_inicio time without time zone DEFAULT '08:00:00'::time without time zone, + horario_contato_fim time without time zone DEFAULT '20:00:00'::time without time zone, + idioma text DEFAULT 'pt-BR'::text, + origem text, + metodo_pagamento_preferido text, + motivo_saida text, + data_saida date, + encaminhado_para text, + risco_elevado boolean DEFAULT false NOT NULL, + risco_nota text, + risco_sinalizado_em timestamp with time zone, + risco_sinalizado_por uuid, + horario_contato text, + convenio text, + convenio_id uuid, + CONSTRAINT cpf_responsavel_format_check CHECK (((cpf_responsavel IS NULL) OR (cpf_responsavel ~ '^\d{11}$'::text))), + CONSTRAINT patients_cpf_format_check CHECK (((cpf IS NULL) OR (cpf ~ '^\d{11}$'::text))), + CONSTRAINT patients_faixa_renda_check CHECK (((faixa_renda IS NULL) OR (faixa_renda = ANY (ARRAY['ate_1sm'::text, '1_3sm'::text, '3_6sm'::text, '6_10sm'::text, 'acima_10sm'::text, 'nao_informado'::text])))), + CONSTRAINT patients_metodo_pagamento_check CHECK (((metodo_pagamento_preferido IS NULL) OR (metodo_pagamento_preferido = ANY (ARRAY['pix'::text, 'cartao'::text, 'dinheiro'::text, 'deposito'::text, 'convenio'::text])))), + CONSTRAINT patients_risco_consistency_check CHECK (((risco_elevado = false) OR ((risco_elevado = true) AND (risco_nota IS NOT NULL) AND (risco_sinalizado_por IS NOT NULL)))), + CONSTRAINT patients_status_check CHECK ((status = ANY (ARRAY['Ativo'::text, 'Em espera'::text, 'Inativo'::text, 'Alta'::text, 'Encaminhado'::text, 'Arquivado'::text]))) +); + + +-- +-- Name: COLUMN patients.avatar_url; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.avatar_url IS 'URL p??blica da imagem de avatar armazenada no Supabase Storage'; + + +-- +-- Name: COLUMN patients.nome_social; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.nome_social IS 'Nome social / como prefere ser chamado(a) no atendimento.'; + + +-- +-- Name: COLUMN patients.pronomes; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.pronomes IS 'Pronomes de tratamento. Ex: ela/dela, ele/dele. Exibido no header do perfil.'; + + +-- +-- Name: COLUMN patients.etnia; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.etnia IS 'Etnia / raça autodeclarada. Exibida no card "Dados pessoais".'; + + +-- +-- Name: COLUMN patients.religiao; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.religiao IS 'Religião ou espiritualidade (opcional, relevante clinicamente)'; + + +-- +-- Name: COLUMN patients.faixa_renda; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.faixa_renda IS 'Faixa de renda em salários mínimos — usado para precificação solidária'; + + +-- +-- Name: COLUMN patients.canal_preferido; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.canal_preferido IS 'Canal preferido de contato. Ex: WhatsApp, Telefone, E-mail.'; + + +-- +-- Name: COLUMN patients.horario_contato_inicio; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.horario_contato_inicio IS 'Início da janela de horário preferida para contato'; + + +-- +-- Name: COLUMN patients.horario_contato_fim; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.horario_contato_fim IS 'Fim da janela de horário preferida para contato'; + + +-- +-- Name: COLUMN patients.origem; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.origem IS 'Como o paciente chegou: indicacao, agendador, redes_sociais, encaminhamento, outro'; + + +-- +-- Name: COLUMN patients.metodo_pagamento_preferido; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.metodo_pagamento_preferido IS 'Método de pagamento preferido. Ex: PIX, Cartão crédito. Exibido no card Origem.'; + + +-- +-- Name: COLUMN patients.motivo_saida; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.motivo_saida IS 'Motivo de encerramento do acompanhamento. Exibido no card Origem quando preenchido.'; + + +-- +-- Name: COLUMN patients.data_saida; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.data_saida IS 'Data em que o paciente foi desligado/encaminhado'; + + +-- +-- Name: COLUMN patients.encaminhado_para; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.encaminhado_para IS 'Nome ou serviço para onde o paciente foi encaminhado'; + + +-- +-- Name: COLUMN patients.risco_elevado; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.risco_elevado IS 'Flag de atenção clínica — exibe alerta no topo do cadastro e prontuário'; + + +-- +-- Name: COLUMN patients.risco_nota; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.risco_nota IS 'Descrição do risco (obrigatória quando risco_elevado = true)'; + + +-- +-- Name: COLUMN patients.risco_sinalizado_em; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.risco_sinalizado_em IS 'Timestamp em que o risco foi sinalizado'; + + +-- +-- Name: COLUMN patients.risco_sinalizado_por; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.risco_sinalizado_por IS 'Usuário que sinalizou o risco'; + + +-- +-- Name: COLUMN patients.horario_contato; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.horario_contato IS 'Horário preferido para contato. Ex: 08h–18h.'; + + +-- +-- Name: COLUMN patients.convenio; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.convenio IS 'Nome do convênio para exibição (badge azul no header). Derivado de convenio_id.'; + + +-- +-- Name: COLUMN patients.convenio_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.convenio_id IS 'FK para insurance_plans.id. Vincula o paciente ao convênio cadastrado.'; + + +-- +-- Name: payment_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.payment_settings ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + pix_ativo boolean DEFAULT false NOT NULL, + pix_tipo text DEFAULT 'cpf'::text NOT NULL, + pix_chave text DEFAULT ''::text NOT NULL, + pix_nome_titular text DEFAULT ''::text NOT NULL, + deposito_ativo boolean DEFAULT false NOT NULL, + deposito_banco text DEFAULT ''::text NOT NULL, + deposito_agencia text DEFAULT ''::text NOT NULL, + deposito_conta text DEFAULT ''::text NOT NULL, + deposito_tipo_conta text DEFAULT 'corrente'::text NOT NULL, + deposito_titular text DEFAULT ''::text NOT NULL, + deposito_cpf_cnpj text DEFAULT ''::text NOT NULL, + dinheiro_ativo boolean DEFAULT false NOT NULL, + cartao_ativo boolean DEFAULT false NOT NULL, + cartao_instrucao text DEFAULT ''::text NOT NULL, + convenio_ativo boolean DEFAULT false NOT NULL, + convenio_lista text DEFAULT ''::text NOT NULL, + observacoes_pagamento text DEFAULT ''::text NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: plan_prices; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_prices ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + plan_id uuid NOT NULL, + currency text DEFAULT 'BRL'::text NOT NULL, + "interval" text NOT NULL, + amount_cents integer NOT NULL, + is_active boolean DEFAULT true NOT NULL, + active_from timestamp with time zone DEFAULT now() NOT NULL, + active_to timestamp with time zone, + source text DEFAULT 'manual'::text NOT NULL, + provider text, + provider_price_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT plan_prices_amount_cents_check CHECK ((amount_cents >= 0)), + CONSTRAINT plan_prices_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text]))) +); + + +-- +-- Name: TABLE plan_prices; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.plan_prices IS 'Hist??rico de pre??os por plano (fonte: manual/gateway).'; + + +-- +-- Name: plan_public; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_public ( + plan_id uuid NOT NULL, + public_name text DEFAULT ''::text NOT NULL, + public_description text DEFAULT ''::text NOT NULL, + badge text, + is_featured boolean DEFAULT false NOT NULL, + is_visible boolean DEFAULT true NOT NULL, + sort_order integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: TABLE plan_public; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.plan_public IS 'Configura????o de vitrine (p??gina p??blica) dos planos.'; + + +-- +-- Name: plan_public_bullets; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_public_bullets ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + plan_id uuid NOT NULL, + text text NOT NULL, + sort_order integer DEFAULT 0 NOT NULL, + highlight boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: plans; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plans ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + name text NOT NULL, + description text, + is_active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + price_cents integer DEFAULT 0 NOT NULL, + currency text DEFAULT 'BRL'::text NOT NULL, + billing_interval text DEFAULT 'month'::text NOT NULL, + target text, + max_supervisees integer, + CONSTRAINT plans_target_check CHECK ((target = ANY (ARRAY['patient'::text, 'therapist'::text, 'clinic'::text, 'supervisor'::text]))) +); + + +-- +-- Name: COLUMN plans.name; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.plans.name IS 'Nome interno do plano (admin). A key ?? t??cnica/imut??vel.'; + + +-- +-- Name: COLUMN plans.target; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.plans.target IS 'P??blico-alvo do plano: patient, therapist ou clinic.'; + + +-- +-- Name: COLUMN plans.max_supervisees; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.plans.max_supervisees IS 'Limite de terapeutas que podem ser supervisionados. Apenas para planos target=supervisor. NULL = sem limite.'; + + +-- +-- Name: professional_pricing; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.professional_pricing ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + determined_commitment_id uuid, + price numeric(10,2) NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: TABLE professional_pricing; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.professional_pricing IS 'DEPRECATED: substitu??da por public.services. Manter at?? pr??xima release de limpeza.'; + + +-- +-- Name: profiles; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.profiles ( + id uuid NOT NULL, + role text DEFAULT 'tenant_member'::text NOT NULL, + full_name text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + avatar_url text, + phone text, + bio text, + language text DEFAULT 'pt-BR'::text NOT NULL, + timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL, + notify_system_email boolean DEFAULT true NOT NULL, + notify_reminders boolean DEFAULT true NOT NULL, + notify_news boolean DEFAULT false NOT NULL, + account_type text DEFAULT 'free'::text NOT NULL, + platform_roles text[] DEFAULT '{}'::text[] NOT NULL, + nickname text, + work_description text, + work_description_other text, + site_url text, + social_instagram text, + social_youtube text, + social_facebook text, + social_x text, + social_custom jsonb DEFAULT '[]'::jsonb NOT NULL, + CONSTRAINT profiles_account_type_check CHECK ((account_type = ANY (ARRAY['free'::text, 'patient'::text, 'therapist'::text, 'clinic'::text]))), + CONSTRAINT profiles_role_check CHECK ((role = ANY (ARRAY['saas_admin'::text, 'tenant_member'::text, 'portal_user'::text, 'patient'::text]))) +); + + +-- +-- Name: COLUMN profiles.phone; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.phone IS 'WhatsApp / telefone no formato (99) 99999-9999'; + + +-- +-- Name: COLUMN profiles.bio; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.bio IS 'Breve apresenta????o p??blica (m??x 300 chars no front)'; + + +-- +-- Name: COLUMN profiles.account_type; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.account_type IS 'Tipo de conta: free=sem perfil ainda, patient=paciente (imut??vel), therapist=terapeuta (imut??vel), clinic=cl??nica (imut??vel).'; + + +-- +-- Name: COLUMN profiles.platform_roles; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.platform_roles IS 'Papéis globais de plataforma, independentes de tenant. Ex: editor de microlearning. Atribuído pelo saas_admin.'; + + +-- +-- Name: COLUMN profiles.nickname; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.nickname IS 'Apelido preferido para comunica????o interna (Ag??ncia PSI)'; + + +-- +-- Name: COLUMN profiles.work_description; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.work_description IS 'Categoria de trabalho selecionada no perfil p??blico'; + + +-- +-- Name: COLUMN profiles.work_description_other; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.work_description_other IS 'Descri????o livre quando work_description = ''outro'''; + + +-- +-- Name: COLUMN profiles.site_url; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.site_url IS 'Endere??o do site pessoal ou profissional'; + + +-- +-- Name: COLUMN profiles.social_instagram; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_instagram IS 'Handle ou URL do Instagram'; + + +-- +-- Name: COLUMN profiles.social_youtube; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_youtube IS 'Handle ou URL do canal no YouTube'; + + +-- +-- Name: COLUMN profiles.social_facebook; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_facebook IS 'Handle ou URL da p??gina no Facebook'; + + +-- +-- Name: COLUMN profiles.social_x; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_x IS 'Handle ou URL do perfil no X (Twitter)'; + + +-- +-- Name: COLUMN profiles.social_custom; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_custom IS 'Array JSON com redes adicionais livres: [{name, url}]'; + + +-- +-- Name: recurrence_exceptions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.recurrence_exceptions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + recurrence_id uuid NOT NULL, + tenant_id uuid NOT NULL, + original_date date NOT NULL, + type public.recurrence_exception_type NOT NULL, + new_date date, + new_start_time time without time zone, + new_end_time time without time zone, + modalidade text, + observacoes text, + titulo_custom text, + extra_fields jsonb, + reason text, + agenda_evento_id uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: recurrence_rule_services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.recurrence_rule_services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + rule_id uuid NOT NULL, + service_id uuid NOT NULL, + quantity integer DEFAULT 1 NOT NULL, + unit_price numeric(10,2) NOT NULL, + discount_pct numeric(5,2) DEFAULT 0, + discount_flat numeric(10,2) DEFAULT 0, + final_price numeric(10,2) NOT NULL, + created_at timestamp with time zone DEFAULT now(), + CONSTRAINT recurrence_rule_services_disc_flat_chk CHECK ((discount_flat >= (0)::numeric)), + CONSTRAINT recurrence_rule_services_disc_pct_chk CHECK (((discount_pct >= (0)::numeric) AND (discount_pct <= (100)::numeric))), + CONSTRAINT recurrence_rule_services_final_price_chk CHECK ((final_price >= (0)::numeric)), + CONSTRAINT recurrence_rule_services_quantity_chk CHECK ((quantity > 0)) +); + + +-- +-- Name: recurrence_rules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.recurrence_rules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + therapist_id uuid, + patient_id uuid, + determined_commitment_id uuid, + type public.recurrence_type DEFAULT 'weekly'::public.recurrence_type NOT NULL, + "interval" smallint DEFAULT 1 NOT NULL, + weekdays smallint[] DEFAULT '{}'::smallint[] NOT NULL, + start_time time without time zone NOT NULL, + end_time time without time zone NOT NULL, + timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL, + duration_min smallint DEFAULT 50 NOT NULL, + start_date date NOT NULL, + end_date date, + max_occurrences integer, + open_ended boolean DEFAULT true NOT NULL, + modalidade text DEFAULT 'presencial'::text, + titulo_custom text, + observacoes text, + extra_fields jsonb, + status text DEFAULT 'ativo'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + price numeric(10,2), + insurance_plan_id uuid, + insurance_guide_number text, + insurance_value numeric(10,2), + insurance_plan_service_id uuid, + CONSTRAINT recurrence_rules_dates_chk CHECK (((end_date IS NULL) OR (end_date >= start_date))), + CONSTRAINT recurrence_rules_interval_chk CHECK (("interval" >= 1)), + CONSTRAINT recurrence_rules_status_check CHECK ((status = ANY (ARRAY['ativo'::text, 'pausado'::text, 'cancelado'::text]))), + CONSTRAINT recurrence_rules_times_chk CHECK ((end_time > start_time)) +); + + +-- +-- Name: saas_admins; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_admins ( + user_id uuid NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: saas_doc_votos; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_doc_votos ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + doc_id uuid NOT NULL, + user_id uuid NOT NULL, + util boolean NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: saas_docs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_docs ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + titulo text NOT NULL, + conteudo text DEFAULT ''::text NOT NULL, + medias jsonb DEFAULT '[]'::jsonb NOT NULL, + tipo_acesso text DEFAULT 'usuario'::text NOT NULL, + pagina_path text NOT NULL, + docs_relacionados uuid[] DEFAULT '{}'::uuid[] NOT NULL, + ativo boolean DEFAULT true NOT NULL, + ordem integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + categoria text, + exibir_no_faq boolean DEFAULT false NOT NULL, + votos_util integer DEFAULT 0 NOT NULL, + votos_nao_util integer DEFAULT 0 NOT NULL, + CONSTRAINT saas_docs_tipo_acesso_check CHECK ((tipo_acesso = ANY (ARRAY['admin'::text, 'usuario'::text]))) +); + + +-- +-- Name: COLUMN saas_docs.categoria; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.saas_docs.categoria IS 'Agrupa docs no portal FAQ (ex: Conta, Agenda, Pagamentos)'; + + +-- +-- Name: COLUMN saas_docs.exibir_no_faq; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.saas_docs.exibir_no_faq IS 'Se true, a doc e seus itens FAQ aparecem no portal de FAQ'; + + +-- +-- Name: saas_faq; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_faq ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + pergunta text NOT NULL, + categoria text, + publico boolean DEFAULT false NOT NULL, + votos integer DEFAULT 0 NOT NULL, + titulo text, + conteudo text, + tipo_acesso text DEFAULT 'usuario'::text NOT NULL, + pagina_path text NOT NULL, + pagina_label text, + medias jsonb DEFAULT '[]'::jsonb NOT NULL, + faqs_relacionados uuid[] DEFAULT '{}'::uuid[] NOT NULL, + ativo boolean DEFAULT true NOT NULL, + ordem integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: saas_faq_itens; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_faq_itens ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + doc_id uuid NOT NULL, + pergunta text NOT NULL, + resposta text, + ordem integer DEFAULT 0 NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: TABLE saas_faq_itens; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.saas_faq_itens IS 'Pares pergunta/resposta vinculados a um documento de ajuda'; + + +-- +-- Name: services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + name text NOT NULL, + description text, + price numeric(10,2) NOT NULL, + duration_min integer, + active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: subscription_events; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_events ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + subscription_id uuid NOT NULL, + owner_id uuid NOT NULL, + event_type text NOT NULL, + old_plan_id uuid, + new_plan_id uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL, + created_by uuid, + source text DEFAULT 'admin_ui'::text, + reason text, + metadata jsonb, + owner_type text NOT NULL, + owner_ref uuid NOT NULL, + CONSTRAINT subscription_events_owner_ref_consistency_chk CHECK ((owner_id = owner_ref)), + CONSTRAINT subscription_events_owner_type_chk CHECK (((owner_type IS NULL) OR (owner_type = ANY (ARRAY['clinic'::text, 'therapist'::text])))) +); + + +-- +-- Name: subscription_intents_personal; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_intents_personal ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid NOT NULL, + created_by_user_id uuid, + email text NOT NULL, + plan_id uuid NOT NULL, + plan_key text, + "interval" text, + amount_cents integer, + currency text, + status text DEFAULT 'new'::text NOT NULL, + source text DEFAULT 'manual'::text NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + paid_at timestamp with time zone, + subscription_id uuid, + CONSTRAINT sint_personal_interval_check CHECK ((("interval" IS NULL) OR ("interval" = ANY (ARRAY['month'::text, 'year'::text])))), + CONSTRAINT sint_personal_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text]))) +); + + +-- +-- Name: subscription_intents_tenant; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_intents_tenant ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid NOT NULL, + created_by_user_id uuid, + email text NOT NULL, + plan_id uuid NOT NULL, + plan_key text, + "interval" text, + amount_cents integer, + currency text, + status text DEFAULT 'new'::text NOT NULL, + source text DEFAULT 'manual'::text NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + paid_at timestamp with time zone, + tenant_id uuid NOT NULL, + subscription_id uuid, + CONSTRAINT sint_tenant_interval_check CHECK ((("interval" IS NULL) OR ("interval" = ANY (ARRAY['month'::text, 'year'::text])))), + CONSTRAINT sint_tenant_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text]))) +); + + +-- +-- Name: subscription_intents; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.subscription_intents AS + SELECT t.id, + t.user_id, + t.created_by_user_id, + t.email, + t.plan_id, + t.plan_key, + t."interval", + t.amount_cents, + t.currency, + t.status, + t.source, + t.notes, + t.created_at, + t.paid_at, + t.tenant_id, + t.subscription_id, + 'clinic'::text AS plan_target + FROM public.subscription_intents_tenant t +UNION ALL + SELECT p.id, + p.user_id, + p.created_by_user_id, + p.email, + p.plan_id, + p.plan_key, + p."interval", + p.amount_cents, + p.currency, + p.status, + p.source, + p.notes, + p.created_at, + p.paid_at, + NULL::uuid AS tenant_id, + p.subscription_id, + 'therapist'::text AS plan_target + FROM public.subscription_intents_personal p; + + +-- +-- Name: subscription_intents_legacy; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_intents_legacy ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid, + email text, + plan_key text NOT NULL, + "interval" text NOT NULL, + amount_cents integer NOT NULL, + currency text DEFAULT 'BRL'::text NOT NULL, + status text DEFAULT 'new'::text NOT NULL, + source text DEFAULT 'landing'::text NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + paid_at timestamp with time zone, + tenant_id uuid NOT NULL, + created_by_user_id uuid, + CONSTRAINT subscription_intents_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text]))), + CONSTRAINT subscription_intents_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text]))) +); + + +-- +-- Name: support_sessions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.support_sessions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + admin_id uuid NOT NULL, + token text DEFAULT encode(extensions.gen_random_bytes(32), 'hex'::text) NOT NULL, + expires_at timestamp with time zone DEFAULT (now() + '01:00:00'::interval) NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_feature_exceptions_log; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_feature_exceptions_log ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + feature_key text NOT NULL, + enabled boolean NOT NULL, + reason text, + created_by uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_features ( + tenant_id uuid NOT NULL, + feature_key text NOT NULL, + enabled boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_invites; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_invites ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + email text NOT NULL, + role text NOT NULL, + token uuid DEFAULT gen_random_uuid() NOT NULL, + invited_by uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL, + expires_at timestamp with time zone DEFAULT (now() + '7 days'::interval) NOT NULL, + accepted_at timestamp with time zone, + accepted_by uuid, + revoked_at timestamp with time zone, + revoked_by uuid, + CONSTRAINT tenant_invites_role_check CHECK ((role = ANY (ARRAY['therapist'::text, 'secretary'::text]))) +); + + +-- +-- Name: tenants; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenants ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + name text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + kind text DEFAULT 'saas'::text NOT NULL, + CONSTRAINT tenants_kind_check CHECK ((kind = ANY (ARRAY['therapist'::text, 'clinic_coworking'::text, 'clinic_reception'::text, 'clinic_full'::text, 'clinic'::text, 'saas'::text, 'supervisor'::text]))) +); + + +-- +-- Name: COLUMN tenants.kind; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.tenants.kind IS 'Tipo do tenant. Imut??vel ap??s cria????o. therapist=terapeuta solo. clinic_coworking/clinic_reception/clinic_full=cl??nicas. clinic e saas s??o legados.'; + + +-- +-- Name: therapist_payout_records; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.therapist_payout_records ( + payout_id uuid NOT NULL, + financial_record_id uuid NOT NULL +); + + +-- +-- Name: twilio_subaccount_usage; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.twilio_subaccount_usage ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + channel_id uuid NOT NULL, + twilio_subaccount_sid text NOT NULL, + period_start date NOT NULL, + period_end date NOT NULL, + messages_sent integer DEFAULT 0 NOT NULL, + messages_delivered integer DEFAULT 0 NOT NULL, + messages_failed integer DEFAULT 0 NOT NULL, + cost_usd numeric(12,6) DEFAULT 0 NOT NULL, + cost_brl numeric(12,4) DEFAULT 0 NOT NULL, + revenue_brl numeric(12,4) DEFAULT 0 NOT NULL, + margin_brl numeric(12,4) GENERATED ALWAYS AS ((revenue_brl - cost_brl)) STORED, + usd_brl_rate numeric(8,4) DEFAULT 0, + synced_at timestamp with time zone DEFAULT now(), + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT twilio_subaccount_usage_period_check CHECK ((period_end >= period_start)) +); + + +-- +-- Name: TABLE twilio_subaccount_usage; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.twilio_subaccount_usage IS 'Consumo mensal de mensagens WhatsApp por subconta Twilio. Sincronizado via Edge Function.'; + + +-- +-- Name: user_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.user_settings ( + user_id uuid NOT NULL, + theme_mode text DEFAULT 'dark'::text NOT NULL, + preset text DEFAULT 'Aura'::text NOT NULL, + primary_color text DEFAULT 'noir'::text NOT NULL, + surface_color text DEFAULT 'slate'::text NOT NULL, + menu_mode text DEFAULT 'static'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + layout_variant text DEFAULT 'classic'::text NOT NULL, + CONSTRAINT user_settings_layout_variant_check CHECK ((layout_variant = ANY (ARRAY['classic'::text, 'rail'::text]))), + CONSTRAINT user_settings_menu_mode_check CHECK ((menu_mode = ANY (ARRAY['static'::text, 'overlay'::text]))), + CONSTRAINT user_settings_theme_mode_check CHECK ((theme_mode = ANY (ARRAY['light'::text, 'dark'::text]))) +); + + +-- +-- Name: TABLE user_settings; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.user_settings IS 'Prefer??ncias de apar??ncia e layout por usu??rio'; + + +-- +-- Name: COLUMN user_settings.user_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.user_id IS 'FK = auth.users.id ??? um registro por usu??rio'; + + +-- +-- Name: COLUMN user_settings.theme_mode; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.theme_mode IS 'light | dark'; + + +-- +-- Name: COLUMN user_settings.preset; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.preset IS 'Preset PrimeVue: Aura | Lara | Nora'; + + +-- +-- Name: COLUMN user_settings.primary_color; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.primary_color IS 'Nome da cor prim??ria (ex: blue, emerald, noir)'; + + +-- +-- Name: COLUMN user_settings.surface_color; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.surface_color IS 'Nome da surface (ex: slate, zinc, neutral)'; + + +-- +-- Name: COLUMN user_settings.menu_mode; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.menu_mode IS 'static | overlay'; + + +-- +-- Name: COLUMN user_settings.layout_variant; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.layout_variant IS 'classic (sidebar) | rail (mini rail + painel)'; + + +-- +-- Name: v_auth_users_public; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_auth_users_public AS + SELECT id AS user_id, + email, + created_at, + last_sign_in_at + FROM auth.users u; + + +-- +-- Name: v_cashflow_projection; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_cashflow_projection WITH (security_invoker='on') AS + SELECT gs.mes, + to_char(gs.mes, 'YYYY-MM'::text) AS mes_label, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric) AS receitas_projetadas, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric) AS despesas_projetadas, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = 'pending'::text))), (0)::numeric) AS receitas_pendentes, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = 'overdue'::text))), (0)::numeric) AS receitas_vencidas, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = 'pending'::text))), (0)::numeric) AS despesas_pendentes, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = 'overdue'::text))), (0)::numeric) AS despesas_vencidas, + (COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric) - COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric)) AS saldo_projetado, + count(fr.id) FILTER (WHERE (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text]))) AS count_registros + FROM (generate_series(((date_trunc('month'::text, (CURRENT_DATE)::timestamp with time zone))::date)::timestamp with time zone, (((date_trunc('month'::text, (CURRENT_DATE)::timestamp with time zone) + '5 mons'::interval))::date)::timestamp with time zone, '1 mon'::interval) gs(mes) + LEFT JOIN public.financial_records fr ON (((fr.deleted_at IS NULL) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])) AND ((date_trunc('month'::text, (fr.due_date)::timestamp with time zone))::date = gs.mes)))) + GROUP BY gs.mes + ORDER BY gs.mes; + + +-- +-- Name: VIEW v_cashflow_projection; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.v_cashflow_projection IS 'Fluxo de caixa projetado: pr??ximos 6 meses com totais de pending+overdue por due_date. Usa security_invoker=on ??? filtra automaticamente pelo auth.uid() via RLS de financial_records.'; + + +-- +-- Name: v_commitment_totals; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_commitment_totals AS + SELECT c.tenant_id, + c.id AS commitment_id, + (COALESCE(sum(l.minutes), (0)::bigint))::integer AS total_minutes + FROM (public.determined_commitments c + LEFT JOIN public.commitment_time_logs l ON ((l.commitment_id = c.id))) + GROUP BY c.tenant_id, c.id; + + +-- +-- Name: v_patient_engajamento; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_patient_engajamento WITH (security_invoker='on') AS + WITH sessoes AS ( + SELECT ae.patient_id, + ae.tenant_id, + count(*) FILTER (WHERE (ae.status = 'realizado'::public.status_evento_agenda)) AS total_realizadas, + count(*) FILTER (WHERE (ae.status = ANY (ARRAY['realizado'::public.status_evento_agenda, 'cancelado'::public.status_evento_agenda, 'faltou'::public.status_evento_agenda]))) AS total_marcadas, + count(*) FILTER (WHERE (ae.status = 'faltou'::public.status_evento_agenda)) AS total_faltas, + max(ae.inicio_em) FILTER (WHERE (ae.status = 'realizado'::public.status_evento_agenda)) AS ultima_sessao_em, + min(ae.inicio_em) FILTER (WHERE (ae.status = 'realizado'::public.status_evento_agenda)) AS primeira_sessao_em, + count(*) FILTER (WHERE ((ae.status = 'realizado'::public.status_evento_agenda) AND (ae.inicio_em >= (now() - '30 days'::interval)))) AS sessoes_ultimo_mes + FROM public.agenda_eventos ae + WHERE (ae.patient_id IS NOT NULL) + GROUP BY ae.patient_id, ae.tenant_id + ), financeiro AS ( + SELECT fr.patient_id, + fr.tenant_id, + COALESCE(sum(fr.final_amount) FILTER (WHERE (fr.status = 'paid'::text)), (0)::numeric) AS total_pago, + COALESCE(avg(fr.final_amount) FILTER (WHERE (fr.status = 'paid'::text)), (0)::numeric) AS ticket_medio, + count(*) FILTER (WHERE ((fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])) AND (fr.due_date < now()))) AS cobr_vencidas, + count(*) FILTER (WHERE (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text]))) AS cobr_pendentes, + count(*) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = 'paid'::text))) AS cobr_pagas + FROM public.financial_records fr + WHERE ((fr.patient_id IS NOT NULL) AND (fr.deleted_at IS NULL)) + GROUP BY fr.patient_id, fr.tenant_id + ) + SELECT p.id AS patient_id, + p.tenant_id, + p.nome_completo, + p.status, + p.risco_elevado, + COALESCE(s.total_realizadas, (0)::bigint) AS total_sessoes, + COALESCE(s.sessoes_ultimo_mes, (0)::bigint) AS sessoes_ultimo_mes, + s.primeira_sessao_em, + s.ultima_sessao_em, + (EXTRACT(day FROM (now() - s.ultima_sessao_em)))::integer AS dias_sem_sessao, + CASE + WHEN (COALESCE(s.total_marcadas, (0)::bigint) = 0) THEN NULL::numeric + ELSE round((((s.total_realizadas)::numeric / (s.total_marcadas)::numeric) * (100)::numeric), 1) + END AS taxa_comparecimento, + COALESCE(f.total_pago, (0)::numeric) AS ltv_total, + round(COALESCE(f.ticket_medio, (0)::numeric), 2) AS ticket_medio, + COALESCE(f.cobr_vencidas, (0)::bigint) AS cobr_vencidas, + COALESCE(f.cobr_pagas, (0)::bigint) AS cobr_pagas, + CASE + WHEN (COALESCE((f.cobr_pagas + f.cobr_vencidas), (0)::bigint) = 0) THEN NULL::numeric + ELSE round((((f.cobr_pagas)::numeric / ((f.cobr_pagas + f.cobr_vencidas))::numeric) * (100)::numeric), 1) + END AS taxa_pagamentos_dia, + round(LEAST((100)::numeric, COALESCE((( + CASE + WHEN (COALESCE(s.total_marcadas, (0)::bigint) = 0) THEN (50)::numeric + ELSE LEAST((50)::numeric, (((s.total_realizadas)::numeric / (s.total_marcadas)::numeric) * (50)::numeric)) + END + + CASE + WHEN (COALESCE((f.cobr_pagas + f.cobr_vencidas), (0)::bigint) = 0) THEN (30)::numeric + ELSE LEAST((30)::numeric, (((f.cobr_pagas)::numeric / ((f.cobr_pagas + f.cobr_vencidas))::numeric) * (30)::numeric)) + END) + ( + CASE + WHEN (s.ultima_sessao_em IS NULL) THEN 0 + WHEN (EXTRACT(day FROM (now() - s.ultima_sessao_em)) <= (14)::numeric) THEN 20 + WHEN (EXTRACT(day FROM (now() - s.ultima_sessao_em)) <= (30)::numeric) THEN 15 + WHEN (EXTRACT(day FROM (now() - s.ultima_sessao_em)) <= (60)::numeric) THEN 8 + ELSE 0 + END)::numeric), (0)::numeric)), 0) AS engajamento_score, + CASE + WHEN (s.primeira_sessao_em IS NULL) THEN NULL::integer + ELSE (EXTRACT(day FROM (now() - s.primeira_sessao_em)))::integer + END AS duracao_tratamento_dias + FROM ((public.patients p + LEFT JOIN sessoes s ON (((s.patient_id = p.id) AND (s.tenant_id = p.tenant_id)))) + LEFT JOIN financeiro f ON (((f.patient_id = p.id) AND (f.tenant_id = p.tenant_id)))); + + +-- +-- Name: VIEW v_patient_engajamento; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.v_patient_engajamento IS 'Score de engajamento e métricas consolidadas por paciente. Calculado em tempo real via RLS (security_invoker=on).'; + + +-- +-- Name: v_patient_groups_with_counts; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_patient_groups_with_counts AS + SELECT pg.id, + pg.nome, + pg.cor, + pg.owner_id, + pg.is_system, + pg.is_active, + pg.created_at, + pg.updated_at, + (COALESCE(count(pgp.patient_id), (0)::bigint))::integer AS patients_count + FROM (public.patient_groups pg + LEFT JOIN public.patient_group_patient pgp ON ((pgp.patient_group_id = pg.id))) + GROUP BY pg.id, pg.nome, pg.cor, pg.owner_id, pg.is_system, pg.is_active, pg.created_at, pg.updated_at; + + +-- +-- Name: v_patients_risco; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_patients_risco WITH (security_invoker='on') AS + SELECT p.id, + p.tenant_id, + p.nome_completo, + p.status, + p.risco_elevado, + p.risco_nota, + p.risco_sinalizado_em, + e.dias_sem_sessao, + e.engajamento_score, + e.taxa_comparecimento, + CASE + WHEN p.risco_elevado THEN 'risco_sinalizado'::text + WHEN ((COALESCE(e.dias_sem_sessao, 999) > 30) AND (p.status = 'Ativo'::text)) THEN 'sem_sessao_30d'::text + WHEN (COALESCE(e.taxa_comparecimento, (100)::numeric) < (60)::numeric) THEN 'baixo_comparecimento'::text + WHEN (COALESCE(e.cobr_vencidas, (0)::bigint) > 0) THEN 'cobranca_vencida'::text + ELSE 'ok'::text + END AS alerta_tipo + FROM (public.patients p + JOIN public.v_patient_engajamento e ON ((e.patient_id = p.id))) + WHERE ((p.status = 'Ativo'::text) AND ((p.risco_elevado = true) OR (COALESCE(e.dias_sem_sessao, 999) > 30) OR (COALESCE(e.taxa_comparecimento, (100)::numeric) < (60)::numeric) OR (COALESCE(e.cobr_vencidas, (0)::bigint) > 0))); + + +-- +-- Name: VIEW v_patients_risco; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.v_patients_risco IS 'Pacientes ativos que precisam de atenção: risco clínico, sem sessão há 30+ dias, baixo comparecimento ou cobrança vencida'; + + +-- +-- Name: v_plan_active_prices; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_plan_active_prices AS + SELECT plan_id, + max( + CASE + WHEN (("interval" = 'month'::text) AND is_active) THEN amount_cents + ELSE NULL::integer + END) AS monthly_cents, + max( + CASE + WHEN (("interval" = 'year'::text) AND is_active) THEN amount_cents + ELSE NULL::integer + END) AS yearly_cents, + max( + CASE + WHEN (("interval" = 'month'::text) AND is_active) THEN currency + ELSE NULL::text + END) AS monthly_currency, + max( + CASE + WHEN (("interval" = 'year'::text) AND is_active) THEN currency + ELSE NULL::text + END) AS yearly_currency + FROM public.plan_prices + GROUP BY plan_id; + + +-- +-- Name: v_public_pricing; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_public_pricing AS + SELECT p.id AS plan_id, + p.key AS plan_key, + p.name AS plan_name, + COALESCE(pp.public_name, ''::text) AS public_name, + COALESCE(pp.public_description, ''::text) AS public_description, + pp.badge, + COALESCE(pp.is_featured, false) AS is_featured, + COALESCE(pp.is_visible, true) AS is_visible, + COALESCE(pp.sort_order, 0) AS sort_order, + ap.monthly_cents, + ap.yearly_cents, + ap.monthly_currency, + ap.yearly_currency, + COALESCE(( SELECT jsonb_agg(jsonb_build_object('id', b.id, 'text', b.text, 'highlight', b.highlight, 'sort_order', b.sort_order) ORDER BY b.sort_order, b.created_at) AS jsonb_agg + FROM public.plan_public_bullets b + WHERE (b.plan_id = p.id)), '[]'::jsonb) AS bullets, + p.target AS plan_target + FROM ((public.plans p + LEFT JOIN public.plan_public pp ON ((pp.plan_id = p.id))) + LEFT JOIN public.v_plan_active_prices ap ON ((ap.plan_id = p.id))) + ORDER BY COALESCE(pp.sort_order, 0), p.key; + + +-- +-- Name: v_subscription_feature_mismatch; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_subscription_feature_mismatch AS + WITH expected AS ( + SELECT s.user_id AS owner_id, + f.key AS feature_key + FROM ((public.subscriptions s + JOIN public.plan_features pf ON (((pf.plan_id = s.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))) + WHERE ((s.status = 'active'::text) AND (s.tenant_id IS NULL) AND (s.user_id IS NOT NULL)) + ), actual AS ( + SELECT e.owner_id, + e.feature_key + FROM public.owner_feature_entitlements e + ) + SELECT COALESCE(expected.owner_id, actual.owner_id) AS owner_id, + COALESCE(expected.feature_key, actual.feature_key) AS feature_key, + CASE + WHEN ((expected.feature_key IS NOT NULL) AND (actual.feature_key IS NULL)) THEN 'missing_entitlement'::text + WHEN ((expected.feature_key IS NULL) AND (actual.feature_key IS NOT NULL)) THEN 'unexpected_entitlement'::text + ELSE NULL::text + END AS mismatch_type + FROM (expected + FULL JOIN actual ON (((expected.owner_id = actual.owner_id) AND (expected.feature_key = actual.feature_key)))) + WHERE ((expected.feature_key IS NULL) OR (actual.feature_key IS NULL)); + + +-- +-- Name: v_subscription_health; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_subscription_health AS + SELECT s.id AS subscription_id, + s.user_id AS owner_id, + s.status, + s.plan_id, + p.key AS plan_key, + s.current_period_start, + s.current_period_end, + s.updated_at, + CASE + WHEN (s.plan_id IS NULL) THEN 'missing_plan'::text + WHEN (p.id IS NULL) THEN 'invalid_plan'::text + WHEN ((s.status = 'active'::text) AND (s.current_period_end IS NOT NULL) AND (s.current_period_end < now())) THEN 'expired_but_active'::text + WHEN ((s.status = 'canceled'::text) AND (s.current_period_end > now())) THEN 'canceled_but_still_in_period'::text + ELSE 'ok'::text + END AS health_status, + CASE + WHEN (s.tenant_id IS NOT NULL) THEN 'clinic'::text + ELSE 'therapist'::text + END AS owner_type, + COALESCE(s.tenant_id, s.user_id) AS owner_ref + FROM (public.subscriptions s + LEFT JOIN public.plans p ON ((p.id = s.plan_id))); + + +-- +-- Name: v_subscription_health_v2; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_subscription_health_v2 AS + SELECT s.id AS subscription_id, + s.user_id AS owner_id, + CASE + WHEN (s.tenant_id IS NOT NULL) THEN 'clinic'::text + ELSE 'therapist'::text + END AS owner_type, + COALESCE(s.tenant_id, s.user_id) AS owner_ref, + s.status, + s.plan_id, + p.key AS plan_key, + s.current_period_start, + s.current_period_end, + s.updated_at, + CASE + WHEN (s.plan_id IS NULL) THEN 'missing_plan'::text + WHEN (p.id IS NULL) THEN 'invalid_plan'::text + WHEN ((s.status = 'active'::text) AND (s.current_period_end IS NOT NULL) AND (s.current_period_end < now())) THEN 'expired_but_active'::text + WHEN ((s.status = 'canceled'::text) AND (s.current_period_end > now())) THEN 'canceled_but_still_in_period'::text + ELSE 'ok'::text + END AS health_status + FROM (public.subscriptions s + LEFT JOIN public.plans p ON ((p.id = s.plan_id))); + + +-- +-- Name: v_tag_patient_counts; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tag_patient_counts AS + SELECT t.id, + t.owner_id, + t.nome, + t.cor, + t.is_padrao, + t.created_at, + t.updated_at, + (COALESCE(count(ppt.patient_id), (0)::bigint))::integer AS pacientes_count, + (COALESCE(count(ppt.patient_id), (0)::bigint))::integer AS patient_count + FROM (public.patient_tags t + LEFT JOIN public.patient_patient_tag ppt ON (((ppt.tag_id = t.id) AND (ppt.owner_id = t.owner_id)))) + GROUP BY t.id, t.owner_id, t.nome, t.cor, t.is_padrao, t.created_at, t.updated_at; + + +-- +-- Name: v_tenant_active_subscription; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_active_subscription AS + SELECT DISTINCT ON (tenant_id) tenant_id, + plan_id, + plan_key, + "interval", + status, + current_period_start, + current_period_end, + created_at + FROM public.subscriptions s + WHERE ((tenant_id IS NOT NULL) AND (status = 'active'::text) AND ((current_period_end IS NULL) OR (current_period_end > now()))) + ORDER BY tenant_id, created_at DESC; + + +-- +-- Name: v_tenant_entitlements; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_entitlements AS + SELECT a.tenant_id, + f.key AS feature_key, + true AS allowed + FROM ((public.v_tenant_active_subscription a + JOIN public.plan_features pf ON (((pf.plan_id = a.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))); + + +-- +-- Name: v_tenant_entitlements_full; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_entitlements_full AS + SELECT a.tenant_id, + f.key AS feature_key, + (pf.enabled = true) AS allowed, + pf.limits, + a.plan_id, + p.key AS plan_key + FROM (((public.v_tenant_active_subscription a + JOIN public.plan_features pf ON ((pf.plan_id = a.plan_id))) + JOIN public.features f ON ((f.id = pf.feature_id))) + JOIN public.plans p ON ((p.id = a.plan_id))); + + +-- +-- Name: v_tenant_entitlements_json; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_entitlements_json AS + SELECT tenant_id, + max(plan_key) AS plan_key, + jsonb_object_agg(feature_key, jsonb_build_object('allowed', allowed, 'limits', COALESCE(limits, '{}'::jsonb)) ORDER BY feature_key) AS entitlements + FROM public.v_tenant_entitlements_full + GROUP BY tenant_id; + + +-- +-- Name: v_tenant_feature_exceptions; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_feature_exceptions AS + SELECT tf.tenant_id, + a.plan_key, + tf.feature_key, + 'commercial_exception'::text AS exception_type + FROM ((public.tenant_features tf + JOIN public.v_tenant_active_subscription a ON ((a.tenant_id = tf.tenant_id))) + LEFT JOIN public.v_tenant_entitlements_full v ON (((v.tenant_id = tf.tenant_id) AND (v.feature_key = tf.feature_key)))) + WHERE ((tf.enabled = true) AND (COALESCE(v.allowed, false) = false)); + + +-- +-- Name: v_tenant_feature_mismatch; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_feature_mismatch AS + WITH plan_allowed AS ( + SELECT v.tenant_id, + v.feature_key, + v.allowed + FROM public.v_tenant_entitlements_full v + ), overrides AS ( + SELECT tf.tenant_id, + tf.feature_key, + tf.enabled + FROM public.tenant_features tf + ) + SELECT o.tenant_id, + o.feature_key, + CASE + WHEN ((o.enabled = true) AND (COALESCE(p.allowed, false) = false)) THEN 'unexpected_override'::text + ELSE NULL::text + END AS mismatch_type + FROM (overrides o + LEFT JOIN plan_allowed p ON (((p.tenant_id = o.tenant_id) AND (p.feature_key = o.feature_key)))) + WHERE ((o.enabled = true) AND (COALESCE(p.allowed, false) = false)); + + +-- +-- Name: v_tenant_members_with_profiles; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_members_with_profiles AS + SELECT tm.id AS tenant_member_id, + tm.tenant_id, + tm.user_id, + tm.role, + tm.status, + tm.created_at, + p.full_name, + au.email + FROM ((public.tenant_members tm + LEFT JOIN public.profiles p ON ((p.id = tm.user_id))) + LEFT JOIN auth.users au ON ((au.id = tm.user_id))); + + +-- +-- Name: v_tenant_people; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_people AS + SELECT 'member'::text AS type, + m.tenant_id, + m.user_id, + u.email, + m.role, + m.status, + NULL::uuid AS invite_token, + NULL::timestamp with time zone AS expires_at + FROM (public.tenant_members m + JOIN auth.users u ON ((u.id = m.user_id))) +UNION ALL + SELECT 'invite'::text AS type, + i.tenant_id, + NULL::uuid AS user_id, + i.email, + i.role, + 'invited'::text AS status, + i.token AS invite_token, + i.expires_at + FROM public.tenant_invites i + WHERE ((i.accepted_at IS NULL) AND (i.revoked_at IS NULL)); + + +-- +-- Name: v_tenant_staff; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_staff AS + SELECT ('m_'::text || (tm.id)::text) AS row_id, + tm.tenant_id, + tm.user_id, + tm.role, + tm.status, + tm.created_at, + p.full_name, + au.email, + NULL::uuid AS invite_token + FROM ((public.tenant_members tm + LEFT JOIN public.profiles p ON ((p.id = tm.user_id))) + LEFT JOIN auth.users au ON ((au.id = tm.user_id))) +UNION ALL + SELECT ('i_'::text || (ti.id)::text) AS row_id, + ti.tenant_id, + NULL::uuid AS user_id, + ti.role, + 'invited'::text AS status, + ti.created_at, + NULL::text AS full_name, + ti.email, + ti.token AS invite_token + FROM public.tenant_invites ti + WHERE ((ti.accepted_at IS NULL) AND (ti.revoked_at IS NULL) AND (ti.expires_at > now())); + + +-- +-- Name: v_twilio_whatsapp_overview; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_twilio_whatsapp_overview AS + SELECT nc.id AS channel_id, + nc.tenant_id, + nc.owner_id, + nc.is_active, + nc.connection_status, + nc.display_name, + nc.twilio_subaccount_sid, + nc.twilio_phone_number, + nc.twilio_phone_sid, + nc.cost_per_message_usd, + nc.price_per_message_brl, + nc.provisioned_at, + nc.created_at, + nc.updated_at, + COALESCE(u.messages_sent, 0) AS current_month_sent, + COALESCE(u.messages_delivered, 0) AS current_month_delivered, + COALESCE(u.messages_failed, 0) AS current_month_failed, + COALESCE(u.cost_usd, (0)::numeric) AS current_month_cost_usd, + COALESCE(u.cost_brl, (0)::numeric) AS current_month_cost_brl, + COALESCE(u.revenue_brl, (0)::numeric) AS current_month_revenue_brl, + COALESCE(u.margin_brl, (0)::numeric) AS current_month_margin_brl + FROM (public.notification_channels nc + LEFT JOIN public.twilio_subaccount_usage u ON (((u.channel_id = nc.id) AND (u.period_start = (date_trunc('month'::text, (CURRENT_DATE)::timestamp with time zone))::date)))) + WHERE ((nc.channel = 'whatsapp'::text) AND (nc.provider = 'twilio'::text) AND (nc.deleted_at IS NULL)); + + +-- +-- Name: VIEW v_twilio_whatsapp_overview; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.v_twilio_whatsapp_overview IS 'Visão consolidada de subcontas Twilio WhatsApp com uso do mês corrente.'; + + +-- +-- Name: v_user_active_subscription; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_user_active_subscription AS + SELECT DISTINCT ON (user_id) user_id, + plan_id, + plan_key, + "interval", + status, + current_period_start, + current_period_end, + created_at + FROM public.subscriptions s + WHERE ((tenant_id IS NULL) AND (user_id IS NOT NULL) AND (status = 'active'::text) AND ((current_period_end IS NULL) OR (current_period_end > now()))) + ORDER BY user_id, created_at DESC; + + +-- +-- Name: v_user_entitlements; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_user_entitlements AS + SELECT a.user_id, + f.key AS feature_key, + true AS allowed + FROM ((public.v_user_active_subscription a + JOIN public.plan_features pf ON (((pf.plan_id = a.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))); + + +-- +-- Name: messages; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +) +PARTITION BY RANGE (inserted_at); + + +-- +-- Name: messages_2026_03_26; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_26 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_27; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_27 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_28; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_28 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_29; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_29 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_30; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_30 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_31; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_31 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_04_01; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_04_01 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: schema_migrations; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.schema_migrations ( + version bigint NOT NULL, + inserted_at timestamp(0) without time zone +); + + +-- +-- Name: subscription; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.subscription ( + id bigint NOT NULL, + subscription_id uuid NOT NULL, + entity regclass NOT NULL, + filters realtime.user_defined_filter[] DEFAULT '{}'::realtime.user_defined_filter[] NOT NULL, + claims jsonb NOT NULL, + claims_role regrole GENERATED ALWAYS AS (realtime.to_regrole((claims ->> 'role'::text))) STORED NOT NULL, + created_at timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL +); + + +-- +-- Name: subscription_id_seq; Type: SEQUENCE; Schema: realtime; Owner: - +-- + +ALTER TABLE realtime.subscription ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY ( + SEQUENCE NAME realtime.subscription_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: buckets; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.buckets ( + id text NOT NULL, + name text NOT NULL, + owner uuid, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + public boolean DEFAULT false, + avif_autodetection boolean DEFAULT false, + file_size_limit bigint, + allowed_mime_types text[], + owner_id text, + type storage.buckettype DEFAULT 'STANDARD'::storage.buckettype NOT NULL +); + + +-- +-- Name: COLUMN buckets.owner; Type: COMMENT; Schema: storage; Owner: - +-- + +COMMENT ON COLUMN storage.buckets.owner IS 'Field is deprecated, use owner_id instead'; + + +-- +-- Name: buckets_analytics; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.buckets_analytics ( + name text NOT NULL, + type storage.buckettype DEFAULT 'ANALYTICS'::storage.buckettype NOT NULL, + format text DEFAULT 'ICEBERG'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL, + deleted_at timestamp with time zone +); + + +-- +-- Name: buckets_vectors; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.buckets_vectors ( + id text NOT NULL, + type storage.buckettype DEFAULT 'VECTOR'::storage.buckettype NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: iceberg_namespaces; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.iceberg_namespaces ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + bucket_name text NOT NULL, + name text NOT NULL COLLATE pg_catalog."C", + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + metadata jsonb DEFAULT '{}'::jsonb NOT NULL, + catalog_id uuid NOT NULL +); + + +-- +-- Name: iceberg_tables; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.iceberg_tables ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + namespace_id uuid NOT NULL, + bucket_name text NOT NULL, + name text NOT NULL COLLATE pg_catalog."C", + location text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + remote_table_id text, + shard_key text, + shard_id text, + catalog_id uuid NOT NULL +); + + +-- +-- Name: migrations; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.migrations ( + id integer NOT NULL, + name character varying(100) NOT NULL, + hash character varying(40) NOT NULL, + executed_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP +); + + +-- +-- Name: objects; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.objects ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + bucket_id text, + name text, + owner uuid, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + last_accessed_at timestamp with time zone DEFAULT now(), + metadata jsonb, + path_tokens text[] GENERATED ALWAYS AS (string_to_array(name, '/'::text)) STORED, + version text, + owner_id text, + user_metadata jsonb +); + + +-- +-- Name: COLUMN objects.owner; Type: COMMENT; Schema: storage; Owner: - +-- + +COMMENT ON COLUMN storage.objects.owner IS 'Field is deprecated, use owner_id instead'; + + +-- +-- Name: s3_multipart_uploads; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.s3_multipart_uploads ( + id text NOT NULL, + in_progress_size bigint DEFAULT 0 NOT NULL, + upload_signature text NOT NULL, + bucket_id text NOT NULL, + key text NOT NULL COLLATE pg_catalog."C", + version text NOT NULL, + owner_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + user_metadata jsonb +); + + +-- +-- Name: s3_multipart_uploads_parts; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.s3_multipart_uploads_parts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + upload_id text NOT NULL, + size bigint DEFAULT 0 NOT NULL, + part_number integer NOT NULL, + bucket_id text NOT NULL, + key text NOT NULL COLLATE pg_catalog."C", + etag text NOT NULL, + owner_id text, + version text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: vector_indexes; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.vector_indexes ( + id text DEFAULT gen_random_uuid() NOT NULL, + name text NOT NULL COLLATE pg_catalog."C", + bucket_id text NOT NULL, + data_type text NOT NULL, + dimension integer NOT NULL, + distance_metric text NOT NULL, + metadata_configuration jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: hooks; Type: TABLE; Schema: supabase_functions; Owner: - +-- + +CREATE TABLE supabase_functions.hooks ( + id bigint NOT NULL, + hook_table_id integer NOT NULL, + hook_name text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + request_id bigint +); + + +-- +-- Name: TABLE hooks; Type: COMMENT; Schema: supabase_functions; Owner: - +-- + +COMMENT ON TABLE supabase_functions.hooks IS 'Supabase Functions Hooks: Audit trail for triggered hooks.'; + + +-- +-- Name: hooks_id_seq; Type: SEQUENCE; Schema: supabase_functions; Owner: - +-- + +CREATE SEQUENCE supabase_functions.hooks_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: hooks_id_seq; Type: SEQUENCE OWNED BY; Schema: supabase_functions; Owner: - +-- + +ALTER SEQUENCE supabase_functions.hooks_id_seq OWNED BY supabase_functions.hooks.id; + + +-- +-- Name: migrations; Type: TABLE; Schema: supabase_functions; Owner: - +-- + +CREATE TABLE supabase_functions.migrations ( + version text NOT NULL, + inserted_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: messages_2026_03_26; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_26 FOR VALUES FROM ('2026-03-26 00:00:00') TO ('2026-03-27 00:00:00'); + + +-- +-- Name: messages_2026_03_27; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_27 FOR VALUES FROM ('2026-03-27 00:00:00') TO ('2026-03-28 00:00:00'); + + +-- +-- Name: messages_2026_03_28; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_28 FOR VALUES FROM ('2026-03-28 00:00:00') TO ('2026-03-29 00:00:00'); + + +-- +-- Name: messages_2026_03_29; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_29 FOR VALUES FROM ('2026-03-29 00:00:00') TO ('2026-03-30 00:00:00'); + + +-- +-- Name: messages_2026_03_30; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_30 FOR VALUES FROM ('2026-03-30 00:00:00') TO ('2026-03-31 00:00:00'); + + +-- +-- Name: messages_2026_03_31; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_31 FOR VALUES FROM ('2026-03-31 00:00:00') TO ('2026-04-01 00:00:00'); + + +-- +-- Name: messages_2026_04_01; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_04_01 FOR VALUES FROM ('2026-04-01 00:00:00') TO ('2026-04-02 00:00:00'); + + +-- +-- Name: refresh_tokens id; Type: DEFAULT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens ALTER COLUMN id SET DEFAULT nextval('auth.refresh_tokens_id_seq'::regclass); + + +-- +-- Name: _db_migrations id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._db_migrations ALTER COLUMN id SET DEFAULT nextval('public._db_migrations_id_seq'::regclass); + + +-- +-- Name: agenda_online_slots id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots ALTER COLUMN id SET DEFAULT nextval('public.agenda_online_slots_id_seq'::regclass); + + +-- +-- Name: hooks id; Type: DEFAULT; Schema: supabase_functions; Owner: - +-- + +ALTER TABLE ONLY supabase_functions.hooks ALTER COLUMN id SET DEFAULT nextval('supabase_functions.hooks_id_seq'::regclass); + + +-- +-- Data for Name: extensions; Type: TABLE DATA; Schema: _realtime; Owner: - +-- + +COPY _realtime.extensions (id, type, settings, tenant_external_id, inserted_at, updated_at) FROM stdin; +1144465c-0c37-4c5e-826b-d37078c0f125 postgres_cdc_rls {"region": "us-east-1", "db_host": "jojNM5epTA6mHrc9dSyLoHNyb6lzYaXqj0adu+DMEsk9UXGm67BsSlbKWPaH8DuL", "db_name": "sWBpZNdjggEPTQVlI52Zfw==", "db_port": "+enMDFi1J/3IrrquHHwUmA==", "db_user": "uxbEq/zz8DXVD53TOI1zmw==", "slot_name": "supabase_realtime_replication_slot", "db_password": "sWBpZNdjggEPTQVlI52Zfw==", "publication": "supabase_realtime", "ssl_enforced": false, "poll_interval_ms": 100, "poll_max_changes": 100, "poll_max_record_bytes": 1048576} realtime-dev 2026-03-29 11:26:20 2026-03-29 11:26:20 +\. + + +-- +-- Data for Name: schema_migrations; Type: TABLE DATA; Schema: _realtime; Owner: - +-- + +COPY _realtime.schema_migrations (version, inserted_at) FROM stdin; +20210706140551 2026-03-23 10:13:14 +20220329161857 2026-03-23 10:13:14 +20220410212326 2026-03-23 10:13:14 +20220506102948 2026-03-23 10:13:14 +20220527210857 2026-03-23 10:13:14 +20220815211129 2026-03-23 10:13:14 +20220815215024 2026-03-23 10:13:14 +20220818141501 2026-03-23 10:13:14 +20221018173709 2026-03-23 10:13:14 +20221102172703 2026-03-23 10:13:14 +20221223010058 2026-03-23 10:13:14 +20230110180046 2026-03-23 10:13:14 +20230810220907 2026-03-23 10:13:14 +20230810220924 2026-03-23 10:13:14 +20231024094642 2026-03-23 10:13:14 +20240306114423 2026-03-23 10:13:14 +20240418082835 2026-03-23 10:13:14 +20240625211759 2026-03-23 10:13:14 +20240704172020 2026-03-23 10:13:14 +20240902173232 2026-03-23 10:13:14 +20241106103258 2026-03-23 10:13:14 +20250424203323 2026-03-23 10:13:14 +20250613072131 2026-03-23 10:13:14 +20250711044927 2026-03-23 10:13:14 +20250811121559 2026-03-23 10:13:14 +20250926223044 2026-03-23 10:13:14 +20251204170944 2026-03-23 10:13:14 +20251218000543 2026-03-23 10:13:14 +\. + + +-- +-- Data for Name: tenants; Type: TABLE DATA; Schema: _realtime; Owner: - +-- + +COPY _realtime.tenants (id, name, external_id, jwt_secret, max_concurrent_users, inserted_at, updated_at, max_events_per_second, postgres_cdc_default, max_bytes_per_second, max_channels_per_client, max_joins_per_second, suspend, jwt_jwks, notify_private_alpha, private_only, migrations_ran, broadcast_adapter, max_presence_events_per_second, max_payload_size_in_kb) FROM stdin; +e921977e-b752-4790-9389-9d3f6be0e6b1 realtime-dev realtime-dev iNjicxc4+llvc9wovDvqymwfnj9teWMlyOIbJ8Fh6j2WNU8CIJ2ZgjR6MUIKqSmeDmvpsKLsZ9jgXJmQPpwL8w== 200 2026-03-29 11:26:20 2026-03-29 11:26:20 100 postgres_cdc_rls 100000 100 100 f {"keys": [{"x": "M5Sjqn5zwC9Kl1zVfUUGvv9boQjCGd45G8sdopBExB4", "y": "P6IXMvA2WYXSHSOMTBH2jsw_9rrzGy89FjPf6oOsIxQ", "alg": "ES256", "crv": "P-256", "ext": true, "kid": "b81269f1-21d8-4f2e-b719-c2240a840d90", "kty": "EC", "use": "sig", "key_ops": ["verify"]}, {"k": "c3VwZXItc2VjcmV0LWp3dC10b2tlbi13aXRoLWF0LWxlYXN0LTMyLWNoYXJhY3RlcnMtbG9uZw", "kty": "oct"}]} f f 65 gen_rpc 1000 3000 +\. + + +-- +-- Data for Name: audit_log_entries; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.audit_log_entries (instance_id, id, payload, created_at, ip_address) FROM stdin; +00000000-0000-0000-0000-000000000000 f3a9bb5c-a48b-48da-83b0-5e5f71cc1278 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-23 10:58:23.661651+00 +00000000-0000-0000-0000-000000000000 015ae6c3-f000-49f5-a05a-7e43256a5ead {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-23 10:59:13.173068+00 +00000000-0000-0000-0000-000000000000 2222f6e6-4a78-4c44-88cf-65b677a0bdb6 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-23 10:59:17.817071+00 +00000000-0000-0000-0000-000000000000 12c8d5f5-46f0-4e67-96d2-7722167c7b3c {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-23 11:30:05.361177+00 +00000000-0000-0000-0000-000000000000 4aa98446-9aa8-4442-985a-aae3fbd28cd2 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-23 11:30:08.832248+00 +00000000-0000-0000-0000-000000000000 4e5e241b-0ba7-4772-a492-5dce48836688 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 12:28:24.672784+00 +00000000-0000-0000-0000-000000000000 12c376a9-7e40-4701-b163-173b66bbcc05 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 12:28:24.674266+00 +00000000-0000-0000-0000-000000000000 54e723ec-3977-4d92-8595-ce7ab53dbd86 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 13:30:01.59908+00 +00000000-0000-0000-0000-000000000000 de3c62a2-5771-4cb3-8bc5-a0f2c5cdf7bd {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 13:30:01.600518+00 +00000000-0000-0000-0000-000000000000 970016c6-0f0b-41a0-b4eb-5da3a5064f8b {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 14:30:14.581786+00 +00000000-0000-0000-0000-000000000000 a951c68b-1c3f-46ca-82d1-aead121e268f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 14:30:14.582962+00 +00000000-0000-0000-0000-000000000000 34299538-d49f-41a9-b0cc-2abbedbae26e {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 15:28:29.566693+00 +00000000-0000-0000-0000-000000000000 276722cb-b0ee-46a3-93b9-f3d966f16c26 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 15:28:29.56777+00 +00000000-0000-0000-0000-000000000000 bdc467e8-37fa-488e-b21d-e12bb16d5225 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 16:26:59.228619+00 +00000000-0000-0000-0000-000000000000 f00324fe-263c-42fd-b885-1309db0c76d0 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 16:26:59.230039+00 +00000000-0000-0000-0000-000000000000 4e468b7b-c2c1-479f-a6f5-9d7aea99aaa7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 17:25:28.767876+00 +00000000-0000-0000-0000-000000000000 b1e6f592-6413-44bd-82ce-eb4ae4d3873b {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 17:25:28.770199+00 +00000000-0000-0000-0000-000000000000 8ab04c44-66b6-469e-b869-a69b35c1d35f {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 18:25:01.605164+00 +00000000-0000-0000-0000-000000000000 00c18ea4-a6bb-429b-9d0d-7dbddd7b3d94 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 18:25:01.615437+00 +00000000-0000-0000-0000-000000000000 53a407ef-2f1c-4bed-838a-997812acb065 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 19:25:02.095787+00 +00000000-0000-0000-0000-000000000000 2c310c64-0933-439d-81f9-74fb6ffbf68d {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 19:25:02.097455+00 +00000000-0000-0000-0000-000000000000 2720bc5f-9871-4c13-a725-b0c640a04698 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 20:23:51.534132+00 +00000000-0000-0000-0000-000000000000 19fc4616-6242-4e56-b340-48467d9be144 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 20:23:51.535311+00 +00000000-0000-0000-0000-000000000000 1eed06b7-6fa2-4d66-ac2c-3d3bcb5b0592 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 21:22:21.898206+00 +00000000-0000-0000-0000-000000000000 920acd65-8146-4b35-b6d5-5281f4ea1ed1 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 21:22:21.900821+00 +00000000-0000-0000-0000-000000000000 73a31066-566b-42a1-a281-0ec8fc71a2f5 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 22:25:02.312362+00 +00000000-0000-0000-0000-000000000000 44c5a6ee-6bd3-43f1-9a3f-7c13ef1a83b5 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 22:25:02.314918+00 +00000000-0000-0000-0000-000000000000 a28430ae-7a7d-4ce8-9ae3-c83f7b3f8f5d {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 23:24:14.39221+00 +00000000-0000-0000-0000-000000000000 b4f2883f-f222-4862-8cdf-135563aaa257 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-23 23:24:14.393364+00 +00000000-0000-0000-0000-000000000000 2d425bb6-1ab6-4d54-9502-1132ff7fd14f {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 10:17:05.380495+00 +00000000-0000-0000-0000-000000000000 48dac6fd-0dce-42e1-ba70-3b235a0e1161 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 10:17:05.385002+00 +00000000-0000-0000-0000-000000000000 bb85f6c0-d96a-4eb6-a8b7-5170b4ea74a7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 11:17:55.391428+00 +00000000-0000-0000-0000-000000000000 b063d335-22f8-470b-b0ed-df6f9d68a7ba {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 11:17:55.392611+00 +00000000-0000-0000-0000-000000000000 29529cd0-0680-4d1d-a5ff-e43736f70555 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 12:17:55.375751+00 +00000000-0000-0000-0000-000000000000 f1dc7c02-dd21-4882-83e1-5e83d60156f2 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 12:17:55.378137+00 +00000000-0000-0000-0000-000000000000 e0ce9d96-5ed7-4342-bcb0-13e420b91523 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 19:20:20.869716+00 +00000000-0000-0000-0000-000000000000 0a93156f-a22c-4d18-81f2-54367a3745ba {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 19:20:20.936303+00 +00000000-0000-0000-0000-000000000000 1ddab552-8e6f-4ea6-8cc1-f7d00b92abc7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 20:20:04.2956+00 +00000000-0000-0000-0000-000000000000 bf82dc60-43b8-4d3c-8cb1-01bc4fd4a341 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 20:20:04.297562+00 +00000000-0000-0000-0000-000000000000 c5a9e22f-feb2-4383-b4cd-7910f2266978 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 21:18:48.738276+00 +00000000-0000-0000-0000-000000000000 fb58f5f0-7279-41de-98aa-20fa277e3d84 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 21:18:48.74055+00 +00000000-0000-0000-0000-000000000000 0dda5ef1-f572-4379-b734-f65b9e044830 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 22:17:29.961966+00 +00000000-0000-0000-0000-000000000000 8762f6f6-d22e-4c60-b2a3-c5c8bf99d548 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-24 22:17:29.965545+00 +00000000-0000-0000-0000-000000000000 51cea4a0-f2cc-4e9b-8b1a-8b2c9cdf8b47 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-24 23:01:52.959606+00 +00000000-0000-0000-0000-000000000000 61e26d26-ce04-490d-802d-4f67cc379fda {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-24 23:01:56.400534+00 +00000000-0000-0000-0000-000000000000 8c73c9b9-2da1-42df-b1a6-f92b98ab9300 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-24 23:07:05.837147+00 +00000000-0000-0000-0000-000000000000 05bf6f2d-60c0-4e5a-8ec1-6f2a7403d0c2 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-24 23:07:09.755293+00 +00000000-0000-0000-0000-000000000000 8d63e967-c616-42b0-b1c5-c8f54f54beaa {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:01:49.505958+00 +00000000-0000-0000-0000-000000000000 cdde610b-ac0f-4d61-91bd-7af34f23acf0 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:01:57.651986+00 +00000000-0000-0000-0000-000000000000 ef89c4e5-e092-4c12-a3b5-608b0e79bd65 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:03:22.169295+00 +00000000-0000-0000-0000-000000000000 ba251517-d704-4aa9-9992-16a8590582e0 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:03:28.975189+00 +00000000-0000-0000-0000-000000000000 b9d81262-4370-4712-8db7-81d51e27fcc8 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:06:51.450521+00 +00000000-0000-0000-0000-000000000000 26ca7ff5-87d3-48a2-ad9f-4ac67a216da8 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:06:55.726041+00 +00000000-0000-0000-0000-000000000000 f8c943c2-6dc0-42f6-87c9-adffc9a336c5 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:07:45.021178+00 +00000000-0000-0000-0000-000000000000 bba67a2b-049e-4d1b-b6a7-286d2b9c45f7 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:07:48.235967+00 +00000000-0000-0000-0000-000000000000 151834d9-2232-43c8-a0e6-a39c6617246e {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:11:54.788614+00 +00000000-0000-0000-0000-000000000000 e903f726-e5fe-4e06-9051-f36dacc780ce {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:12:00.861214+00 +00000000-0000-0000-0000-000000000000 e300a60b-b944-4c72-bb52-9ff7dff08dd2 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 00:24:51.770222+00 +00000000-0000-0000-0000-000000000000 4580e71b-e19b-4a74-ad85-f08f74791e53 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 00:24:57.09306+00 +00000000-0000-0000-0000-000000000000 a12cb7e1-768c-4ca2-96de-d68ce160971a {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 01:25:50.776943+00 +00000000-0000-0000-0000-000000000000 ce559936-37f2-411f-ba99-401e6ac4d94f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 01:25:50.778414+00 +00000000-0000-0000-0000-000000000000 46ebfc8e-e65a-4118-8a42-1d873b1436cc {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 02:25:50.797262+00 +00000000-0000-0000-0000-000000000000 d09f6b35-52d2-4f5e-80f5-0b188fef98da {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 02:25:50.798466+00 +00000000-0000-0000-0000-000000000000 7e7a2284-bb63-4b8d-ab19-186394b12f8f {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:13:42.627459+00 +00000000-0000-0000-0000-000000000000 cd92d32d-ac1a-4681-b957-11d8d4bd1a09 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:13:46.932353+00 +00000000-0000-0000-0000-000000000000 54013ee2-2dc3-4e36-8a35-09eb748ccba4 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:20:03.154923+00 +00000000-0000-0000-0000-000000000000 598cd35b-8c6e-4d41-9ec6-c25dcc4c6715 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:20:07.255873+00 +00000000-0000-0000-0000-000000000000 520167bb-ec1c-4e67-8c07-8d2ba457ec3c {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:24:18.554167+00 +00000000-0000-0000-0000-000000000000 4dc11041-8b36-4703-b5c5-2411a13b96da {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:24:24.699031+00 +00000000-0000-0000-0000-000000000000 9cfd2eae-f911-4dc7-9c5d-78499fd608f1 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 03:25:07.736803+00 +00000000-0000-0000-0000-000000000000 258e6ed2-5959-4893-8433-caa4b9ccb106 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 03:29:21.141199+00 +00000000-0000-0000-0000-000000000000 c76a59d2-0829-456a-957b-c623b14ae03e {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 10:28:55.843362+00 +00000000-0000-0000-0000-000000000000 f96b1f88-1f48-4a96-bbfc-b8c6527a31df {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 10:29:14.90848+00 +00000000-0000-0000-0000-000000000000 65f06c2b-5ee5-4bf1-a6ff-21dbe657e0b4 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 10:29:19.403154+00 +00000000-0000-0000-0000-000000000000 9e2290b7-dcfa-4bf5-b244-1f91011bfbe3 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 11:27:32.781897+00 +00000000-0000-0000-0000-000000000000 9cc32bd5-ae94-4014-90cb-b468e133cc10 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 11:27:32.783409+00 +00000000-0000-0000-0000-000000000000 2e244065-7456-4aba-a841-0920875e7f1b {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 12:16:54.862937+00 +00000000-0000-0000-0000-000000000000 f7cf0072-bda9-4b59-9532-7354a79c0306 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 12:17:01.538685+00 +00000000-0000-0000-0000-000000000000 5d8407ed-be29-482b-8877-42cf6e78f6c3 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:05:17.486922+00 +00000000-0000-0000-0000-000000000000 a8a0c3ef-6a10-4c8f-b53c-830ee3c2cfaf {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:05:21.871823+00 +00000000-0000-0000-0000-000000000000 2dfcfe2d-3770-42a6-8ceb-f16bd2e52151 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:05:31.190404+00 +00000000-0000-0000-0000-000000000000 c9e57ce5-33e2-4e79-b0b6-204dc22e4c8f {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:05:54.861933+00 +00000000-0000-0000-0000-000000000000 f62ee264-f036-46b9-9bdc-8bababf26492 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:06:00.886992+00 +00000000-0000-0000-0000-000000000000 ace96eee-7f68-4139-9f73-c3aa21d5e1d7 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:29:24.160199+00 +00000000-0000-0000-0000-000000000000 75b60071-d53f-490c-be4c-69f3c6f08da4 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:29:33.800526+00 +00000000-0000-0000-0000-000000000000 4a736318-9980-48ed-b13b-b47e38be5d41 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 13:30:03.032141+00 +00000000-0000-0000-0000-000000000000 a61bb073-c754-406a-b123-0b40c22915a3 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 13:37:21.932796+00 +00000000-0000-0000-0000-000000000000 80fd895f-8963-40f4-a9bb-4c59d95e964e {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 14:40:14.190475+00 +00000000-0000-0000-0000-000000000000 d2392bf0-18e8-45af-98f0-1d70e7cbe6f0 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 14:40:14.193129+00 +00000000-0000-0000-0000-000000000000 3fcfde42-9951-4df4-aae3-4abf256ed25d {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 15:38:52.104451+00 +00000000-0000-0000-0000-000000000000 e4c92ebd-11df-4e4c-82a1-7af12b797164 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 15:38:52.121069+00 +00000000-0000-0000-0000-000000000000 e6d4fb6d-fca8-4ee4-992a-59661b86b3d7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 16:39:45.201853+00 +00000000-0000-0000-0000-000000000000 f2606751-7081-4463-8c3f-985860702b7a {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 16:39:45.203238+00 +00000000-0000-0000-0000-000000000000 0db3c68a-49b6-42e3-8df0-a3dd1daa805d {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 17:39:45.220264+00 +00000000-0000-0000-0000-000000000000 20d02e8a-bdae-4c3d-9629-e0c1bc5e9d0a {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 17:39:45.221411+00 +00000000-0000-0000-0000-000000000000 58afa2b2-3d21-4d56-9b99-f96ec194170a {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 18:32:34.529973+00 +00000000-0000-0000-0000-000000000000 4afd9a79-0d8e-462e-8439-8c86ae984aa8 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 18:32:38.624903+00 +00000000-0000-0000-0000-000000000000 ee9fb7dc-392e-4fb2-9ee1-5d6fd7f632a8 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 18:35:17.643808+00 +00000000-0000-0000-0000-000000000000 aa72f08d-bdd6-400e-8798-3ad17598c5a8 {"action":"login","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 18:35:21.349866+00 +00000000-0000-0000-0000-000000000000 7ffd90be-aa82-4fa0-9a6a-6015fccedf99 {"action":"logout","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-25 18:35:43.306104+00 +00000000-0000-0000-0000-000000000000 ce068f66-0a8a-47a0-9fb1-2c3f727409f3 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-25 18:35:46.92007+00 +00000000-0000-0000-0000-000000000000 76e76323-7144-4ab0-9350-b1b009a5688a {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 19:33:46.819266+00 +00000000-0000-0000-0000-000000000000 f4ad35f0-f7f4-45c7-9e6f-b76d7075bcbd {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 19:33:46.822119+00 +00000000-0000-0000-0000-000000000000 d8d7a18a-1cd7-4694-a709-1c25bafd47cd {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 20:32:08.57133+00 +00000000-0000-0000-0000-000000000000 c0efd1eb-d3a4-4588-a6e4-6c6316afcbfa {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-25 20:32:08.573939+00 +00000000-0000-0000-0000-000000000000 663aa064-f89b-4629-9853-6921cec5f658 {"action":"login","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 10:13:28.062141+00 +00000000-0000-0000-0000-000000000000 bada5aa6-4962-42ab-90ac-f10ec7ada174 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 11:12:40.283978+00 +00000000-0000-0000-0000-000000000000 0284d0a9-1045-4d63-84c0-199f8fd57463 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 11:12:40.285424+00 +00000000-0000-0000-0000-000000000000 7fe49307-b9fc-4045-86e6-77ec5b6724d4 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 12:12:40.261029+00 +00000000-0000-0000-0000-000000000000 1cf673eb-6fed-4b60-a756-e5d9d8164ab2 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 12:12:40.262344+00 +00000000-0000-0000-0000-000000000000 89931cd1-3362-4153-9e26-c3a3450315bf {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 15:51:12.813868+00 +00000000-0000-0000-0000-000000000000 d4ae8391-85ed-4d02-be33-fc9895d10993 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 15:51:12.815012+00 +00000000-0000-0000-0000-000000000000 26bfda48-9d72-443a-8324-1944ebe4a701 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 16:49:43.315+00 +00000000-0000-0000-0000-000000000000 0f7adbbc-aaa1-4f3a-b04c-a6845b9e0ca6 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 16:49:43.323835+00 +00000000-0000-0000-0000-000000000000 d9baf33f-c034-49ad-8f16-902bea98ec5e {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 17:48:58.248187+00 +00000000-0000-0000-0000-000000000000 ca2f00e5-300f-4a17-b87e-ccbec14f2c7e {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 17:48:58.250458+00 +00000000-0000-0000-0000-000000000000 fa02ec44-7c86-418c-99c7-de2622acfb31 {"action":"token_refreshed","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 18:49:41.829309+00 +00000000-0000-0000-0000-000000000000 136da06f-a9bb-4523-bb16-47717edd8eb5 {"action":"token_revoked","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 18:49:41.831653+00 +00000000-0000-0000-0000-000000000000 e8a3a012-3e8d-499a-aa7a-4cbca7999463 {"action":"logout","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:46:23.049908+00 +00000000-0000-0000-0000-000000000000 c31208e4-7589-44be-9f3d-5f3f201a4df6 {"action":"login","actor_id":"aaaaaaaa-0008-0008-0008-000000000008","actor_username":"editor@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:46:29.068361+00 +00000000-0000-0000-0000-000000000000 81031375-449a-4a69-b4dc-2ee999a654b2 {"action":"logout","actor_id":"aaaaaaaa-0008-0008-0008-000000000008","actor_username":"editor@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:46:38.212531+00 +00000000-0000-0000-0000-000000000000 5bcaaf60-9f35-4d7b-a54b-b491ac07ed15 {"action":"login","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:46:43.300041+00 +00000000-0000-0000-0000-000000000000 6e71806d-9b38-4aec-af46-15f9aa5b5375 {"action":"logout","actor_id":"aaaaaaaa-0006-0006-0006-000000000006","actor_username":"saas@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:46:49.885518+00 +00000000-0000-0000-0000-000000000000 ab45715c-877c-4cea-9249-5e02e163d77a {"action":"user_signedup","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"team","traits":{"provider":"email"}} 2026-03-26 19:47:30.494975+00 +00000000-0000-0000-0000-000000000000 98634ad1-21f0-448b-81a4-6686bbfc9932 {"action":"login","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:47:30.515066+00 +00000000-0000-0000-0000-000000000000 e0613923-7e54-45b6-9861-14fd4a03f8e7 {"action":"logout","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:48:35.667525+00 +00000000-0000-0000-0000-000000000000 11139b73-efc6-43e4-b228-2ea2c4da475b {"action":"login","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:48:44.25225+00 +00000000-0000-0000-0000-000000000000 cf97f01d-8b2d-45a2-a571-d5ae4adce236 {"action":"logout","actor_id":"384a69d8-b7cd-40ac-9d3c-764c93532b66","actor_username":"terapeuta2@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:48:53.19869+00 +00000000-0000-0000-0000-000000000000 51c6d2fc-9a32-4449-91b0-707a74077e92 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:51:35.028402+00 +00000000-0000-0000-0000-000000000000 6f152cff-1e9a-4f0d-b2aa-d556ed20ad19 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:53:14.391225+00 +00000000-0000-0000-0000-000000000000 4dbde0d4-78e6-408f-84dc-ab6c246168b0 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:53:17.60162+00 +00000000-0000-0000-0000-000000000000 719ef7ce-f477-4de3-85d4-cb39b346bbf1 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 19:56:14.350054+00 +00000000-0000-0000-0000-000000000000 cd55882a-9e54-4730-b063-c9b4887c0e0a {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 19:56:18.624252+00 +00000000-0000-0000-0000-000000000000 232c3b43-f160-40a1-be37-2da29fc997bd {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 20:57:32.788753+00 +00000000-0000-0000-0000-000000000000 8d6852ac-b3b5-4543-bfed-e2369a73f211 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-26 20:57:32.794287+00 +00000000-0000-0000-0000-000000000000 f860fa47-b12d-4463-ab9a-46cbf022837e {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:19:25.905866+00 +00000000-0000-0000-0000-000000000000 449ddd1b-11e8-4b1e-bdc0-abc398dca77d {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:19:52.487097+00 +00000000-0000-0000-0000-000000000000 61bf81aa-1454-4723-af1a-fb00b2203c72 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:19:54.580581+00 +00000000-0000-0000-0000-000000000000 74a985d1-14b7-40c7-92db-444d0ba62ffb {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:31:18.590523+00 +00000000-0000-0000-0000-000000000000 17d9c260-731d-4fa7-9d69-9c1241c30bd9 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:31:20.209708+00 +00000000-0000-0000-0000-000000000000 6ec569b6-f0a1-4a0c-8c81-54fc82f6be93 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:31:39.762447+00 +00000000-0000-0000-0000-000000000000 75c4095e-994d-4ce9-9d77-59c514d88653 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:32:10.888074+00 +00000000-0000-0000-0000-000000000000 8ec7110e-415d-4627-935f-f0327b1dd58e {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:32:12.867178+00 +00000000-0000-0000-0000-000000000000 8350236f-d596-4d1e-8925-ae917e599ad4 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:33:05.320834+00 +00000000-0000-0000-0000-000000000000 25a7c28b-8ee2-4b94-bf6f-d046387fcde2 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:33:25.418988+00 +00000000-0000-0000-0000-000000000000 ce951d2d-488f-4757-92d0-3460f5bde396 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:40:53.407118+00 +00000000-0000-0000-0000-000000000000 39056ca1-fbd1-4e85-88ab-355c01a969ab {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:40:55.222474+00 +00000000-0000-0000-0000-000000000000 9591ecbc-aad0-4d5a-9076-413ed1e585ff {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:42:37.889869+00 +00000000-0000-0000-0000-000000000000 5777adf8-b362-496b-899f-5b05c0d93721 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:42:53.50928+00 +00000000-0000-0000-0000-000000000000 6a1e5a17-c7ea-453c-8626-d5022145ddf7 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:46:43.464106+00 +00000000-0000-0000-0000-000000000000 d28f02fc-a6ff-4aac-b49a-4fda2583bf17 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:46:46.362488+00 +00000000-0000-0000-0000-000000000000 b0e12d07-d349-4e14-8a3a-6ad42e747b21 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:52:49.203802+00 +00000000-0000-0000-0000-000000000000 c95a6365-3083-4f4f-a910-48f1405b272a {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:52:50.894594+00 +00000000-0000-0000-0000-000000000000 a8da7e38-f276-4aac-9a34-704af25a8716 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:53:20.228765+00 +00000000-0000-0000-0000-000000000000 1630012a-3347-479e-ab2b-76996c192c36 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:53:40.261757+00 +00000000-0000-0000-0000-000000000000 a8db2936-54c5-4614-94c5-4a13e6c83654 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:59:17.880911+00 +00000000-0000-0000-0000-000000000000 3d4fb0b1-d8d1-4beb-8c3a-53a22607cddd {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:59:25.603132+00 +00000000-0000-0000-0000-000000000000 10ef60d6-f55d-4370-b320-78c7b6561104 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 21:59:34.821814+00 +00000000-0000-0000-0000-000000000000 efc9e3f6-c034-4a04-977e-b81d445763ed {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 21:59:55.339526+00 +00000000-0000-0000-0000-000000000000 3b3a02dc-067d-42cc-917c-7811c4fd6347 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:02:44.995107+00 +00000000-0000-0000-0000-000000000000 c7cb137e-33a1-4fb2-9960-4bb015db752c {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:02:46.891055+00 +00000000-0000-0000-0000-000000000000 9bd389c2-f3ed-4197-a22b-bab553eb4e4f {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:12:46.756181+00 +00000000-0000-0000-0000-000000000000 bab2f893-dc20-436c-8363-d33a4aee8d06 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:13:14.788124+00 +00000000-0000-0000-0000-000000000000 651c44ae-e597-4840-ab53-1e6732543227 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:21:43.325253+00 +00000000-0000-0000-0000-000000000000 b6c79412-3e78-40e3-b079-555f9240682f {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:21:56.512281+00 +00000000-0000-0000-0000-000000000000 8a4f0ef0-c0ef-4278-852c-ca0ca0393fb9 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:22:05.26663+00 +00000000-0000-0000-0000-000000000000 1811dd26-c005-4aa5-b35d-f1d3a6668be7 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:22:21.864228+00 +00000000-0000-0000-0000-000000000000 5b46125b-ec57-4907-90bd-0f797cf56bfc {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 22:48:00.736884+00 +00000000-0000-0000-0000-000000000000 45fd6936-faf4-4df8-aac0-263d13f756c8 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 22:48:53.242588+00 +00000000-0000-0000-0000-000000000000 2a2f7b53-e0c5-4cfc-9b38-cea9d67ad2ad {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-26 23:32:17.947743+00 +00000000-0000-0000-0000-000000000000 8e3139b2-1c40-47ad-a247-3ccaa94e08ad {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-26 23:32:44.158626+00 +00000000-0000-0000-0000-000000000000 c0f1f8c1-5da2-4171-acf4-f15f3fef837b {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 09:06:02.093059+00 +00000000-0000-0000-0000-000000000000 0f53bba3-4d36-4bd9-bc78-4e5ce659129c {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 09:06:02.096051+00 +00000000-0000-0000-0000-000000000000 e2ea64a5-54a5-4b0a-95b4-cc26be5dc85e {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:27:06.674863+00 +00000000-0000-0000-0000-000000000000 f008faaf-004d-4d13-ba4d-cec5a113cfe8 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:39:56.661836+00 +00000000-0000-0000-0000-000000000000 5d36ee39-6793-42c6-a157-31037e7ddcbe {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"asd","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:52:45.795272+00 +00000000-0000-0000-0000-000000000000 98a8a1e1-8a81-4837-ba3c-c149d8d0be77 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leoardno","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:57:12.890495+00 +00000000-0000-0000-0000-000000000000 7a5d36c5-48b9-4a8c-b5af-af0fee6b61e2 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leoardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:57:34.211215+00 +00000000-0000-0000-0000-000000000000 4f5a2ed6-3898-4274-a852-519d71b71b21 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 09:58:48.279047+00 +00000000-0000-0000-0000-000000000000 5404eb4e-93f4-41a5-a86d-4250e2301115 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 10:05:13.289504+00 +00000000-0000-0000-0000-000000000000 e1e7b9e4-86c0-4d43-b108-ce91cfa7305f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 10:05:13.291502+00 +00000000-0000-0000-0000-000000000000 51d32ba8-701c-4bcc-ab72-74c02b06f37e {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 10:05:38.42979+00 +00000000-0000-0000-0000-000000000000 04b8022c-7a17-466e-baab-8355226f82f6 {"action":"user_modified","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"user"} 2026-03-27 10:13:27.443108+00 +00000000-0000-0000-0000-000000000000 c59f34c3-6d7c-43b6-8335-5605d7914abb {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 10:52:34.48919+00 +00000000-0000-0000-0000-000000000000 94735b50-0e6e-4c02-8e0a-7ed9fe148337 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 10:52:36.530338+00 +00000000-0000-0000-0000-000000000000 e2b764c8-79d0-4e51-95f0-b980ccf2d799 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:03:42.792326+00 +00000000-0000-0000-0000-000000000000 3e674f0a-99ab-4c1e-81cf-467b04d50628 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:04:08.619676+00 +00000000-0000-0000-0000-000000000000 b2e669ff-00d5-4de8-a7a1-212c058b997e {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:04:18.215073+00 +00000000-0000-0000-0000-000000000000 efd87e91-95e7-4d43-906c-8700b9763e5d {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:04:41.642564+00 +00000000-0000-0000-0000-000000000000 0e3b31f4-ac2c-427b-9d35-3e9888bea099 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:09:33.113539+00 +00000000-0000-0000-0000-000000000000 c66e88bb-5e43-4d64-9941-bf7b87df1f57 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:09:42.050633+00 +00000000-0000-0000-0000-000000000000 f0d83a39-f0b6-411b-99cd-522f82a7c614 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 11:27:39.615562+00 +00000000-0000-0000-0000-000000000000 df8b1dd9-47ac-48a7-a1b0-49d7fb81e65c {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 11:30:42.202869+00 +00000000-0000-0000-0000-000000000000 825d4eb3-8690-4d89-89ee-36235b27fd2b {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 12:24:04.81973+00 +00000000-0000-0000-0000-000000000000 5449ebbf-358c-4daf-941a-071a5aa38352 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 12:24:06.324772+00 +00000000-0000-0000-0000-000000000000 5241335c-6b1e-42cd-b5f0-cf43ae4eac42 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 12:36:58.667642+00 +00000000-0000-0000-0000-000000000000 f77cb484-a8c6-4f12-b203-e08788747c89 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 12:37:01.302944+00 +00000000-0000-0000-0000-000000000000 7f91383b-30cf-4e95-ba47-b901f59133e5 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 12:37:09.603409+00 +00000000-0000-0000-0000-000000000000 de46c96d-1bbd-42c1-82d6-d28eb0cefd38 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 12:37:21.704034+00 +00000000-0000-0000-0000-000000000000 107aa0d2-82b7-4bd7-8672-96a43308cce4 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 13:05:48.781671+00 +00000000-0000-0000-0000-000000000000 cafae40c-1851-459a-8015-bf579bf00dfb {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 13:06:26.132815+00 +00000000-0000-0000-0000-000000000000 a24e3b39-ec62-480e-a8ab-1abe7cf5b6d1 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 14:06:35.696602+00 +00000000-0000-0000-0000-000000000000 a3ec0876-8151-446b-8027-f98fd9caf51b {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 14:06:35.697965+00 +00000000-0000-0000-0000-000000000000 c4557809-e407-495e-80d8-2de06b0c3cf5 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 14:15:40.80509+00 +00000000-0000-0000-0000-000000000000 c12e09c1-2140-4214-b3a6-b3f865941b29 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 14:15:43.617516+00 +00000000-0000-0000-0000-000000000000 fc7e22db-e27e-422e-ac30-93d657d23e78 {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 14:16:38.012591+00 +00000000-0000-0000-0000-000000000000 c4b30fe1-40d5-4c74-aa66-0736bb7a3fb8 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 14:17:04.239061+00 +00000000-0000-0000-0000-000000000000 1608005f-42b8-4860-b87b-0232d72312a6 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 15:19:35.676083+00 +00000000-0000-0000-0000-000000000000 8cf0e884-4e73-47b2-8ae6-6b782ef8ad10 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 15:19:35.678353+00 +00000000-0000-0000-0000-000000000000 f9dac421-76ac-45f4-86ef-8d3e6d5f98fb {"action":"logout","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 15:59:56.791407+00 +00000000-0000-0000-0000-000000000000 33eed8d8-66e0-4b32-9911-9a51a021903b {"action":"login","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 16:00:02.074346+00 +00000000-0000-0000-0000-000000000000 78d20dc8-f2fc-49b7-b0d2-ab87d74f7c29 {"action":"logout","actor_id":"aaaaaaaa-0005-0005-0005-000000000005","actor_username":"clinica3@agenciapsi.com.br","actor_via_sso":false,"log_type":"account"} 2026-03-27 16:00:24.342213+00 +00000000-0000-0000-0000-000000000000 d0bf8715-a43d-43ac-88d9-e3258869de64 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-27 16:00:29.89578+00 +00000000-0000-0000-0000-000000000000 72c8a85f-a799-40d9-983e-3ac69c5b3423 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 16:59:35.640753+00 +00000000-0000-0000-0000-000000000000 b715f1b6-c4e3-4202-9862-4329e4122982 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-27 16:59:35.6428+00 +00000000-0000-0000-0000-000000000000 f77600bd-8300-4dbc-818d-0858ea37f087 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 00:09:26.410887+00 +00000000-0000-0000-0000-000000000000 ffa75206-56e7-44b4-a178-2c30d81d9f9d {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 00:09:26.419786+00 +00000000-0000-0000-0000-000000000000 07f1ae30-3e05-4c31-af31-0a8ee3e5d273 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 10:21:19.707113+00 +00000000-0000-0000-0000-000000000000 9f1825b9-5b7a-438c-88b3-f57e362d28ad {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 10:21:19.710186+00 +00000000-0000-0000-0000-000000000000 303beb7c-43b9-47bc-912b-eb79e60e6dc6 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 11:21:29.053307+00 +00000000-0000-0000-0000-000000000000 2a079185-3947-491f-8e82-f24a0c76296f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 11:21:29.054667+00 +00000000-0000-0000-0000-000000000000 14fb8f67-738e-47de-bbbe-56e7847cd64e {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 12:19:44.588785+00 +00000000-0000-0000-0000-000000000000 84d82180-07d8-479b-bed2-a192d529f215 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 12:19:44.590076+00 +00000000-0000-0000-0000-000000000000 bf82881e-4b5d-4097-825e-36c8521f4dfa {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 13:18:24.943319+00 +00000000-0000-0000-0000-000000000000 c2d62d87-760f-4587-a060-e98b8d8f8ffa {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 13:18:24.944856+00 +00000000-0000-0000-0000-000000000000 6e0891d3-a8c1-48f8-b5c0-322de7d7c5cb {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 16:29:24.12373+00 +00000000-0000-0000-0000-000000000000 9804cc85-8789-471c-898d-1fad349a06b2 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 16:29:24.141016+00 +00000000-0000-0000-0000-000000000000 1eae238f-1b13-4e81-8149-944142ed6d47 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 17:32:18.742735+00 +00000000-0000-0000-0000-000000000000 72e31b31-f40a-4068-8918-0c5b64644945 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 17:32:18.743711+00 +00000000-0000-0000-0000-000000000000 67ca8fca-4037-4e05-a540-41d512d4fccc {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 18:32:18.727376+00 +00000000-0000-0000-0000-000000000000 1fc355c0-9972-483f-adb7-c9d3b8d0b5d4 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 18:32:18.729407+00 +00000000-0000-0000-0000-000000000000 55042c1f-7216-47f9-b0ba-d54e3870c7e5 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 19:32:18.863727+00 +00000000-0000-0000-0000-000000000000 969bd349-4ecd-491b-a3f2-8f65a7d6523f {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 19:32:18.865113+00 +00000000-0000-0000-0000-000000000000 a168aa6b-e162-457b-b5e2-d0a5af4ab940 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 20:32:19.191799+00 +00000000-0000-0000-0000-000000000000 6fb6746b-5b91-4e11-b8f1-c89f002bdddb {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 20:32:19.192779+00 +00000000-0000-0000-0000-000000000000 0784fc43-7924-4a9a-be13-2feb238cdf33 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 21:32:19.266164+00 +00000000-0000-0000-0000-000000000000 3450c0c7-9516-46e5-8ca8-e255c881aa61 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 21:32:19.267431+00 +00000000-0000-0000-0000-000000000000 736c7b14-d238-4efb-a96b-b6eef788b8f8 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 22:32:19.303114+00 +00000000-0000-0000-0000-000000000000 45fc0d26-6476-4bfe-afb2-76696e44ba87 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 22:32:19.304733+00 +00000000-0000-0000-0000-000000000000 41bd0117-bc99-4833-bfad-864cd8b82149 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 23:32:19.338465+00 +00000000-0000-0000-0000-000000000000 e2338140-054b-47aa-87de-2d93b214dd19 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-28 23:32:19.340439+00 +00000000-0000-0000-0000-000000000000 34e12e1c-8c15-47ce-82f8-c2388a927eb7 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-29 11:26:58.456145+00 +00000000-0000-0000-0000-000000000000 afaff300-4e87-4a1d-9132-8b37de1b0fb1 {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-29 11:26:58.462288+00 +00000000-0000-0000-0000-000000000000 27f97d70-943a-4aff-a76f-6aef08880011 {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-29 11:27:04.444496+00 +00000000-0000-0000-0000-000000000000 779cbb08-431d-4aa5-8257-895ea5563c2e {"action":"login","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}} 2026-03-29 11:27:10.843532+00 +00000000-0000-0000-0000-000000000000 ac0230ad-5db1-46bb-86c1-38290ce25ec5 {"action":"token_refreshed","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-29 12:28:22.663176+00 +00000000-0000-0000-0000-000000000000 bb9a5133-34ae-4ddf-a117-6a11f65c4b8c {"action":"token_revoked","actor_id":"aaaaaaaa-0002-0002-0002-000000000002","actor_name":"Leonardo Nohama","actor_username":"terapeuta@agenciapsi.com.br","actor_via_sso":false,"log_type":"token"} 2026-03-29 12:28:22.666638+00 +\. + + +-- +-- Data for Name: flow_state; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.flow_state (id, user_id, auth_code, code_challenge_method, code_challenge, provider_type, provider_access_token, provider_refresh_token, created_at, updated_at, authentication_method, auth_code_issued_at, invite_token, referrer, oauth_client_state_id, linking_target_id, email_optional) FROM stdin; +\. + + +-- +-- Data for Name: identities; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.identities (provider_id, user_id, identity_data, provider, last_sign_in_at, created_at, updated_at, id) FROM stdin; +paciente@agenciapsi.com.br aaaaaaaa-0001-0001-0001-000000000001 {"sub": "aaaaaaaa-0001-0001-0001-000000000001", "email": "paciente@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 977a296d-b7ac-4151-8017-3099808e1f7f +terapeuta@agenciapsi.com.br aaaaaaaa-0002-0002-0002-000000000002 {"sub": "aaaaaaaa-0002-0002-0002-000000000002", "email": "terapeuta@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 44ccd811-6c51-48e9-81a8-77247022c693 +clinica1@agenciapsi.com.br aaaaaaaa-0003-0003-0003-000000000003 {"sub": "aaaaaaaa-0003-0003-0003-000000000003", "email": "clinica1@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 433175a2-4065-47ab-96dd-32508c9a7389 +clinica2@agenciapsi.com.br aaaaaaaa-0004-0004-0004-000000000004 {"sub": "aaaaaaaa-0004-0004-0004-000000000004", "email": "clinica2@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 390305dc-1f61-44e8-9c95-bd9de9591e5a +clinica3@agenciapsi.com.br aaaaaaaa-0005-0005-0005-000000000005 {"sub": "aaaaaaaa-0005-0005-0005-000000000005", "email": "clinica3@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 f669ef52-181e-44d5-8649-833d6b133313 +saas@agenciapsi.com.br aaaaaaaa-0006-0006-0006-000000000006 {"sub": "aaaaaaaa-0006-0006-0006-000000000006", "email": "saas@agenciapsi.com.br", "email_verified": true} email 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 e6af6a5e-55ce-4872-a745-40d688d08982 +supervisor@agenciapsi.com.br aaaaaaaa-0007-0007-0007-000000000007 {"sub": "aaaaaaaa-0007-0007-0007-000000000007", "email": "supervisor@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 8dff38d2-1b09-4b88-9038-28b747358645 +editor@agenciapsi.com.br aaaaaaaa-0008-0008-0008-000000000008 {"sub": "aaaaaaaa-0008-0008-0008-000000000008", "email": "editor@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 a7856eb7-e96f-4752-84a3-b676503b74ae +therapist2@agenciapsi.com.br aaaaaaaa-0009-0009-0009-000000000009 {"sub": "aaaaaaaa-0009-0009-0009-000000000009", "email": "therapist2@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 8129cc38-db24-4f84-b48e-bf0b46235811 +therapist3@agenciapsi.com.br aaaaaaaa-0010-0010-0010-000000000010 {"sub": "aaaaaaaa-0010-0010-0010-000000000010", "email": "therapist3@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 754b015d-3c88-4104-b246-eede6fffabba +secretary@agenciapsi.com.br aaaaaaaa-0011-0011-0011-000000000011 {"sub": "aaaaaaaa-0011-0011-0011-000000000011", "email": "secretary@agenciapsi.com.br", "email_verified": true} email 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 a7aa272b-659f-4bc7-8667-10cf49200410 +384a69d8-b7cd-40ac-9d3c-764c93532b66 384a69d8-b7cd-40ac-9d3c-764c93532b66 {"sub": "384a69d8-b7cd-40ac-9d3c-764c93532b66", "email": "terapeuta2@agenciapsi.com.br", "email_verified": false, "phone_verified": false} email 2026-03-26 19:47:30.489663+00 2026-03-26 19:47:30.489705+00 2026-03-26 19:47:30.489705+00 7e35a3f9-29d8-4f8d-bdfa-5bfe1328293f +\. + + +-- +-- Data for Name: instances; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.instances (id, uuid, raw_base_config, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: mfa_amr_claims; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.mfa_amr_claims (session_id, created_at, updated_at, authentication_method, id) FROM stdin; +2dc73205-6913-432b-8eb7-e8b609e5f0f4 2026-03-27 16:00:29.920845+00 2026-03-27 16:00:29.920845+00 password eb1238d8-fd33-4b72-b879-46e82e0abe4c +3b2b06a2-19d1-4bd6-9cc5-7e0903adfbec 2026-03-29 11:27:04.463145+00 2026-03-29 11:27:04.463145+00 password fb84282b-14d2-4b24-b5f8-00892a474c71 +5af1b6d5-ae69-4eaa-b951-34e5ee9df49a 2026-03-29 11:27:10.851292+00 2026-03-29 11:27:10.851292+00 password 873191aa-89fe-418c-b1d0-36a6e01d91fa +\. + + +-- +-- Data for Name: mfa_challenges; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.mfa_challenges (id, factor_id, created_at, verified_at, ip_address, otp_code, web_authn_session_data) FROM stdin; +\. + + +-- +-- Data for Name: mfa_factors; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.mfa_factors (id, user_id, friendly_name, factor_type, status, created_at, updated_at, secret, phone, last_challenged_at, web_authn_credential, web_authn_aaguid, last_webauthn_challenge_data) FROM stdin; +\. + + +-- +-- Data for Name: oauth_authorizations; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_authorizations (id, authorization_id, client_id, user_id, redirect_uri, scope, state, resource, code_challenge, code_challenge_method, response_type, status, authorization_code, created_at, expires_at, approved_at, nonce) FROM stdin; +\. + + +-- +-- Data for Name: oauth_client_states; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_client_states (id, provider_type, code_verifier, created_at) FROM stdin; +\. + + +-- +-- Data for Name: oauth_clients; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_clients (id, client_secret_hash, registration_type, redirect_uris, grant_types, client_name, client_uri, logo_uri, created_at, updated_at, deleted_at, client_type, token_endpoint_auth_method) FROM stdin; +\. + + +-- +-- Data for Name: oauth_consents; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.oauth_consents (id, user_id, client_id, scopes, granted_at, revoked_at) FROM stdin; +\. + + +-- +-- Data for Name: one_time_tokens; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.one_time_tokens (id, user_id, token_type, token_hash, relates_to, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: refresh_tokens; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.refresh_tokens (instance_id, id, token, user_id, revoked, created_at, updated_at, parent, session_id) FROM stdin; +00000000-0000-0000-0000-000000000000 137 ow4odpbr72tv aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-27 16:00:29.902122+00 2026-03-27 16:59:35.643784+00 \N 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 138 o3r5wxkohiyw aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-27 16:59:35.646788+00 2026-03-28 00:09:26.422611+00 ow4odpbr72tv 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 139 kfkyr35zoc3h aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 00:09:26.424171+00 2026-03-28 10:21:19.710939+00 o3r5wxkohiyw 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 140 kuezvais3wpo aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 10:21:19.711933+00 2026-03-28 11:21:29.055573+00 kfkyr35zoc3h 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 141 z5x6eax4igbd aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 11:21:29.056815+00 2026-03-28 12:19:44.591001+00 kuezvais3wpo 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 142 ske3i45rhxno aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 12:19:44.59206+00 2026-03-28 13:18:24.94649+00 z5x6eax4igbd 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 143 j2kgfyyvyzoz aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 13:18:24.947764+00 2026-03-28 16:29:24.142032+00 ske3i45rhxno 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 144 lbfbw2cfblg6 aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 16:29:24.143185+00 2026-03-28 17:32:18.744294+00 j2kgfyyvyzoz 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 145 5hxaedb3akrf aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 17:32:18.745103+00 2026-03-28 18:32:18.730707+00 lbfbw2cfblg6 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 146 mfcvxvsspo6x aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 18:32:18.73203+00 2026-03-28 19:32:18.86568+00 5hxaedb3akrf 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 147 7y34dduriqt7 aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 19:32:18.866633+00 2026-03-28 20:32:19.19363+00 mfcvxvsspo6x 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 148 eu4ulwavwkgg aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 20:32:19.194909+00 2026-03-28 21:32:19.268258+00 7y34dduriqt7 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 149 eld7gsmd4hv6 aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 21:32:19.269488+00 2026-03-28 22:32:19.307119+00 eu4ulwavwkgg 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 150 bjkdbypnep24 aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 22:32:19.308462+00 2026-03-28 23:32:19.341287+00 eld7gsmd4hv6 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 151 p5yygguwwdiy aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-28 23:32:19.342438+00 2026-03-29 11:26:58.46342+00 bjkdbypnep24 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 152 mly6ofb2hilw aaaaaaaa-0002-0002-0002-000000000002 f 2026-03-29 11:26:58.464478+00 2026-03-29 11:26:58.464478+00 p5yygguwwdiy 2dc73205-6913-432b-8eb7-e8b609e5f0f4 +00000000-0000-0000-0000-000000000000 153 aeam4sb4pg3c aaaaaaaa-0002-0002-0002-000000000002 f 2026-03-29 11:27:04.459198+00 2026-03-29 11:27:04.459198+00 \N 3b2b06a2-19d1-4bd6-9cc5-7e0903adfbec +00000000-0000-0000-0000-000000000000 154 ipmk2xa65emx aaaaaaaa-0002-0002-0002-000000000002 t 2026-03-29 11:27:10.848209+00 2026-03-29 12:28:22.670434+00 \N 5af1b6d5-ae69-4eaa-b951-34e5ee9df49a +00000000-0000-0000-0000-000000000000 155 snr5vebtnvai aaaaaaaa-0002-0002-0002-000000000002 f 2026-03-29 12:28:22.674079+00 2026-03-29 12:28:22.674079+00 ipmk2xa65emx 5af1b6d5-ae69-4eaa-b951-34e5ee9df49a +\. + + +-- +-- Data for Name: saml_providers; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.saml_providers (id, sso_provider_id, entity_id, metadata_xml, metadata_url, attribute_mapping, created_at, updated_at, name_id_format) FROM stdin; +\. + + +-- +-- Data for Name: saml_relay_states; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.saml_relay_states (id, sso_provider_id, request_id, for_email, redirect_to, created_at, updated_at, flow_state_id) FROM stdin; +\. + + +-- +-- Data for Name: schema_migrations; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.schema_migrations (version) FROM stdin; +20171026211738 +20171026211808 +20171026211834 +20180103212743 +20180108183307 +20180119214651 +20180125194653 +00 +20210710035447 +20210722035447 +20210730183235 +20210909172000 +20210927181326 +20211122151130 +20211124214934 +20211202183645 +20220114185221 +20220114185340 +20220224000811 +20220323170000 +20220429102000 +20220531120530 +20220614074223 +20220811173540 +20221003041349 +20221003041400 +20221011041400 +20221020193600 +20221021073300 +20221021082433 +20221027105023 +20221114143122 +20221114143410 +20221125140132 +20221208132122 +20221215195500 +20221215195800 +20221215195900 +20230116124310 +20230116124412 +20230131181311 +20230322519590 +20230402418590 +20230411005111 +20230508135423 +20230523124323 +20230818113222 +20230914180801 +20231027141322 +20231114161723 +20231117164230 +20240115144230 +20240214120130 +20240306115329 +20240314092811 +20240427152123 +20240612123726 +20240729123726 +20240802193726 +20240806073726 +20241009103726 +20250717082212 +20250731150234 +20250804100000 +20250901200500 +20250903112500 +20250904133000 +20250925093508 +20251007112900 +20251104100000 +20251111201300 +20251201000000 +20260115000000 +20260121000000 +\. + + +-- +-- Data for Name: sessions; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.sessions (id, user_id, created_at, updated_at, factor_id, aal, not_after, refreshed_at, user_agent, ip, tag, oauth_client_id, refresh_token_hmac_key, refresh_token_counter, scopes) FROM stdin; +2dc73205-6913-432b-8eb7-e8b609e5f0f4 aaaaaaaa-0002-0002-0002-000000000002 2026-03-27 16:00:29.897964+00 2026-03-29 11:26:58.474232+00 \N aal1 \N 2026-03-29 11:26:58.474151 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 172.19.0.1 \N \N \N \N \N +3b2b06a2-19d1-4bd6-9cc5-7e0903adfbec aaaaaaaa-0002-0002-0002-000000000002 2026-03-29 11:27:04.446516+00 2026-03-29 11:27:04.446516+00 \N aal1 \N \N Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 172.19.0.1 \N \N \N \N \N +5af1b6d5-ae69-4eaa-b951-34e5ee9df49a aaaaaaaa-0002-0002-0002-000000000002 2026-03-29 11:27:10.845343+00 2026-03-29 12:28:22.680165+00 \N aal1 \N 2026-03-29 12:28:22.680085 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 172.19.0.1 \N \N \N \N \N +\. + + +-- +-- Data for Name: sso_domains; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.sso_domains (id, sso_provider_id, domain, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: sso_providers; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.sso_providers (id, resource_id, created_at, updated_at, disabled) FROM stdin; +\. + + +-- +-- Data for Name: users; Type: TABLE DATA; Schema: auth; Owner: - +-- + +COPY auth.users (instance_id, id, aud, role, email, encrypted_password, email_confirmed_at, invited_at, confirmation_token, confirmation_sent_at, recovery_token, recovery_sent_at, email_change_token_new, email_change, email_change_sent_at, last_sign_in_at, raw_app_meta_data, raw_user_meta_data, is_super_admin, created_at, updated_at, phone, phone_confirmed_at, phone_change, phone_change_token, phone_change_sent_at, email_change_token_current, email_change_confirm_status, banned_until, reauthentication_token, reauthentication_sent_at, is_sso_user, deleted_at, is_anonymous) FROM stdin; +00000000-0000-0000-0000-000000000000 aaaaaaaa-0001-0001-0001-000000000001 authenticated authenticated paciente@agenciapsi.com.br $2a$06$ipEc7puaVhQnpusOGhAYgOcrVHq4RnqZeDooS8FaehzHhueScf9S. 2026-03-23 10:46:29.876072+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Ana Paciente"} \N 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0005-0005-0005-000000000005 authenticated authenticated clinica3@agenciapsi.com.br $2a$06$L3aFykCSdduzTHEKsEQ3q.GdHTb5EJBvbIit4k7ZgnbRd5BCGuTxu 2026-03-23 10:46:29.876072+00 \N \N \N \N 2026-03-27 16:00:02.080329+00 {"provider": "email", "providers": ["email"]} {"name": "Clínica Bem Estar"} \N 2026-03-23 10:46:29.876072+00 2026-03-27 16:00:02.084768+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0011-0011-0011-000000000011 authenticated authenticated secretary@agenciapsi.com.br $2a$06$O7HeygRYgJViriMFCImLZu7DD.3A9wZWb9y3c5G2PIURgJ65UnqT. 2026-03-23 14:18:06.087973+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Gabriela Secretária"} \N 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0002-0002-0002-000000000002 authenticated authenticated terapeuta@agenciapsi.com.br $2a$06$CztXijQkaPZa6pUwXmMHWuzSF19GiVtRBdMLp.k4iWf7ftGWNBIg6 2026-03-23 10:46:29.876072+00 \N \N \N \N 2026-03-29 11:27:10.845265+00 {"provider": "email", "providers": ["email"]} {"name": "Bruno Terapeuta", "full_name": "Leonardo Nohama", "avatar_url": "http://127.0.0.1:54321/storage/v1/object/public/avatars/aaaaaaaa-0002-0002-0002-000000000002/avatar.jpg"} \N 2026-03-23 10:46:29.876072+00 2026-03-29 12:28:22.676402+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 384a69d8-b7cd-40ac-9d3c-764c93532b66 authenticated authenticated terapeuta2@agenciapsi.com.br $2a$10$MBE/uZcT1lpKira6nsTY6OzrUabKwtOrm.QvJzdy.IU95tiX3M2ia 2026-03-26 19:47:30.496218+00 \N \N \N \N 2026-03-26 19:48:44.253788+00 {"provider": "email", "providers": ["email"]} {"sub": "384a69d8-b7cd-40ac-9d3c-764c93532b66", "email": "terapeuta2@agenciapsi.com.br", "email_verified": true, "phone_verified": false} \N 2026-03-26 19:47:30.478057+00 2026-03-26 19:48:44.258995+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0007-0007-0007-000000000007 authenticated authenticated supervisor@agenciapsi.com.br $2a$06$.kF47/tagPNwSpgGM4ryZOu01L0ykU2IXakM8trZ.Hon1TTUDeqYK 2026-03-23 14:18:05.215881+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Carlos Supervisor"} \N 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0009-0009-0009-000000000009 authenticated authenticated therapist2@agenciapsi.com.br $2a$06$16hf/nUbN0lElm9l8vQI4ek8vIM2T8ymiJTQ8CHXXw/jD1gMuDFJS 2026-03-23 14:18:06.087973+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Eva Terapeuta"} \N 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0010-0010-0010-000000000010 authenticated authenticated therapist3@agenciapsi.com.br $2a$06$sBJPPHRI/MsrCTEeCK5/vOhASc/SNLeO.B/QEE2MZNWEP8FamyCXW 2026-03-23 14:18:06.087973+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Felipe Terapeuta"} \N 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0003-0003-0003-000000000003 authenticated authenticated clinica1@agenciapsi.com.br $2a$06$cxZ2uXWIOS9MgzoyzSla8Oocid6wKtEBPA4k9QyC8DvwzmOsI0co2 2026-03-23 10:46:29.876072+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Clínica Espaço Psi"} \N 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0004-0004-0004-000000000004 authenticated authenticated clinica2@agenciapsi.com.br $2a$06$ZSW6FPPCmhO8EkfSM4/QLu/J32HRe/87zoNLvPtCbqTdNBbanaLPi 2026-03-23 10:46:29.876072+00 \N \N \N \N \N {"provider": "email", "providers": ["email"]} {"name": "Clínica Mente Sã"} \N 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0008-0008-0008-000000000008 authenticated authenticated editor@agenciapsi.com.br $2a$06$lcF3sQOKaQOMwo5OTPPpcODcMtjDoUpHw3rOBhJMYow15LoJFLvH6 2026-03-23 14:18:05.215881+00 \N \N \N \N 2026-03-26 19:46:29.070205+00 {"provider": "email", "providers": ["email"]} {"name": "Diana Editora"} \N 2026-03-23 14:18:05.215881+00 2026-03-26 19:46:29.07828+00 \N \N \N 0 \N \N f \N f +00000000-0000-0000-0000-000000000000 aaaaaaaa-0006-0006-0006-000000000006 authenticated authenticated saas@agenciapsi.com.br $2a$06$QsvWGUd7HQTv6kSQDbRsiOkNcLM4O2BnQflXPbx3MK9E4RPGz5FvS 2026-03-23 10:46:29.876072+00 \N \N \N \N 2026-03-26 19:46:43.303588+00 {"provider": "email", "providers": ["email"]} {"name": "Admin Plataforma"} \N 2026-03-23 10:46:29.876072+00 2026-03-26 19:46:43.308732+00 \N \N \N 0 \N \N f \N f +\. + + +-- +-- Data for Name: job; Type: TABLE DATA; Schema: cron; Owner: - +-- + +COPY cron.job (jobid, schedule, command, nodename, nodeport, database, username, active, jobname) FROM stdin; +\. + + +-- +-- Data for Name: job_run_details; Type: TABLE DATA; Schema: cron; Owner: - +-- + +COPY cron.job_run_details (jobid, runid, job_pid, database, username, command, status, return_message, start_time, end_time) FROM stdin; +\. + + +-- +-- Data for Name: _db_migrations; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public._db_migrations (id, filename, hash, category, applied_at) FROM stdin; +1 seed_001_fixed.sql 87fc24517f6446f7 seed 2026-03-23 14:15:02.14603+00 +2 seed_002.sql b05d565b35c97300 seed 2026-03-23 14:15:02.45035+00 +3 seed_003.sql 257ef8bba4e319a2 seed 2026-03-23 14:15:02.755322+00 +4 seed_010_plans.sql 0de612f2301e27d3 seed 2026-03-23 14:15:02.974622+00 +5 seed_011_features.sql e7326ac0e33e4fee seed 2026-03-23 14:15:03.261589+00 +6 seed_012_plan_features.sql f0e1b4ab684383f7 seed 2026-03-23 14:15:03.553899+00 +7 seed_013_subscriptions.sql b61e4af59262f3ac seed 2026-03-23 14:15:03.816997+00 +8 seed_014_global_data.sql a7bc086bc6f052ee seed 2026-03-23 14:15:04.080095+00 +9 fix_addon_credits_fk.sql aaff13facb98e4d8 fix 2026-03-23 14:15:04.372331+00 +10 fix_addon_rls_saas_admin.sql 84d85284eb441afc fix 2026-03-23 14:15:04.630692+00 +11 fix_missing_subscriptions.sql f5740f6eef9e5405 fix 2026-03-23 14:15:04.916745+00 +12 fix_notification_templates_rls_admin.sql ede371cbce54e13e fix 2026-03-23 14:15:05.15764+00 +13 fix_seed_patient_groups.sql e9b870ba0ad5f359 fix 2026-03-23 14:15:05.485322+00 +14 fix_subscriptions_validate_scope.sql c814a90d768d339c fix 2026-03-23 14:15:06.461275+00 +15 fix_template_keys_match_populate.sql e0fdd2a420abaeb8 fix 2026-03-23 14:15:06.977464+00 +\. + + +-- +-- Data for Name: addon_credits; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.addon_credits (id, tenant_id, owner_id, addon_type, balance, total_purchased, total_consumed, low_balance_threshold, low_balance_notified, daily_limit, hourly_limit, daily_used, hourly_used, daily_reset_at, hourly_reset_at, from_number_override, expires_at, is_active, created_at, updated_at) FROM stdin; +0f2ed178-d2d1-4bf0-a58c-206be0183c1c bbbbbbbb-0002-0002-0002-000000000002 \N sms 320 320 0 10 f \N \N 0 0 \N \N \N \N t 2026-03-25 00:15:28.741153+00 2026-03-25 00:15:37.596479+00 +\. + + +-- +-- Data for Name: addon_products; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.addon_products (id, slug, name, description, addon_type, icon, credits_amount, price_cents, currency, is_active, is_visible, sort_order, metadata, created_at, updated_at, deleted_at) FROM stdin; +a6cfa47f-e26d-4835-9c14-4629e8afa331 sms_basico SMS Básico 100 créditos SMS. Ideal para quem está começando ou tem poucos pacientes. sms pi pi-comment 100 2500 BRL t t 10 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +56d95e40-9e32-491d-969d-8970f51c05ed sms_essencial SMS Essencial 220 créditos SMS. Para consultórios em crescimento com envio regular de lembretes. sms pi pi-comments 220 5000 BRL t t 20 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +7589b9fc-2ccf-43f0-aea4-abc3edf05473 sms_profissional SMS Profissional 350 créditos SMS. Para quem envia lembretes, confirmações e avisos de cobrança. sms pi pi-send 350 7500 BRL t t 30 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +2dff4229-32a8-4090-8f19-6c3ab836b9c1 sms_premium SMS Premium 500 créditos SMS. Melhor custo-benefício. Clínicas e agenda cheia. sms pi pi-star 500 10000 BRL t t 40 {} 2026-03-25 00:15:11.612013+00 2026-03-25 00:15:11.612013+00 \N +\. + + +-- +-- Data for Name: addon_transactions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.addon_transactions (id, tenant_id, owner_id, addon_type, type, amount, balance_before, balance_after, product_id, queue_id, description, admin_user_id, payment_method, payment_reference, price_cents, currency, created_at, metadata) FROM stdin; +46181c84-9f01-4fec-baaf-c10aa935f240 bbbbbbbb-0002-0002-0002-000000000002 \N sms purchase 100 0 100 a6cfa47f-e26d-4835-9c14-4629e8afa331 \N SMS Básico aaaaaaaa-0006-0006-0006-000000000006 manual \N 2500 BRL 2026-03-25 00:15:28.741153+00 {} +a07a56b5-1b6d-46a1-88fd-15623c77f07f bbbbbbbb-0002-0002-0002-000000000002 \N sms purchase 220 100 320 56d95e40-9e32-491d-969d-8970f51c05ed \N SMS Essencial aaaaaaaa-0006-0006-0006-000000000006 manual \N 5000 BRL 2026-03-25 00:15:37.596479+00 {} +\. + + +-- +-- Data for Name: agenda_bloqueios; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_bloqueios (id, owner_id, tenant_id, tipo, titulo, data_inicio, data_fim, hora_inicio, hora_fim, recorrente, dia_semana, observacao, origem, created_at) FROM stdin; +f809fcb6-0369-42fd-985a-5d9976798e88 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 bloqueio Feriado: Sexta-feira Santa 2026-04-03 2026-04-03 \N \N f \N \N agenda_feriado 2026-03-23 11:32:18.225845+00 +\. + + +-- +-- Data for Name: agenda_configuracoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_configuracoes (owner_id, duracao_padrao_minutos, intervalo_padrao_minutos, timezone, usar_horario_admin_custom, admin_inicio_visualizacao, admin_fim_visualizacao, admin_slot_visual_minutos, online_ativo, online_min_antecedencia_horas, online_max_dias_futuro, online_cancelar_ate_horas, online_reagendar_ate_horas, online_limite_agendamentos_futuros, online_modo, online_buffer_antes_min, online_buffer_depois_min, online_modalidade, created_at, updated_at, usar_granularidade_custom, granularidade_min, setup_concluido, setup_concluido_em, agenda_view_mode, agenda_custom_start, agenda_custom_end, session_duration_min, session_break_min, pausas_semanais, setup_clinica_concluido, setup_clinica_concluido_em, tenant_id, jornada_igual_todos, slot_mode, atendimento_mode) FROM stdin; +aaaaaaaa-0005-0005-0005-000000000005 50 0 America/Sao_Paulo f \N \N 30 f 24 60 12 12 1 automatico 0 0 ambos 2026-03-25 18:35:26.138564+00 2026-03-26 17:19:10.818241+00 f \N t 2026-03-26 17:19:10.804+00 full_24h \N \N 40 10 [{"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 0}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 1}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 2}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 3}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 4}, {"id": "52ca536da82b919d29a33b81", "fim": "14:00", "label": "Almoço", "inicio": "13:00", "dia_semana": 5}] f \N bbbbbbbb-0005-0005-0005-000000000005 t fixed ambos +aaaaaaaa-0002-0002-0002-000000000002 50 0 America/Sao_Paulo f \N \N 30 f 24 60 12 12 1 automatico 0 0 ambos 2026-03-23 10:58:31.422764+00 2026-03-27 14:18:31.232768+00 f \N t 2026-03-27 14:18:31.221+00 full_24h \N \N 50 10 [{"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 0}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 1}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 2}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 3}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 4}, {"id": "64aae49bb44ef819d1a76b459", "fim": "13:00", "label": "Almoço", "inicio": "12:00", "dia_semana": 5}] f \N bbbbbbbb-0002-0002-0002-000000000002 t fixed ambos +\. + + +-- +-- Data for Name: agenda_eventos; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_eventos (id, owner_id, tipo, status, titulo, observacoes, inicio_em, fim_em, created_at, updated_at, terapeuta_id, tenant_id, visibility_scope, mirror_of_event_id, mirror_source, patient_id, determined_commitment_id, link_online, titulo_custom, extra_fields, recurrence_id, recurrence_date, modalidade, price, billing_contract_id, billed, services_customized, insurance_plan_id, insurance_guide_number, insurance_value, insurance_plan_service_id) FROM stdin; +dbf6b44e-24f1-4cd2-86f5-a594fa39f9d3 aaaaaaaa-0002-0002-0002-000000000002 sessao agendado Otto Rank [Sess??o] \N 2026-03-23 13:00:00+00 2026-03-23 13:50:00+00 2026-03-23 11:33:08.0471+00 2026-03-23 11:33:08.0471+00 \N bbbbbbbb-0002-0002-0002-000000000002 public \N \N 6449e64b-050b-419f-8845-029b6f10a17d 42a8c681-8b32-4608-870f-b617acbe249e \N \N \N \N \N presencial 0.00 \N f f \N \N \N \N +\. + + +-- +-- Data for Name: agenda_excecoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_excecoes (id, owner_id, data, hora_inicio, hora_fim, tipo, motivo, created_at, updated_at, status, fonte, aplicavel_online, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agenda_online_slots; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_online_slots (id, owner_id, weekday, "time", enabled, created_at, updated_at, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agenda_regras_semanais; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_regras_semanais (id, owner_id, dia_semana, hora_inicio, hora_fim, modalidade, ativo, created_at, updated_at, tenant_id) FROM stdin; +a3212bcd-fc80-40c3-b3ea-92d31d1d051a aaaaaaaa-0002-0002-0002-000000000002 0 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +cbeae6bd-1ebd-494d-b5c0-2f3a8bf09e14 aaaaaaaa-0002-0002-0002-000000000002 1 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +dd9ecded-d82c-4d87-a2df-85b89c331001 aaaaaaaa-0002-0002-0002-000000000002 2 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +ce5054c2-f382-4135-8365-06b3dab7ea1c aaaaaaaa-0002-0002-0002-000000000002 3 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +e8e22a8d-4deb-4eeb-a9e8-e1f906030499 aaaaaaaa-0002-0002-0002-000000000002 4 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +265ff3ab-645f-4dd6-8a91-4a3a4d085ff0 aaaaaaaa-0002-0002-0002-000000000002 5 08:00:00 18:00:00 ambos t 2026-03-23 11:31:24.019622+00 2026-03-23 11:31:24.019622+00 bbbbbbbb-0002-0002-0002-000000000002 +cf21b2ed-fbf6-4899-95b6-bee502e7b139 aaaaaaaa-0005-0005-0005-000000000005 0 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +a6fc9552-59dd-4717-9282-129d3297b5e0 aaaaaaaa-0005-0005-0005-000000000005 1 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +c028ea38-75a0-4e5f-a6c5-705264f43826 aaaaaaaa-0005-0005-0005-000000000005 2 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +c674e740-90b9-4ae1-8b9b-56ee6342235e aaaaaaaa-0005-0005-0005-000000000005 3 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +a272ac19-32d2-4c3c-b1a1-fbdedfffc6d3 aaaaaaaa-0005-0005-0005-000000000005 4 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +1a98514b-8508-4539-9e96-6af89b91417d aaaaaaaa-0005-0005-0005-000000000005 5 08:00:00 18:00:00 ambos t 2026-03-26 10:14:17.66352+00 2026-03-26 10:14:17.66352+00 bbbbbbbb-0005-0005-0005-000000000005 +\. + + +-- +-- Data for Name: agenda_slots_bloqueados_semanais; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_slots_bloqueados_semanais (id, owner_id, dia_semana, hora_inicio, motivo, ativo, created_at, updated_at, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agenda_slots_regras; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agenda_slots_regras (id, owner_id, dia_semana, passo_minutos, offset_minutos, buffer_antes_min, buffer_depois_min, min_antecedencia_horas, ativo, created_at, updated_at, tenant_id) FROM stdin; +\. + + +-- +-- Data for Name: agendador_configuracoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agendador_configuracoes (owner_id, tenant_id, ativo, link_slug, imagem_fundo_url, imagem_header_url, logomarca_url, cor_primaria, nome_exibicao, endereco, botao_como_chegar_ativo, maps_url, modo_aprovacao, modalidade, tipos_habilitados, duracao_sessao_min, antecedencia_minima_horas, prazo_resposta_horas, reserva_horas, pagamento_obrigatorio, pix_chave, pix_countdown_minutos, triagem_motivo, triagem_como_conheceu, verificacao_email, exigir_aceite_lgpd, mensagem_boas_vindas, texto_como_se_preparar, texto_termos_lgpd, created_at, updated_at, pagamento_modo, pagamento_metodos_visiveis) FROM stdin; +\. + + +-- +-- Data for Name: agendador_solicitacoes; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.agendador_solicitacoes (id, owner_id, tenant_id, paciente_nome, paciente_sobrenome, paciente_email, paciente_celular, paciente_cpf, tipo, modalidade, data_solicitada, hora_solicitada, reservado_ate, motivo, como_conheceu, pix_status, pix_pago_em, status, recusado_motivo, autorizado_em, autorizado_por, user_id, patient_id, evento_id, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: billing_contracts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.billing_contracts (id, owner_id, tenant_id, patient_id, type, total_sessions, sessions_used, package_price, amount, billing_interval, active_from, active_to, status, created_at) FROM stdin; +\. + + +-- +-- Data for Name: commitment_services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.commitment_services (id, commitment_id, service_id, quantity, unit_price, discount_pct, discount_flat, final_price, created_at) FROM stdin; +\. + + +-- +-- Data for Name: commitment_time_logs; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.commitment_time_logs (id, tenant_id, commitment_id, calendar_event_id, source, started_at, ended_at, minutes, created_by, created_at) FROM stdin; +\. + + +-- +-- Data for Name: company_profiles; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.company_profiles (id, tenant_id, nome_fantasia, razao_social, tipo_empresa, cnpj, ie, im, cep, logradouro, numero, complemento, bairro, cidade, estado, email, telefone, site, logo_url, redes_sociais, created_at, updated_at) FROM stdin; +9c2039d4-0058-46d0-b60f-29e16459bb85 bbbbbbbb-0002-0002-0002-000000000002 teste \N consultorio \N \N \N 13561-260 Avenida Tancredo de Almeida Neves 457 complemento Parque Santa Mônica São Carlos SP comercial@gmail.com (11) 11111-1111 site http://127.0.0.1:54321/storage/v1/object/public/logos/bbbbbbbb-0002-0002-0002-000000000002/logo.png [{"url": "@perfil", "name": "Instagram"}] 2026-03-27 12:35:48.759848+00 2026-03-27 14:18:20.352667+00 +\. + + +-- +-- Data for Name: determined_commitment_fields; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.determined_commitment_fields (id, tenant_id, commitment_id, key, label, field_type, required, sort_order, created_at, updated_at) FROM stdin; +3f45731e-f025-4a95-b37d-35becf557474 bbbbbbbb-0002-0002-0002-000000000002 5944e5d9-622d-4db1-b73d-0083a6a4a9a1 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +7cb99c51-3de2-4216-be29-1067d1d2a6e8 bbbbbbbb-0002-0002-0002-000000000002 5944e5d9-622d-4db1-b73d-0083a6a4a9a1 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +29a48e4d-b4b9-41e6-8a3f-23e862b11f71 bbbbbbbb-0002-0002-0002-000000000002 5944e5d9-622d-4db1-b73d-0083a6a4a9a1 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +de1bf25c-2c6e-4de1-9be2-9976eb26e9e6 bbbbbbbb-0002-0002-0002-000000000002 717e552c-99cc-4375-b7f2-a1b29f3581e3 supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +0a2f4c96-be82-49a1-b0bc-65d08bf29078 bbbbbbbb-0002-0002-0002-000000000002 717e552c-99cc-4375-b7f2-a1b29f3581e3 topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +ea018b37-ac9a-49a5-93dd-ebc4f2bcc069 bbbbbbbb-0002-0002-0002-000000000002 717e552c-99cc-4375-b7f2-a1b29f3581e3 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4f4fff30-8b01-46cc-a6c9-25e0c623b03c bbbbbbbb-0002-0002-0002-000000000002 b3bf589d-4a72-429f-b9bd-ba426c3f42f1 theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +3ddb7185-eea6-43e4-bab7-686aa996f4e6 bbbbbbbb-0002-0002-0002-000000000002 b3bf589d-4a72-429f-b9bd-ba426c3f42f1 group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +20c208a1-eae7-4021-916f-a3a45f7d9e84 bbbbbbbb-0002-0002-0002-000000000002 b3bf589d-4a72-429f-b9bd-ba426c3f42f1 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +82c2b726-8de7-4bdd-a7ab-0fe519200f2c bbbbbbbb-0002-0002-0002-000000000002 e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +88bb598e-44f1-4090-a09a-21a11e628ab0 bbbbbbbb-0002-0002-0002-000000000002 e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +f0aa7c4c-e26d-42c2-8f90-5e4a87308fe9 bbbbbbbb-0002-0002-0002-000000000002 e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +fa8083ef-7e1f-4e00-84b6-6dd197a31e11 bbbbbbbb-0003-0003-0003-000000000003 cd1076b1-0f3d-460e-842f-1585c0ae2a64 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +2fea846d-3d7f-4946-8b8a-921756d1375d bbbbbbbb-0003-0003-0003-000000000003 cd1076b1-0f3d-460e-842f-1585c0ae2a64 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +7f71d073-7337-40c3-9f2b-80d359ee6825 bbbbbbbb-0003-0003-0003-000000000003 cd1076b1-0f3d-460e-842f-1585c0ae2a64 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +9ea25942-9529-4ffb-99b1-17c8f48fa1b3 bbbbbbbb-0003-0003-0003-000000000003 9ded91f1-1974-4e56-afc6-12b2e8f9456c supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +642ee453-276b-4636-808b-0deed8827416 bbbbbbbb-0003-0003-0003-000000000003 9ded91f1-1974-4e56-afc6-12b2e8f9456c topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +1107588b-dafc-49f0-adf1-1d478410fbab bbbbbbbb-0003-0003-0003-000000000003 9ded91f1-1974-4e56-afc6-12b2e8f9456c notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +2d00519b-2d66-4b66-9471-4edfd7f03152 bbbbbbbb-0003-0003-0003-000000000003 b82819d4-02a1-40c5-bf57-6e2e0425e62d theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +6fb9b8f8-0a50-4267-93ff-a154487d0ee2 bbbbbbbb-0003-0003-0003-000000000003 b82819d4-02a1-40c5-bf57-6e2e0425e62d group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +99d0df7d-20f7-49df-acd8-c359b195348d bbbbbbbb-0003-0003-0003-000000000003 b82819d4-02a1-40c5-bf57-6e2e0425e62d notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +241c3b0f-78c1-4485-9f11-c8fc5fdf6650 bbbbbbbb-0003-0003-0003-000000000003 b5c43dc2-a3a1-40ca-8b75-02ec649fd914 analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +08440a39-027f-4879-8091-7a9b2cf522c1 bbbbbbbb-0003-0003-0003-000000000003 b5c43dc2-a3a1-40ca-8b75-02ec649fd914 focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +d11f4029-c46e-42bf-b7aa-bc3b1cc9874a bbbbbbbb-0003-0003-0003-000000000003 b5c43dc2-a3a1-40ca-8b75-02ec649fd914 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +d2d11b9f-5dcf-4355-bc33-c977ac7591a9 bbbbbbbb-0004-0004-0004-000000000004 2e5f4151-e259-4261-8954-e3d196a28383 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +2d3e3c6e-6ca0-4170-b9ed-e45e470a8738 bbbbbbbb-0004-0004-0004-000000000004 2e5f4151-e259-4261-8954-e3d196a28383 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +a7003c31-46b1-44a2-876e-034f3c61ce8c bbbbbbbb-0004-0004-0004-000000000004 2e5f4151-e259-4261-8954-e3d196a28383 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +29f2cb01-22c2-45c3-ba02-55b908b5ceac bbbbbbbb-0004-0004-0004-000000000004 0d6cb161-8d89-4b18-9323-5631e8cddd8f supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +e0b8f4dd-740c-4162-b988-62fc8216de8c bbbbbbbb-0004-0004-0004-000000000004 0d6cb161-8d89-4b18-9323-5631e8cddd8f topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +187902e7-4497-4ab6-81f7-7dbe177189e9 bbbbbbbb-0004-0004-0004-000000000004 0d6cb161-8d89-4b18-9323-5631e8cddd8f notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4ef4ac8e-adf4-4034-851f-3767ef37a8e4 bbbbbbbb-0004-0004-0004-000000000004 bec193c2-172b-40ef-a5fe-91b7c44c841e theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4972b76d-c080-4989-a4f8-3d3c57a9b96e bbbbbbbb-0004-0004-0004-000000000004 bec193c2-172b-40ef-a5fe-91b7c44c841e group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +fc589056-f8b8-4d96-94f0-7ac49ab53059 bbbbbbbb-0004-0004-0004-000000000004 bec193c2-172b-40ef-a5fe-91b7c44c841e notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4300058b-c0b4-4b86-8f6e-eda6d99553fc bbbbbbbb-0004-0004-0004-000000000004 d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +20add80c-2807-42c9-8c42-40291a49e5df bbbbbbbb-0004-0004-0004-000000000004 d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4d01680a-733e-4c0c-a26a-e19b962fe9fa bbbbbbbb-0004-0004-0004-000000000004 d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +e56ad576-c831-444a-bb21-0bcd74630d48 bbbbbbbb-0005-0005-0005-000000000005 793f5b1d-b218-40a3-aa73-3e1440d9ea66 book Livro text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +cc36d270-7961-481f-8a8b-99e0b9ef2e64 bbbbbbbb-0005-0005-0005-000000000005 793f5b1d-b218-40a3-aa73-3e1440d9ea66 author Autor text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +8eb622a4-8ee9-4c34-af09-9dd5c74923fa bbbbbbbb-0005-0005-0005-000000000005 793f5b1d-b218-40a3-aa73-3e1440d9ea66 notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +24d0c58f-3382-4ea8-81e7-3dc32f122b90 bbbbbbbb-0005-0005-0005-000000000005 83666a58-2e53-49a6-8b13-73eeb203e5ba supervisor Supervisor text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +4993e870-d101-4754-b2bc-18205255d643 bbbbbbbb-0005-0005-0005-000000000005 83666a58-2e53-49a6-8b13-73eeb203e5ba topic Assunto text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +b6e403de-e93a-42e0-832a-e6103a935421 bbbbbbbb-0005-0005-0005-000000000005 83666a58-2e53-49a6-8b13-73eeb203e5ba notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +a552127c-1b16-464c-a8f3-d7bdf054b0a9 bbbbbbbb-0005-0005-0005-000000000005 8c1a77a1-b66f-462e-940d-2f60d080149c theme Tema text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +be9ecf92-2dc8-4f62-94f3-223b0165c0af bbbbbbbb-0005-0005-0005-000000000005 8c1a77a1-b66f-462e-940d-2f60d080149c group Turma text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +7f7a9b35-60f1-4192-bf6c-d91ca42813a1 bbbbbbbb-0005-0005-0005-000000000005 8c1a77a1-b66f-462e-940d-2f60d080149c notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +528d426f-23d4-422f-8624-5b4178ce3a8b bbbbbbbb-0005-0005-0005-000000000005 a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad analyst Analista text f 10 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +11a02a30-7a0a-4baf-acdd-2525c2f2cf84 bbbbbbbb-0005-0005-0005-000000000005 a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad focus Foco text f 20 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +f456679b-05d7-4ccc-ae3b-c5f673f8f6f8 bbbbbbbb-0005-0005-0005-000000000005 a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad notes Observa????o textarea f 30 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 +07ba5c18-9ebb-4b38-bd2f-591b8e4f7f87 bbbbbbbb-0009-0009-0009-000000000009 fc6da673-fd7c-4101-b980-54eb3842804c book Livro text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +92eec7d8-20f3-46e4-8700-c80e9bd90bcb bbbbbbbb-0009-0009-0009-000000000009 fc6da673-fd7c-4101-b980-54eb3842804c author Autor text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +fdf6b850-b627-494e-9cb0-d1a044409b3c bbbbbbbb-0009-0009-0009-000000000009 fc6da673-fd7c-4101-b980-54eb3842804c notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +ef9a9605-9b0f-4671-b056-835c56844cf9 bbbbbbbb-0009-0009-0009-000000000009 3dbb8c2f-6fab-478f-b094-5bc13606a504 supervisor Supervisor text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e19f0ad4-fe52-4595-84aa-cf11828a7ecd bbbbbbbb-0009-0009-0009-000000000009 3dbb8c2f-6fab-478f-b094-5bc13606a504 topic Assunto text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +41a085ac-6849-4e29-8728-79e1a47c1ac5 bbbbbbbb-0009-0009-0009-000000000009 3dbb8c2f-6fab-478f-b094-5bc13606a504 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +8fdaad8b-f31c-4510-9b72-7346026103b9 bbbbbbbb-0009-0009-0009-000000000009 4c4a8ced-d109-443b-bc83-817c858f9bb6 theme Tema text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +9e53f6f5-6770-4f3e-8b0e-cb18c9df54b7 bbbbbbbb-0009-0009-0009-000000000009 4c4a8ced-d109-443b-bc83-817c858f9bb6 group Turma text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +222021b7-6917-4977-a08f-e19532aa2a0e bbbbbbbb-0009-0009-0009-000000000009 4c4a8ced-d109-443b-bc83-817c858f9bb6 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +329eebff-55c4-49e2-87ee-b3d0f418234b bbbbbbbb-0009-0009-0009-000000000009 b180f6e4-39ad-464c-842e-d42dc60cdf13 analyst Analista text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e87c45d8-8f5f-476d-811d-5b5b4a764e07 bbbbbbbb-0009-0009-0009-000000000009 b180f6e4-39ad-464c-842e-d42dc60cdf13 focus Foco text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +2a8c3eff-4eed-4b2d-98e7-50267ebb910e bbbbbbbb-0009-0009-0009-000000000009 b180f6e4-39ad-464c-842e-d42dc60cdf13 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e6109ef3-7409-4268-8ecf-eaff64fa9498 bbbbbbbb-0010-0010-0010-000000000010 fe52a6b2-e86e-47e0-9dc7-9b7e63599cff book Livro text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +2069cb4c-3c49-4a82-ada1-63082d14bba3 bbbbbbbb-0010-0010-0010-000000000010 fe52a6b2-e86e-47e0-9dc7-9b7e63599cff author Autor text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +d8428a90-f534-4794-99a7-1c752d42e0d2 bbbbbbbb-0010-0010-0010-000000000010 fe52a6b2-e86e-47e0-9dc7-9b7e63599cff notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +69435311-e703-4fbb-84b8-d634fd0148fe bbbbbbbb-0010-0010-0010-000000000010 fb2724d6-3938-4b3f-b8ec-aeb4417a4057 supervisor Supervisor text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +2c71883c-dee5-420e-9c7a-d1b1ade908f5 bbbbbbbb-0010-0010-0010-000000000010 fb2724d6-3938-4b3f-b8ec-aeb4417a4057 topic Assunto text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +1cf750d0-449d-4780-8828-bffce53cc651 bbbbbbbb-0010-0010-0010-000000000010 fb2724d6-3938-4b3f-b8ec-aeb4417a4057 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +e38226ca-3af4-41c3-a901-9edfc0814e46 bbbbbbbb-0010-0010-0010-000000000010 53193b8a-8af1-4824-a7c6-22cbb7459c45 theme Tema text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +6100b0b2-db5c-4aeb-98e3-3c6f6a20f058 bbbbbbbb-0010-0010-0010-000000000010 53193b8a-8af1-4824-a7c6-22cbb7459c45 group Turma text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +ef948cc1-4e2b-4c7a-9043-956a6341085a bbbbbbbb-0010-0010-0010-000000000010 53193b8a-8af1-4824-a7c6-22cbb7459c45 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +9405625b-f781-4b5d-836c-19371fccefab bbbbbbbb-0010-0010-0010-000000000010 29b5f51c-0833-4902-8c03-ee8bb7836a33 analyst Analista text f 10 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +cc414a63-c433-4269-be6c-caad9f83408d bbbbbbbb-0010-0010-0010-000000000010 29b5f51c-0833-4902-8c03-ee8bb7836a33 focus Foco text f 20 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +c5c4f4c9-2e74-4f65-b5d9-ee1121005cbc bbbbbbbb-0010-0010-0010-000000000010 29b5f51c-0833-4902-8c03-ee8bb7836a33 notes Observa????o textarea f 30 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 +155f3805-15e9-48fe-9f36-454e589ca2b2 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b567bfb6-7a58-4893-b072-eb23c72948a2 book Livro text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +95283550-683a-4c3d-a7d7-ac66f3b1f3e5 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b567bfb6-7a58-4893-b072-eb23c72948a2 author Autor text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +f293f578-4b69-46e9-b652-9eb535ae904b a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b567bfb6-7a58-4893-b072-eb23c72948a2 notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +9fa8bb78-eed2-4d10-9cab-e527c19137d8 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 supervisor Supervisor text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +972ac699-e2aa-4312-a0c9-1faa8853e38d a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 topic Assunto text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +f70a4cbb-b55a-4661-94de-d322f80f78a7 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +bc4036a8-32c2-4e13-bfde-0a85e21016f8 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 6a3194d1-475a-4131-9d5f-d8a3bb4ad84e theme Tema text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +4a5acde2-4d27-4377-8cf7-f94c25b2b13a a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 6a3194d1-475a-4131-9d5f-d8a3bb4ad84e group Turma text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +fe89c629-de7b-4dfe-b5b4-00954cf5ef4e a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 6a3194d1-475a-4131-9d5f-d8a3bb4ad84e notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +60192c88-c9ef-4650-8591-104e02f22460 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 fcd58365-0042-4df3-b93d-26cbae67d14a analyst Analista text f 10 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +dd49a6ca-755e-4eb2-b4e8-9c17393c2e77 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 fcd58365-0042-4df3-b93d-26cbae67d14a focus Foco text f 20 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +d2e9d95a-be44-4bde-a853-0e516345421b a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 fcd58365-0042-4df3-b93d-26cbae67d14a notes Observa????o textarea f 30 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 +c0206c42-b9c0-4fc4-afc1-22a6178750e2 1e98ca49-a46c-4701-847b-145a14d53d19 b5bcbc9e-abf0-4ff3-8960-133c3592de88 book Livro text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +23f30e08-728e-460d-9410-cb1bb24548ce 1e98ca49-a46c-4701-847b-145a14d53d19 b5bcbc9e-abf0-4ff3-8960-133c3592de88 author Autor text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +5406a62c-430a-4959-b0c5-cca804b03f53 1e98ca49-a46c-4701-847b-145a14d53d19 b5bcbc9e-abf0-4ff3-8960-133c3592de88 notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +1248cce1-2f5e-4e16-a193-503d374408f0 1e98ca49-a46c-4701-847b-145a14d53d19 8b0f4d71-f1d2-4c97-bd75-241aae919cfe supervisor Supervisor text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +cb9c6678-be1b-474a-b5a8-f4aaaff51213 1e98ca49-a46c-4701-847b-145a14d53d19 8b0f4d71-f1d2-4c97-bd75-241aae919cfe topic Assunto text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +b1ade16e-28f0-4b3e-b25e-7dcc6c338f7f 1e98ca49-a46c-4701-847b-145a14d53d19 8b0f4d71-f1d2-4c97-bd75-241aae919cfe notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +fd21821a-58db-40a6-8d2c-54e8bf8dfe45 1e98ca49-a46c-4701-847b-145a14d53d19 e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 theme Tema text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +757a5af1-e35e-4f70-a81c-89295d85979e 1e98ca49-a46c-4701-847b-145a14d53d19 e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 group Turma text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +198896b3-8181-428b-9022-938587ac9db7 1e98ca49-a46c-4701-847b-145a14d53d19 e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +ca55ceb1-a1d4-48ee-b82d-d767dac745c6 1e98ca49-a46c-4701-847b-145a14d53d19 871f7d97-2d2c-485b-a631-6a4884e1c1e9 analyst Analista text f 10 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +21832e62-718c-405a-b602-1e725330ccb3 1e98ca49-a46c-4701-847b-145a14d53d19 871f7d97-2d2c-485b-a631-6a4884e1c1e9 focus Foco text f 20 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +cad4c61b-482c-4d8c-adf1-9bf08d04f90a 1e98ca49-a46c-4701-847b-145a14d53d19 871f7d97-2d2c-485b-a631-6a4884e1c1e9 notes Observa????o textarea f 30 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 +\. + + +-- +-- Data for Name: determined_commitments; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.determined_commitments (id, tenant_id, created_by, is_native, native_key, is_locked, active, name, description, created_at, updated_at, bg_color, text_color) FROM stdin; +5944e5d9-622d-4db1-b73d-0083a6a4a9a1 bbbbbbbb-0002-0002-0002-000000000002 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +cd1076b1-0f3d-460e-842f-1585c0ae2a64 bbbbbbbb-0003-0003-0003-000000000003 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +b82819d4-02a1-40c5-bf57-6e2e0425e62d bbbbbbbb-0003-0003-0003-000000000003 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +2e5f4151-e259-4261-8954-e3d196a28383 bbbbbbbb-0004-0004-0004-000000000004 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +bec193c2-172b-40ef-a5fe-91b7c44c841e bbbbbbbb-0004-0004-0004-000000000004 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +b3bf589d-4a72-429f-b9bd-ba426c3f42f1 bbbbbbbb-0002-0002-0002-000000000002 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 16:44:21.537071+00 \N \N +793f5b1d-b218-40a3-aa73-3e1440d9ea66 bbbbbbbb-0005-0005-0005-000000000005 \N t reading f t Leitura Praticar leitura 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +717e552c-99cc-4375-b7f2-a1b29f3581e3 bbbbbbbb-0002-0002-0002-000000000002 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 16:46:11.099434+00 \N \N +8c1a77a1-b66f-462e-940d-2f60d080149c bbbbbbbb-0005-0005-0005-000000000005 \N t class f f Aula Dar aula 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N +fc6da673-fd7c-4101-b980-54eb3842804c bbbbbbbb-0009-0009-0009-000000000009 \N t reading f t Leitura Praticar leitura 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +4c4a8ced-d109-443b-bc83-817c858f9bb6 bbbbbbbb-0009-0009-0009-000000000009 \N t class f f Aula Dar aula 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +fe52a6b2-e86e-47e0-9dc7-9b7e63599cff bbbbbbbb-0010-0010-0010-000000000010 \N t reading f t Leitura Praticar leitura 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +53193b8a-8af1-4824-a7c6-22cbb7459c45 bbbbbbbb-0010-0010-0010-000000000010 \N t class f f Aula Dar aula 2026-03-23 11:27:09.865999+00 2026-03-23 11:27:09.865999+00 \N \N +e19a2e0e-cd6a-4cee-97e0-50a43ea69df1 bbbbbbbb-0002-0002-0002-000000000002 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 20:57:27.372517+00 \N \N +b567bfb6-7a58-4893-b072-eb23c72948a2 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t reading f t Leitura Praticar leitura 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +19072432-4eaf-411e-940f-c505639ec78a bbbbbbbb-0002-0002-0002-000000000002 \N f \N f t test teste 2026-03-24 19:42:19.588131+00 2026-03-24 19:42:19.588131+00 f97316 #ffffff +a4dcb5bf-86ab-4daa-80ca-357b88d7a6ba bbbbbbbb-0002-0002-0002-000000000002 \N f \N f t teste teste 2026-03-24 22:56:02.422706+00 2026-03-24 22:56:02.422706+00 6366f1 #ffffff +be219d8b-b2fb-4cfc-8910-64171cd7692f a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t session t t Sess??o Sess??o com paciente 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +b1fcaa5a-2aa2-4452-a48e-29a94b45fca1 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t supervision f t Supervis??o Supervis??o 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +6a3194d1-475a-4131-9d5f-d8a3bb4ad84e a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t class f f Aula Dar aula 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +fcd58365-0042-4df3-b93d-26cbae67d14a a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 \N t analysis f t An??lise Pessoal Minha an??lise pessoal 2026-03-26 19:46:29.161046+00 2026-03-26 19:46:29.161046+00 \N \N +65f1a136-ce11-46a0-b452-80f00132319d 1e98ca49-a46c-4701-847b-145a14d53d19 \N t session t t Sess??o Sess??o com paciente 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +b5bcbc9e-abf0-4ff3-8960-133c3592de88 1e98ca49-a46c-4701-847b-145a14d53d19 \N t reading f t Leitura Praticar leitura 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +8b0f4d71-f1d2-4c97-bd75-241aae919cfe 1e98ca49-a46c-4701-847b-145a14d53d19 \N t supervision f t Supervis??o Supervis??o 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +e4a1f8a5-817c-416c-b88d-5eaed75ce1b6 1e98ca49-a46c-4701-847b-145a14d53d19 \N t class f f Aula Dar aula 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +871f7d97-2d2c-485b-a631-6a4884e1c1e9 1e98ca49-a46c-4701-847b-145a14d53d19 \N t analysis f t An??lise Pessoal Minha an??lise pessoal 2026-03-26 19:47:30.68134+00 2026-03-26 19:47:30.68134+00 \N \N +d2ea6a5e-e563-434b-950e-2d27bbf445ec bbbbbbbb-0003-0003-0003-000000000003 \N t session t t Sessão Sess??o com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +21f39315-5171-42ce-848c-129c1955206b bbbbbbbb-0004-0004-0004-000000000004 \N t session t t Sessão Sess??o com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +3276b032-6990-4bfc-942d-9155943c241e bbbbbbbb-0005-0005-0005-000000000005 \N t session t t Sessão Sess??o com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +fb194a3e-06ee-4704-a8cb-fe302dd2528e bbbbbbbb-0009-0009-0009-000000000009 \N t session t t Sessão Sess??o com paciente 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +1936c4af-5757-4c10-ad8f-8a5988287acc bbbbbbbb-0010-0010-0010-000000000010 \N t session t t Sessão Sess??o com paciente 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +42a8c681-8b32-4608-870f-b617acbe249e bbbbbbbb-0002-0002-0002-000000000002 \N t session t t Sessão Sessão com paciente 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 6366f1 #ffffff +9ded91f1-1974-4e56-afc6-12b2e8f9456c bbbbbbbb-0003-0003-0003-000000000003 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +0d6cb161-8d89-4b18-9323-5631e8cddd8f bbbbbbbb-0004-0004-0004-000000000004 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +83666a58-2e53-49a6-8b13-73eeb203e5ba bbbbbbbb-0005-0005-0005-000000000005 \N t supervision f t Supervisão Supervis??o 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +3dbb8c2f-6fab-478f-b094-5bc13606a504 bbbbbbbb-0009-0009-0009-000000000009 \N t supervision f t Supervisão Supervis??o 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +fb2724d6-3938-4b3f-b8ec-aeb4417a4057 bbbbbbbb-0010-0010-0010-000000000010 \N t supervision f t Supervisão Supervis??o 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +b5c43dc2-a3a1-40ca-8b75-02ec649fd914 bbbbbbbb-0003-0003-0003-000000000003 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +d11aa6fe-ef08-4610-8a6f-4b472ccd1b64 bbbbbbbb-0004-0004-0004-000000000004 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +a72b9eb5-747f-4ec3-8bb4-f9c17925e7ad bbbbbbbb-0005-0005-0005-000000000005 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 10:46:29.876072+00 2026-03-23 14:21:19.247486+00 \N \N +b180f6e4-39ad-464c-842e-d42dc60cdf13 bbbbbbbb-0009-0009-0009-000000000009 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +29b5f51c-0833-4902-8c03-ee8bb7836a33 bbbbbbbb-0010-0010-0010-000000000010 \N t analysis f t Análise Pessoal Minha an??lise pessoal 2026-03-23 11:27:09.865999+00 2026-03-23 14:21:19.247486+00 \N \N +\. + + +-- +-- Data for Name: dev_user_credentials; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.dev_user_credentials (id, user_id, email, password_dev, kind, note, created_at) FROM stdin; +\. + + +-- +-- Data for Name: email_layout_config; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.email_layout_config (id, tenant_id, header_config, footer_config, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: email_templates_global; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.email_templates_global (id, key, domain, channel, subject, body_html, body_text, version, is_active, variables, created_at, updated_at) FROM stdin; +be1e52ec-0c1d-4cbe-97da-9c47ce052631 session.reminder.email session email Lembrete: sua sessão amanhã às {{session_time}} \n

Olá, {{patient_name}}!

\n

Este é um lembrete da sua sessão agendada para {{session_date}} às {{session_time}}.

\n

Modalidade: {{session_modality}}

\n {{#if session_link}}\n

Clique aqui para entrar na sessão online

\n {{/if}}\n

Em caso de necessidade de cancelamento, entre em contato com antecedência.

\n

Até logo,
{{therapist_name}}

\n Olá, {{patient_name}}! Lembrete da sua sessão: {{session_date}} às {{session_time}} ({{session_modality}}). 2 t {"patient_name": "Nome completo do paciente", "session_date": "Data da sessão (ex: 20/03/2026)", "session_link": "Link da videochamada (apenas online)", "session_time": "Horário da sessão (ex: 14:00)", "therapist_name": "Nome do terapeuta", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +bf956245-c79a-4ed0-86f1-e0be57072cc8 session.confirmation.email session email Sessão confirmada — {{session_date}} às {{session_time}} \n

Olá, {{patient_name}}!

\n

Sua sessão foi confirmada com sucesso.

\n
    \n
  • Data: {{session_date}}
  • \n
  • Horário: {{session_time}}
  • \n
  • Modalidade: {{session_modality}}
  • \n {{#if session_address}}
  • Local: {{session_address}}
  • {{/if}}\n
\n

Até lá,
{{therapist_name}}

\n Sessão confirmada: {{session_date}} às {{session_time}} ({{session_modality}}). 1 t {"patient_name": "Nome do paciente", "session_date": "Data da sessão", "session_time": "Horário da sessão", "therapist_name": "Nome do terapeuta", "session_address": "Endereço (apenas presencial)", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +9039f35e-e31f-4bad-a6a5-2b865f1152b3 session.cancellation.email session email Sessão cancelada — {{session_date}} \n

Olá, {{patient_name}}!

\n

Informamos que sua sessão do dia {{session_date}} às {{session_time}} foi cancelada.

\n {{#if cancellation_reason}}

Motivo: {{cancellation_reason}}

{{/if}}\n

Entre em contato para reagendar.

\n

{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Data cancelada", "session_time": "Horário cancelado", "therapist_name": "Nome do terapeuta", "cancellation_reason": "Motivo do cancelamento (opcional)"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +ff36dccc-6fe1-4391-9eac-bd03927b38f5 session.rescheduled.email session email Sessão reagendada — {{session_date}} às {{session_time}} \n

Olá, {{patient_name}}!

\n

Sua sessão foi reagendada para {{session_date}} às {{session_time}}.

\n

Modalidade: {{session_modality}}

\n

Até lá,
{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Nova data", "session_time": "Novo horário", "therapist_name": "Nome do terapeuta", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +f17b10f8-d811-4f83-bf5e-3ed636262f28 intake.received.email intake email Recebemos seu cadastro — {{clinic_name}} \n

Olá, {{patient_name}}!

\n

Recebemos seu cadastro com sucesso. Nossa equipe entrará em contato em breve para dar continuidade ao processo.

\n

Obrigado pela confiança,
{{clinic_name}}

\n \N 1 t {"clinic_name": "Nome da clínica ou terapeuta", "patient_name": "Nome do solicitante"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +5b4a9a8b-bc92-4030-92ec-acbb6f9e7ee0 intake.approved.email intake email Cadastro aprovado — bem-vindo(a)! \n

Olá, {{patient_name}}!

\n

Seu cadastro foi aprovado. Você já pode acessar o portal e agendar sua primeira sessão.

\n

Acessar portal →

\n

Qualquer dúvida, estamos à disposição.
{{therapist_name}}

\n \N 1 t {"portal_link": "Link do portal do paciente", "patient_name": "Nome do paciente", "therapist_name": "Nome do terapeuta"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +04da6880-708a-4018-9b1f-0f398d152e0c intake.rejected.email intake email Atualização sobre seu cadastro — {{clinic_name}} \n

Olá, {{patient_name}}!

\n

Agradecemos seu interesse. Infelizmente não será possível dar continuidade ao seu cadastro no momento.

\n {{#if rejection_reason}}

{{rejection_reason}}

{{/if}}\n

{{clinic_name}}

\n \N 1 t {"clinic_name": "Nome da clínica", "patient_name": "Nome do paciente", "rejection_reason": "Motivo (opcional)"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +3e9ae66a-598c-4763-ab61-f02bb71323a4 scheduler.request_accepted.email session email Sua solicitação foi aceita — {{session_date}} às {{session_time}} \n

Olá, {{patient_name}}!

\n

Sua solicitação de agendamento foi aceita.

\n
    \n
  • Data: {{session_date}}
  • \n
  • Horário: {{session_time}}
  • \n
  • Tipo: {{session_type}}
  • \n
  • Modalidade: {{session_modality}}
  • \n
\n

Até logo,
{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Data confirmada", "session_time": "Horário confirmado", "session_type": "Primeira consulta / Retorno", "therapist_name": "Nome do terapeuta", "session_modality": "Presencial ou Online"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +7c7f736d-1ddb-46ba-ab41-f66d2267b730 scheduler.request_rejected.email session email Atualização sobre sua solicitação de agendamento \n

Olá, {{patient_name}}!

\n

Infelizmente não foi possível confirmar sua solicitação de agendamento para {{session_date}}.

\n {{#if rejection_reason}}

Motivo: {{rejection_reason}}

{{/if}}\n

Entre em contato para verificar outros horários disponíveis.

\n

{{therapist_name}}

\n \N 1 t {"patient_name": "Nome do paciente", "session_date": "Data solicitada", "therapist_name": "Nome do terapeuta", "rejection_reason": "Motivo (opcional)"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +78a33f29-3428-482f-9b0e-362c69ff20a8 system.welcome.email system email Bem-vindo(a) ao {{clinic_name}}! \n

Olá, {{patient_name}}!

\n

Seja bem-vindo(a)! Sua conta foi criada com sucesso.

\n

Acessar minha área →

\n \N 1 t {"clinic_name": "Nome da clínica", "portal_link": "Link do portal", "patient_name": "Nome do paciente"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +21a3fab3-01af-4b16-bd63-50929be5001d system.password_reset.email system email Redefinição de senha \n

Olá, {{patient_name}}!

\n

Recebemos uma solicitação para redefinir sua senha.

\n

Clique aqui para redefinir sua senha →

\n

Se você não solicitou a redefinição, ignore este e-mail.

\n \N 1 t {"reset_link": "Link de redefinição de senha", "patient_name": "Nome do usuário"} 2026-03-23 11:27:15.104483+00 2026-03-23 14:18:08.414378+00 +\. + + +-- +-- Data for Name: email_templates_tenant; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.email_templates_tenant (id, tenant_id, owner_id, template_key, subject, body_html, body_text, enabled, synced_version, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: entitlements_invalidation; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.entitlements_invalidation (owner_id, changed_at) FROM stdin; +aaaaaaaa-0002-0002-0002-000000000002 2026-03-27 09:41:23.50964+00 +\. + + +-- +-- Data for Name: features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.features (id, key, description, created_at, descricao, name) FROM stdin; +5e539124-630f-4c2a-a9de-7999317085e6 agenda.view Visualização da agenda 2026-02-21 02:36:01.562728+00 Visualizar agenda Agenda - Visualizar +a74fef14-c9d9-4884-ba45-f81c60e0783a agenda.manage Gerenciamento completo da agenda 2026-02-21 02:35:50.629667+00 Adicionar Compromissos na agenda Agenda - Gerenciar +a56482a1-0787-49da-90a7-e1857488734a patients Módulo de pacientes 2026-03-02 12:35:19.955748+00 Pacientes Pacientes +57f631a1-9ebe-480b-a2cb-144ad32ff5f0 patients.view Visualização de pacientes 2026-02-28 11:15:14.275572+00 Visualizar pacientes Pacientes - Visualizar +4e5bc50b-e339-42fe-9d91-61e8555f83e7 patients.manage Gerenciamento completo de pacientes 2026-02-28 11:15:14.275572+00 Gerenciar pacientes Pacientes - Gerenciar +53a48c3b-0617-4618-adf8-f3a255c51ee4 online_scheduling Sistema de agendamento online 2026-03-01 09:59:15.432733+00 Agendamento online Agendamento Online +5739aa27-b089-4b15-b149-31b13d768825 online_scheduling.manage Gerenciamento do agendamento online 2026-02-15 21:50:02.056357+00 Gerenciar agendamento online (admin) Agendamento Online - Gerenciar +0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 online_scheduling.public Página pública do agendador 2026-02-15 21:50:02.056357+00 Página pública de agendamento Agendamento Online - Público +f5d66212-fd73-4472-a306-07928e5deaec reminders Sistema de lembretes automáticos 2026-03-01 09:59:15.432733+00 Lembretes Lembretes +b3efa25d-60a4-4974-8153-6ec098b3d477 reports_basic Relatórios básicos 2026-03-01 09:59:15.432733+00 Relatórios básicos Relatórios Básicos +bf133ad1-da8e-4ea9-bd66-21901cb50075 reports_advanced Relatórios avançados com exportação 2026-03-01 09:59:15.432733+00 Relatórios avançados Relatórios Avançados +336aeeba-b18e-4e68-8303-d42ba09f4b20 secretary Funcionalidade de secretária 2026-03-01 09:59:15.432733+00 Secretaria Secretária +30c9cdd5-7c8c-44d9-8c0b-614165bb9496 shared_reception Recepção compartilhada entre terapeutas 2026-03-02 12:35:19.955748+00 Recepção / Secretária Recepção Compartilhada +74fc1321-4d17-49c3-b72e-db3a7f4be451 rooms Gerenciamento de salas 2026-03-02 12:35:19.955748+00 Salas / Coworking Salas +c109ad27-0edf-4774-91a7-94dac4faab49 intake_public Formulário de intake público 2026-03-02 12:35:19.955748+00 Link externo de cadastro Intake Público +90e92108-8124-40ee-88a0-f0ecafb57d76 intakes_pro Funcionalidades avançadas de intake 2026-02-15 23:29:55.845638+00 Formulários PRO Intakes PRO +f393178c-284d-422f-b096-8793f85428d5 custom_branding Personalização de marca 2026-03-01 09:59:15.432733+00 Personalização de marca Branding Personalizado +d6f54674-ea8b-484b-af0e-99127a510da2 api_access Acesso via API 2026-03-01 09:59:15.432733+00 Integrações/API Acesso API +a5593d96-dd95-46bb-bef0-bd379b56ad50 audit_log Log de auditoria completo 2026-03-01 09:59:15.432733+00 Auditoria Log de Auditoria +8cc81988-d02a-4542-9cb2-ce2ed7c18d60 sms_reminder Lembretes via SMS 2026-02-15 23:29:55.845638+00 Lembretes por SMS Lembrete SMS +9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 clinic_calendar Visão consolidada do calendário 2026-03-01 09:59:15.432733+00 Agenda da clínica Calendário da Clínica +a830e45b-3bb4-4b17-812d-fe83777a2377 advanced_reports Relatórios avançados da clínica 2026-02-15 23:29:55.845638+00 Relatórios avançados Relatórios Avançados (Clínica) +9ab8bdbb-838b-4946-aa5d-fd9cfdd257b3 supervisor.access Acesso ao módulo de supervisão 2026-03-05 00:58:17.218326+00 Acesso básico ao espaço de supervisão (sala, lista de supervisionados). Supervisor - Acesso +1167b54a-0e93-43a2-94d7-c12e64eb56de supervisor.invite Convidar supervisionados 2026-03-05 00:58:17.218326+00 Permite convidar terapeutas para participar da sala de supervisão. Supervisor - Convidar +761e4495-b46a-4791-9519-86ffe48dc47f supervisor.sessions Gerenciar sessões de supervisão 2026-03-05 00:58:17.218326+00 Agendamento e registro de sessões de supervisão. Supervisor - Sessões +7e82ee01-44f6-4b3f-9861-840c58e13f58 supervisor.reports Relatórios de supervisão 2026-03-05 00:58:17.218326+00 Relatórios avançados de progresso e evolução dos supervisionados. Supervisor - Relatórios +\. + + +-- +-- Data for Name: feriados; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.feriados (id, tenant_id, owner_id, tipo, nome, data, cidade, estado, observacao, bloqueia_sessoes, criado_em) FROM stdin; +\. + + +-- +-- Data for Name: financial_categories; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.financial_categories (id, user_id, name, type, color, icon, sort_order, created_at) FROM stdin; +\. + + +-- +-- Data for Name: financial_exceptions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.financial_exceptions (id, owner_id, tenant_id, exception_type, charge_mode, charge_value, charge_pct, min_hours_notice, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: financial_records; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.financial_records (id, owner_id, tenant_id, type, amount, description, category, payment_method, paid_at, due_date, installments, installment_number, installment_group, agenda_evento_id, patient_id, clinic_fee_pct, clinic_fee_amount, insurance_plan_id, notes, tags, created_at, updated_at, deleted_at, discount_amount, final_amount, status, category_id) FROM stdin; +d51c8380-812a-45a7-8154-c799f4f95723 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 receita 1.00 \N \N \N \N 2026-03-25 1 1 \N \N 76cec0bb-1e63-45bf-9b03-feaaa2a5d18d 0.00 0.00 \N \N \N 2026-03-24 22:57:09.509577+00 2026-03-24 22:57:09.509577+00 \N 0.00 1.00 pending \N +\. + + +-- +-- Data for Name: global_notices; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.global_notices (id, title, message, variant, roles, contexts, starts_at, ends_at, is_active, priority, dismissible, persist_dismiss, dismiss_scope, show_once, max_views, cooldown_minutes, version, action_type, action_label, action_url, action_route, views_count, clicks_count, created_at, updated_at, created_by, content_align, link_target) FROM stdin; +\. + + +-- +-- Data for Name: insurance_plan_services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.insurance_plan_services (id, insurance_plan_id, name, value, active, created_at, updated_at) FROM stdin; +b24c1a29-a5b3-4676-94b0-8effa89c672a ec7aa65f-cbd2-48a5-9ec0-368a788438a7 procedimento 1111.00 t 2026-03-24 12:21:29.140157+00 2026-03-24 12:21:29.140157+00 +\. + + +-- +-- Data for Name: insurance_plans; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.insurance_plans (id, owner_id, tenant_id, name, notes, default_value, active, created_at, updated_at) FROM stdin; +ec7aa65f-cbd2-48a5-9ec0-368a788438a7 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste asd \N t 2026-03-24 12:21:20.501533+00 2026-03-24 12:21:20.501533+00 +\. + + +-- +-- Data for Name: login_carousel_slides; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.login_carousel_slides (id, title, body, icon, ordem, ativo, created_at, updated_at) FROM stdin; +0af63b3a-1819-4384-bf94-b29cbe84aca3 Gestão clínica simplificada Agendamentos, prontuários e sessões em um único painel. Foco no que importa: seus pacientes. pi-calendar-clock 0 t 2026-03-23 14:18:08.414378+00 2026-03-23 14:18:08.414378+00 +c02309d3-85cf-452f-bb85-363889aea9f3 Gestão clínica simplificada Gerencie agenda, pacientes e financeiro em um só lugar. Simples, rápido e seguro. pi-users 1 t 2026-03-23 14:18:08.414378+00 2026-03-23 14:18:08.414378+00 +763a600e-987c-4e42-8f62-29b5dea59c39 Múltiplos profissionais, uma só plataforma Ideal para clínicas com vários terapeutas. Cada profissional com sua agenda e seus pacientes. pi-shield 2 t 2026-03-23 14:18:08.414378+00 2026-03-23 14:18:08.414378+00 +\. + + +-- +-- Data for Name: medicos; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.medicos (id, owner_id, tenant_id, nome, crm, especialidade, telefone_profissional, telefone_pessoal, email, clinica, cidade, estado, observacoes, ativo, created_at, updated_at) FROM stdin; +135fa2c3-64f5-4268-af93-b8cd7fe96a0f aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste \N teste 11111111111 11111111111 teste \N \N SP \N t 2026-03-28 11:35:32.106083+00 2026-03-28 11:35:32.106083+00 +6e77083f-107b-48a0-bfa7-255078ffc15a aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste2 2 \N \N \N \N \N \N SP \N t 2026-03-28 12:21:30.879819+00 2026-03-28 12:21:30.879819+00 +\. + + +-- +-- Data for Name: module_features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.module_features (module_id, feature_id, enabled, limits, created_at) FROM stdin; +\. + + +-- +-- Data for Name: modules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.modules (id, key, name, description, is_active, created_at) FROM stdin; +\. + + +-- +-- Data for Name: notice_dismissals; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notice_dismissals (id, notice_id, user_id, version, dismissed_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_channels; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_channels (id, tenant_id, owner_id, channel, provider, is_active, display_name, sender_address, credentials, connection_status, last_health_check, metadata, created_at, updated_at, deleted_at, twilio_subaccount_sid, twilio_phone_number, twilio_phone_sid, webhook_url, cost_per_message_usd, price_per_message_brl, provisioned_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_logs; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_logs (id, tenant_id, owner_id, queue_id, agenda_evento_id, patient_id, channel, template_key, schedule_key, recipient_address, resolved_message, resolved_vars, status, provider, provider_message_id, provider_status, provider_response, sent_at, delivered_at, read_at, failed_at, failure_reason, estimated_cost_brl, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_preferences; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_preferences (id, tenant_id, owner_id, patient_id, whatsapp_opt_in, email_opt_in, sms_opt_in, preferred_time_start, preferred_time_end, lgpd_consent_given, lgpd_consent_date, lgpd_consent_version, lgpd_consent_ip, lgpd_opt_out_date, lgpd_opt_out_reason, created_at, updated_at, deleted_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_queue; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_queue (id, tenant_id, owner_id, agenda_evento_id, patient_id, channel, template_key, schedule_key, resolved_vars, recipient_address, status, scheduled_at, sent_at, next_retry_at, attempts, max_attempts, last_error, idempotency_key, provider_message_id, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_schedules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_schedules (id, tenant_id, owner_id, schedule_key, event_type, trigger_type, offset_minutes, whatsapp_enabled, email_enabled, sms_enabled, allowed_time_start, allowed_time_end, skip_weekends, skip_holidays, is_active, sort_order, created_at, updated_at, deleted_at) FROM stdin; +\. + + +-- +-- Data for Name: notification_templates; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notification_templates (id, tenant_id, owner_id, key, domain, channel, event_type, body_text, meta_template_name, meta_template_namespace, meta_components, meta_status, variables, version, is_active, is_default, created_at, updated_at, deleted_at) FROM stdin; +37311b1a-2919-4f38-8dbc-7ccf81942062 \N \N session.reminder.whatsapp session whatsapp lembrete_sessao Olá {{nome_paciente}}! 👋\n\nLembrete: você tem sessão agendada para *{{data_sessao}}* às *{{hora_sessao}}* com {{nome_terapeuta}}.\n\n📋 {{modalidade}}\n\nPara confirmar, responda ✅\nPara cancelar, responda ❌\n\nSe precisar remarcar, entre em contato.\n\n_Responda SAIR para não receber mais lembretes._ \N \N \N draft ["nome_paciente", "data_sessao", "hora_sessao", "nome_terapeuta", "modalidade"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +4e61a64d-b2f9-49de-9d27-6496ddba8aef \N \N session.confirmation.whatsapp session whatsapp confirmacao_sessao ✅ Sessão confirmada!\n\nOlá {{nome_paciente}}, sua sessão com {{nome_terapeuta}} foi confirmada:\n\n📅 {{data_sessao}} às {{hora_sessao}}\n📋 {{modalidade}}\n\nTe esperamos! \N \N \N draft ["nome_paciente", "data_sessao", "hora_sessao", "nome_terapeuta", "modalidade"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +262e9dba-0324-478f-9d76-3faba555ec3c \N \N session.cancellation.whatsapp session whatsapp cancelamento_sessao Olá {{nome_paciente}},\n\nSua sessão de {{data_sessao}} às {{hora_sessao}} com {{nome_terapeuta}} foi *cancelada*.\n\nSe desejar reagendar, entre em contato.\n\nAtenciosamente,\n{{nome_terapeuta}} \N \N \N draft ["nome_paciente", "data_sessao", "hora_sessao", "nome_terapeuta"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +2c5331f9-6728-4cea-b4cc-e9b2659f5362 \N \N session.reminder.sms session sms lembrete_sessao Lembrete: sessao em {{data_sessao}} as {{hora_sessao}} com {{nome_terapeuta}}. Confirme respondendo OK. \N \N \N draft ["data_sessao", "hora_sessao", "nome_terapeuta"] 1 t t 2026-03-23 11:27:15.104483+00 2026-03-23 11:27:15.104483+00 \N +3e4386ba-2790-41e8-9b92-3b6beab549d8 \N \N session.lembrete.whatsapp session whatsapp lembrete_sessao Olá, {{nome_paciente}}! 👋\n\nLembrete da sua sessão com {{nome_terapeuta}}.\n\n📅 Data: {{data_sessao}}\n🕐 Hora: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nQualquer dúvida, entre em contato. Até lá! 💚 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +eb218e05-4ea0-4884-acd2-a766d3986d04 \N \N session.lembrete_2h.whatsapp session whatsapp lembrete_sessao Olá, {{nome_paciente}}! Sua sessão com {{nome_terapeuta}} começa em 2 horas.\n\n🕐 Hora: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nAté já! 😊 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +4574eb97-aeda-4cbb-a7e9-243c8f8c0b17 \N \N session.confirmacao.whatsapp session whatsapp confirmacao_sessao Olá, {{nome_paciente}}! ✅\n\nSua sessão foi confirmada!\n\n📅 Data: {{data_sessao}}\n🕐 Hora: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nAté lá! 💚 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade", "link_confirmacao"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +b321696c-96e2-4d16-8f8d-aa46d98945b8 \N \N session.cancelamento.whatsapp session whatsapp cancelamento_sessao Olá, {{nome_paciente}}. Infelizmente a sessão do dia {{data_sessao}} às {{hora_sessao}} foi cancelada.\n\nEntre em contato para reagendar. 🙏 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +216c6404-6e6c-4cb6-b5bd-486c8abc3c48 \N \N session.reagendamento.whatsapp session whatsapp reagendamento Olá, {{nome_paciente}}! Sua sessão foi reagendada.\n\n📅 Nova data: {{data_sessao}}\n🕐 Novo horário: {{hora_sessao}}\n📍 Modalidade: {{modalidade}}\n\nAté lá! 😊 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +48a93a15-9675-49a7-a2d7-3b9b589e14f5 \N \N cobranca.pendente.whatsapp billing whatsapp cobranca_pendente Olá, {{nome_paciente}}. Identificamos uma cobrança pendente no valor de {{valor_sessao}} referente à sua sessão do dia {{data_sessao}}.\n\nPor favor, entre em contato para regularizar. 🙏 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade", "valor_sessao"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +26edef88-2d45-4ee2-8496-30f76f35cb77 \N \N sistema.boas_vindas.whatsapp system whatsapp boas_vindas_paciente Olá, {{nome_paciente}}! 🎉\n\nSeja muito bem-vindo(a)! Estamos felizes em ter você aqui.\n\nEm caso de dúvidas, estamos à disposição. Até a nossa primeira sessão! 💚 \N \N \N draft ["nome_paciente", "nome_terapeuta", "data_sessao", "hora_sessao", "modalidade"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +e66ef5f5-ab3a-46c1-8446-25b55487a017 \N \N session.confirmation.sms session sms confirmacao_sessao Sessão confirmada! {{session_date}} às {{session_time}} ({{session_modality}}) com {{therapist_name}}. \N \N \N draft ["patient_name", "session_date", "session_time", "session_modality", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +aae09094-82cb-406f-8015-c42cd24afded \N \N session.cancellation.sms session sms cancelamento_sessao Sua sessão de {{session_date}} às {{session_time}} foi cancelada. Entre em contato para reagendar. — {{therapist_name}} \N \N \N draft ["patient_name", "session_date", "session_time", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +c8521ea4-31c0-4015-8923-7ef57b3f8b47 \N \N session.rescheduled.sms session sms reagendamento Sua sessão foi reagendada para {{session_date}} às {{session_time}} ({{session_modality}}). — {{therapist_name}} \N \N \N draft ["patient_name", "session_date", "session_time", "session_modality", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +ba32ee4e-6548-48de-bf83-b0cb11a00761 \N \N intake.received.sms intake sms intake_recebido Olá {{patient_name}}, recebemos seu cadastro. Em breve entraremos em contato. — {{clinic_name}} \N \N \N draft ["patient_name", "clinic_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +7ccd6a04-8b89-4238-bb97-d78b0d56312f \N \N intake.approved.sms intake sms intake_aprovado Olá {{patient_name}}, seu cadastro foi aprovado! Acesse o portal para agendar sua sessão. — {{therapist_name}} \N \N \N draft ["patient_name", "therapist_name"] 1 t t 2026-03-23 12:01:07.120095+00 2026-03-23 12:01:07.120095+00 \N +\. + + +-- +-- Data for Name: notifications; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.notifications (id, owner_id, tenant_id, type, ref_id, ref_table, payload, read_at, archived, created_at) FROM stdin; +de9453b5-688c-411a-9ea1-c03ec3afea56 aaaaaaaa-0002-0002-0002-000000000002 \N new_patient f94e4a39-c92b-4f9b-82b4-1a2956f2140d patient_intake_requests {"title": "Novo cadastro externo", "detail": "Yasmin Gomes Ferreira", "deeplink": "/therapist/patients/cadastro/recebidos", "avatar_initials": "YA"} \N f 2026-03-23 23:25:39.670692+00 +efb5e6fd-ab89-46d9-b9d5-3534199b10e1 aaaaaaaa-0002-0002-0002-000000000002 \N new_patient 208f1883-a9d8-4056-831d-a22aec679cfb patient_intake_requests {"title": "Novo cadastro externo", "detail": "Carla Lima Almeida", "deeplink": "/therapist/patients/cadastro/recebidos", "avatar_initials": "CA"} 2026-03-23 23:34:19.18+00 f 2026-03-23 23:25:46.914188+00 +\. + + +-- +-- Data for Name: owner_users; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.owner_users (owner_id, user_id, role, created_at) FROM stdin; +\. + + +-- +-- Data for Name: patient_contacts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_contacts (id, patient_id, tenant_id, nome, tipo, relacao, telefone, email, cpf, especialidade, registro_profissional, is_primario, ativo, created_at, updated_at) FROM stdin; +26b3a63f-d44c-476b-ba15-8df162c597af 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 bbbbbbbb-0002-0002-0002-000000000002 Henrique Costa emergencia Irmão 55979769772 \N \N \N \N t t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +7334c030-eab5-42ac-a3e7-7091e40676ba 4fdd639d-5f32-453f-9092-74b183c8bbfe bbbbbbbb-0002-0002-0002-000000000002 Felipe Lima emergencia Irmã 55738955502 \N \N \N \N t t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +868bb964-b624-4a19-901e-6550d486893f 2aaa34f7-c770-4d78-a2a2-c77bd4771c6a bbbbbbbb-0002-0002-0002-000000000002 Bruno Martins emergencia Pai 55529945515 \N \N \N \N t t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +0a28b612-7c1b-4517-b0bb-fb5b3efab512 c193905f-e70c-4935-aec3-b9c161c6044c bbbbbbbb-0002-0002-0002-000000000002 Felipe Carvalho emergencia Pai 55225172394 \N \N \N \N t t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +882bd6bc-ea68-4a2e-bf3a-6f043698c1f0 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 bbbbbbbb-0002-0002-0002-000000000002 Yasmin Araújo Lima responsavel_legal Responsável legal 55209246525 \N 85070073257 \N \N f t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +e8ae6155-1445-4c9c-a0bd-e65f2b7799d4 4fdd639d-5f32-453f-9092-74b183c8bbfe bbbbbbbb-0002-0002-0002-000000000002 Vanessa Carvalho Barbosa responsavel_legal Responsável legal 55209874519 \N 24378696207 \N \N f t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +f6032ed5-0b69-4eed-89f9-c6e2ccce7fc3 2aaa34f7-c770-4d78-a2a2-c77bd4771c6a bbbbbbbb-0002-0002-0002-000000000002 Ana Oliveira Martins responsavel_legal Responsável legal 55129232266 \N 29325344165 \N \N f t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +0249c61e-3c4c-41a2-97f5-620228d1b967 c193905f-e70c-4935-aec3-b9c161c6044c bbbbbbbb-0002-0002-0002-000000000002 Sabrina Martins Santos responsavel_legal Responsável legal 55749688637 \N 89130651778 \N \N f t 2026-03-28 00:03:06.694528+00 2026-03-28 00:03:06.694528+00 +\. + + +-- +-- Data for Name: patient_discounts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_discounts (id, owner_id, tenant_id, patient_id, discount_pct, discount_flat, reason, active, active_from, active_to, created_at) FROM stdin; +\. + + +-- +-- Data for Name: patient_group_patient; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_group_patient (patient_group_id, patient_id, created_at, tenant_id) FROM stdin; +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 2026-03-25 20:47:18.396593+00 bbbbbbbb-0002-0002-0002-000000000002 +a5b1e98b-e951-4f8a-b8ab-8221bf74342f 4fdd639d-5f32-453f-9092-74b183c8bbfe 2026-03-25 20:47:43.917435+00 bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 2aaa34f7-c770-4d78-a2a2-c77bd4771c6a 2026-03-25 20:58:12.198254+00 bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 c193905f-e70c-4935-aec3-b9c161c6044c 2026-03-25 20:58:37.212766+00 bbbbbbbb-0002-0002-0002-000000000002 +a5b1e98b-e951-4f8a-b8ab-8221bf74342f 919ce219-3cca-4bf3-ad9d-2cc8db68cec0 2026-03-28 10:35:53.088394+00 bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 778338f5-7122-4d80-a6da-88a52deee1ca 2026-03-28 12:17:10.121291+00 bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 ad3bb70e-881e-4ee1-b44e-436eaae2caf1 2026-03-28 12:20:59.425179+00 bbbbbbbb-0002-0002-0002-000000000002 +a5b1e98b-e951-4f8a-b8ab-8221bf74342f 542156ce-9266-487c-85e6-91a00cdfb3ea 2026-03-28 12:43:28.793188+00 bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 fe670066-0d81-49ea-b177-61e83b455c59 2026-03-29 12:18:54.34788+00 bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: patient_groups; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_groups (id, nome, descricao, cor, is_active, is_system, owner_id, created_at, updated_at, therapist_id, tenant_id) FROM stdin; +a5b1e98b-e951-4f8a-b8ab-8221bf74342f teste \N #22c55e t f aaaaaaaa-0002-0002-0002-000000000002 2026-03-23 15:35:37.961391+00 2026-03-23 17:13:24.546376+00 \N bbbbbbbb-0002-0002-0002-000000000002 +8d5871bc-2728-49b0-8b7f-dc9f3a5a1ed0 teste 2 \N \N t f aaaaaaaa-0002-0002-0002-000000000002 2026-03-24 22:43:26.378387+00 2026-03-24 22:43:26.378387+00 \N bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: patient_intake_requests; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_intake_requests (id, owner_id, token, consent, status, created_at, converted_patient_id, rejected_reason, updated_at, cpf, rg, cep, nome_completo, email_principal, telefone, pais, cidade, estado, endereco, numero, bairro, complemento, data_nascimento, naturalidade, genero, estado_civil, onde_nos_conheceu, encaminhado_por, observacoes, notas_internas, email_alternativo, telefone_alternativo, profissao, escolaridade, nacionalidade, avatar_url, tenant_id) FROM stdin; +208f1883-a9d8-4056-831d-a22aec679cfb aaaaaaaa-0002-0002-0002-000000000002 9c0780f2-bf0a-442e-9aa5-787c2f585f5c t converted 2026-03-23 23:25:46.914188+00 f816b137-fb45-471c-81d2-bda8504b4f00 \N 2026-03-23 23:29:46.973175+00 68948962086 98.292.802-5 13561260 Carla Lima Almeida carla.lima.almeida.336@email.com 57973277841 Brasil São Carlos SP Avenida Tancredo de Almeida Neves 7363 Parque Santa Mônica Apto 122 1966-10-10 Campinas other single Outro \N Tenho disponibilidade no período da noite. \N \N \N Enfermeira Superior completo \N \N \N +f94e4a39-c92b-4f9b-82b4-1a2956f2140d aaaaaaaa-0002-0002-0002-000000000002 9c0780f2-bf0a-442e-9aa5-787c2f585f5c t converted 2026-03-23 23:25:39.670692+00 4214ea1c-c387-47ba-922d-5a492aca2ee7 \N 2026-03-23 23:30:08.162018+00 16487081612 43.076.886-9 13561260 Yasmin Gomes Ferreira yasmin.gomes.ferreira.69@email.com 12947152592 Brasil São Carlos SP Avenida Tancredo de Almeida Neves 8346 Parque Santa Mônica \N 1992-03-02 Bauru na single Google \N Cadastro realizado via link externo. \N \N \N Professora Pós-graduação \N \N \N +\. + + +-- +-- Data for Name: patient_invites; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_invites (id, owner_id, token, active, expires_at, max_uses, uses, created_at, tenant_id) FROM stdin; +d04d07d7-736c-4a28-8102-347cd2987bdd aaaaaaaa-0002-0002-0002-000000000002 9c0780f2-bf0a-442e-9aa5-787c2f585f5c t \N \N 0 2026-03-23 16:52:59.16431+00 \N +\. + + +-- +-- Data for Name: patient_patient_tag; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_patient_tag (owner_id, patient_id, tag_id, created_at, tenant_id) FROM stdin; +aaaaaaaa-0002-0002-0002-000000000002 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-25 20:47:18.644356+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-25 20:47:18.644356+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 4fdd639d-5f32-453f-9092-74b183c8bbfe 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-25 20:47:44.202242+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 2aaa34f7-c770-4d78-a2a2-c77bd4771c6a d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-25 20:58:12.518944+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 c193905f-e70c-4935-aec3-b9c161c6044c 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-25 20:58:37.551427+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 c193905f-e70c-4935-aec3-b9c161c6044c d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-25 20:58:37.551427+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 919ce219-3cca-4bf3-ad9d-2cc8db68cec0 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-28 10:35:53.314452+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 919ce219-3cca-4bf3-ad9d-2cc8db68cec0 d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-28 10:35:53.314452+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 778338f5-7122-4d80-a6da-88a52deee1ca 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-28 12:17:10.597057+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 ad3bb70e-881e-4ee1-b44e-436eaae2caf1 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-28 12:20:59.659774+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 542156ce-9266-487c-85e6-91a00cdfb3ea 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-28 12:43:29.045752+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 542156ce-9266-487c-85e6-91a00cdfb3ea d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-28 12:43:29.045752+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 fe670066-0d81-49ea-b177-61e83b455c59 d39ec5c3-ae75-411c-9f2c-2d19f017b53b 2026-03-29 12:18:54.625126+00 bbbbbbbb-0002-0002-0002-000000000002 +aaaaaaaa-0002-0002-0002-000000000002 fe670066-0d81-49ea-b177-61e83b455c59 69826bd2-005f-414f-a7fe-74b873e88012 2026-03-29 12:18:54.625126+00 bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: patient_status_history; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_status_history (id, patient_id, tenant_id, status_anterior, status_novo, motivo, encaminhado_para, data_saida, alterado_por, alterado_em) FROM stdin; +be09f88d-3935-423d-9ed3-04454a187fbb 2aaa34f7-c770-4d78-a2a2-c77bd4771c6a bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-25 20:58:11.897509+00 +00a38f56-b1e4-4880-9a1f-59124d05156f d8a170f0-c67e-4e6d-8dc1-b51423d20889 bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-25 20:47:35.342234+00 +d321e238-a0e9-48e3-8319-47ffd42050cf 76cec0bb-1e63-45bf-9b03-feaaa2a5d18d bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-24 10:20:35.019712+00 +a5f5e7ed-a96c-4ce0-9503-df651b7326e2 8e33a4dc-4bfb-4cce-aec0-fe5a5c91300f bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-25 20:48:39.752714+00 +187af3e8-0ede-4197-adc1-3aa89bcbde2d 3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-25 20:47:18.217202+00 +b9fd23ec-db77-47aa-bca7-6e75229fefd0 6449e64b-050b-419f-8845-029b6f10a17d bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-23 11:33:02.010795+00 +ba28b753-c79b-48ad-ab60-b0a397c71c8c 4fdd639d-5f32-453f-9092-74b183c8bbfe bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-25 20:47:43.705237+00 +a939c592-901e-4b23-bfd3-51a654f02a15 c193905f-e70c-4935-aec3-b9c161c6044c bbbbbbbb-0002-0002-0002-000000000002 \N Ativo Status inicial — migração de dados \N \N \N 2026-03-25 20:58:37.007581+00 +f0f4685b-bf2e-4f80-80d8-e54d499d7a9c 919ce219-3cca-4bf3-ad9d-2cc8db68cec0 bbbbbbbb-0002-0002-0002-000000000002 \N Ativo \N \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 10:35:52.861986+00 +9f2e7fdc-9cac-46eb-b021-6d8ef110e52b 778338f5-7122-4d80-a6da-88a52deee1ca bbbbbbbb-0002-0002-0002-000000000002 \N Ativo \N \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:17:09.861474+00 +cb51a4c4-0f23-4a0e-9f24-bd658ffdd1b1 ad3bb70e-881e-4ee1-b44e-436eaae2caf1 bbbbbbbb-0002-0002-0002-000000000002 \N Ativo \N \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:18:06.204794+00 +f298c10c-2476-4f11-adf5-486aaee37597 fe670066-0d81-49ea-b177-61e83b455c59 bbbbbbbb-0002-0002-0002-000000000002 \N Ativo \N \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:18:31.642656+00 +d5a73b0e-52e1-4316-be48-69a6f3c6445b 368f2fe0-72c9-4dca-ac40-0ee1af11b44c bbbbbbbb-0002-0002-0002-000000000002 \N Ativo \N \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:37:23.13379+00 +37f0bdda-cde0-4bbb-bbf4-7e602b5dfbe4 542156ce-9266-487c-85e6-91a00cdfb3ea bbbbbbbb-0002-0002-0002-000000000002 \N Ativo \N \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:43:28.632137+00 +\. + + +-- +-- Data for Name: patient_support_contacts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_support_contacts (id, patient_id, owner_id, tenant_id, nome, relacao, tipo, telefone, email, is_primario, created_at, updated_at) FROM stdin; +5b8d1b8f-4b2b-4550-8515-9445f14342ad ad3bb70e-881e-4ee1-b44e-436eaae2caf1 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 Henrique Ferreira cônjuge emergencia 16988280038 teste7@agenciapsi.com.br t 2026-03-28 12:20:59.681978+00 2026-03-28 12:20:59.681978+00 +34c50b28-f647-4e9e-92c8-0b94c5674409 542156ce-9266-487c-85e6-91a00cdfb3ea aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 João Lima cônjuge emergencia 16988280038 teste2@agenciapsi.com.br t 2026-03-28 12:43:29.077599+00 2026-03-28 12:43:29.077599+00 +14384d53-b292-4413-b7ae-55dd68dbdcce fe670066-0d81-49ea-b177-61e83b455c59 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 Eduarda Oliveira psiquiatra emergencia 16988280038 teste2@agenciapsi.com.br t 2026-03-29 12:18:54.647261+00 2026-03-29 12:18:54.647261+00 +\. + + +-- +-- Data for Name: patient_tags; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_tags (id, owner_id, nome, cor, is_padrao, created_at, updated_at, tenant_id) FROM stdin; +69826bd2-005f-414f-a7fe-74b873e88012 aaaaaaaa-0002-0002-0002-000000000002 teste #b53e3e f 2026-03-23 16:56:08.12277+00 2026-03-23 22:54:26.043013+00 bbbbbbbb-0002-0002-0002-000000000002 +d39ec5c3-ae75-411c-9f2c-2d19f017b53b aaaaaaaa-0002-0002-0002-000000000002 teste2 #ef4444 f 2026-03-23 23:34:11.517479+00 \N bbbbbbbb-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: patient_timeline; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patient_timeline (id, patient_id, tenant_id, evento_tipo, titulo, descricao, icone_cor, link_ref_tipo, link_ref_id, gerado_por, ocorrido_em) FROM stdin; +2e6490ab-94be-40b4-bfa7-25eb7ce8a333 919ce219-3cca-4bf3-ad9d-2cc8db68cec0 bbbbbbbb-0002-0002-0002-000000000002 status_alterado Status alterado para Ativo Paciente cadastrado green \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 10:35:52.861986+00 +2c74de8a-2f98-4cd3-bed7-9768342e29d5 778338f5-7122-4d80-a6da-88a52deee1ca bbbbbbbb-0002-0002-0002-000000000002 status_alterado Status alterado para Ativo Paciente cadastrado green \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:17:09.861474+00 +add20535-37da-4cb1-b933-9f3da92ed9b7 ad3bb70e-881e-4ee1-b44e-436eaae2caf1 bbbbbbbb-0002-0002-0002-000000000002 status_alterado Status alterado para Ativo Paciente cadastrado green \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:18:06.204794+00 +5491b6b1-e4ef-4370-acdb-d7ab834cdfbc fe670066-0d81-49ea-b177-61e83b455c59 bbbbbbbb-0002-0002-0002-000000000002 status_alterado Status alterado para Ativo Paciente cadastrado green \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:18:31.642656+00 +f5ffb4b3-58b8-4dd5-bb89-648851f2895a 368f2fe0-72c9-4dca-ac40-0ee1af11b44c bbbbbbbb-0002-0002-0002-000000000002 status_alterado Status alterado para Ativo Paciente cadastrado green \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:37:23.13379+00 +e5d9e952-1f87-4252-bb10-170e1b78963e 542156ce-9266-487c-85e6-91a00cdfb3ea bbbbbbbb-0002-0002-0002-000000000002 status_alterado Status alterado para Ativo Paciente cadastrado green \N \N aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:43:28.632137+00 +\. + + +-- +-- Data for Name: patients; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.patients (id, nome_completo, email_principal, telefone, created_at, owner_id, avatar_url, status, last_attended_at, is_native, naturalidade, data_nascimento, rg, cpf, identification_color, genero, estado_civil, email_alternativo, pais, cep, cidade, estado, endereco, numero, bairro, complemento, escolaridade, profissao, nome_parente, grau_parentesco, telefone_alternativo, onde_nos_conheceu, encaminhado_por, nome_responsavel, telefone_responsavel, cpf_responsavel, observacao_responsavel, cobranca_no_responsavel, observacoes, notas_internas, updated_at, telefone_parente, tenant_id, responsible_member_id, user_id, patient_scope, therapist_member_id, nome_social, pronomes, etnia, religiao, faixa_renda, canal_preferido, horario_contato_inicio, horario_contato_fim, idioma, origem, metodo_pagamento_preferido, motivo_saida, data_saida, encaminhado_para, risco_elevado, risco_nota, risco_sinalizado_em, risco_sinalizado_por, horario_contato, convenio, convenio_id) FROM stdin; +6449e64b-050b-419f-8845-029b6f10a17d Otto Rank otto.rank.437@example.com 86363331874 2026-03-23 11:33:02.010795+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-23 11:33:02.010795+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +76cec0bb-1e63-45bf-9b03-feaaa2a5d18d Peter Fonagy peter.fonagy.466@example.com 88283303064 2026-03-24 10:20:35.019712+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-24 10:20:35.019712+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +3dc43be7-1e3d-482b-b1c7-40d7cf6a15e9 Carla Lima Souza carla.lima.souza.882@email.com 55327597657 2026-03-25 20:47:18.217202+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Bauru 1980-10-09 498989206 41727305582 \N Feminino Casado(a) alt.709@email.com Brasil 73591-841 Santos SP Av. Brasil 5688 Jardim Paulista Apto 637 Superior completo Estudante Henrique Costa Irmão 55216555751 Site Ana Ribeiro Yasmin Araújo Lima 55209246525 85070073257 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:47:18.217202+00 55979769772 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +d8a170f0-c67e-4e6d-8dc1-b51423d20889 Anna Freud anna.freud.323@example.com 96307749614 2026-03-25 20:47:35.342234+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-25 20:47:35.342234+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +4fdd639d-5f32-453f-9092-74b183c8bbfe Daniel Oliveira Silva daniel.oliveira.silva.779@email.com 55629968595 2026-03-25 20:47:43.705237+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Ribeirão Preto 2018-06-14 644776179 07710834329 \N Feminino Solteiro(a) alt.72@email.com Brasil 43519-831 Ribeirão Preto RS Rua XV de Novembro 5937 Vila Prado Apto 78 Superior incompleto Autônomo Felipe Lima Irmã 55619689156 Outro Vanessa Martins Vanessa Carvalho Barbosa 55209874519 24378696207 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:47:43.705237+00 55738955502 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +8e33a4dc-4bfb-4cce-aec0-fe5a5c91300f John Bowlby john.bowlby.398@example.com 84123228043 2026-03-25 20:48:39.752714+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-25 20:48:39.752714+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +2aaa34f7-c770-4d78-a2a2-c77bd4771c6a Yasmin Martins Lima yasmin.martins.lima.810@email.com 55979361027 2026-03-25 20:58:11.897509+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Santos 1950-10-11 25599572 65091040676 \N Prefere não informar Divorciado(a) alt.364@email.com Brasil 55632-609 Santos RJ Av. Brasil 1268 Jardim Paulista Apto 117 Superior incompleto Autônomo Bruno Martins Pai 55819227599 Google Marcos Martins Ana Oliveira Martins 55129232266 29325344165 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:58:11.897509+00 55529945515 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +c193905f-e70c-4935-aec3-b9c161c6044c Otávio Souza Ferreira888 otavio.souza.ferreira.212@email.com 55519100542 2026-03-25 20:58:37.007581+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Campinas 2018-03-10 249907937 85207459899 \N Feminino Solteiro(a) alt.523@email.com Brasil 80355-723 Araraquara RS Rua XV de Novembro 2199 Vila Prado Apto 249 Ensino Médio Professora Felipe Carvalho Pai 55759833223 Threads Yasmin Carvalho Sabrina Martins Santos 55749688637 89130651778 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-25 20:58:37.007581+00 55225172394 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +919ce219-3cca-4bf3-ad9d-2cc8db68cec0 João Almeida Martins joao.almeida.martins.979@email.com 55309899385 2026-03-28 10:35:52.861986+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f Santos 2009-10-23 203656518 40331112558 \N Outro União estável alt.44@email.com Brasil 96195-389 São Carlos SP Rua XV de Novembro 9156 Santa Felícia Apto 486 Pós-graduação Autônomo João Ferreira Pai 55289171423 Outro Larissa Almeida Eduarda Almeida Almeida 55141999898 38448234766 Responsável ciente do contrato. t Paciente relata ansiedade e sobrecarga emocional. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-28 10:35:52.861986+00 55389349597 bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +778338f5-7122-4d80-a6da-88a52deee1ca Larissa Souza Santos teste7@agenciapsi.com.br 16988280038 2026-03-28 12:17:09.861474+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f São Carlos 1984-05-14 914872638 88993691274 \N Não-binário Casado(a) teste1@agenciapsi.com.br Brasil 13561-260 São Carlos SP Rua Conde do Pinhal 457 Centro Apartamento Ensino médio completo Professora \N \N 16996005268 Indicação teste Responsavel 11111111111 11111111111 Obs responsavel t Próximo ao posto de saúde do centro. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-28 12:17:09.861474+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N Híbrido \N \N ela/dela Indígena \N \N WhatsApp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N 08h–20h teste ec7aa65f-cbd2-48a5-9ec0-368a788438a7 +368f2fe0-72c9-4dca-ac40-0ee1af11b44c Otto Rank otto.rank.724@example.com 67720136104 2026-03-28 12:37:23.13379+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f \N \N \N \N \N \N \N \N Brasil \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N \N f \N \N 2026-03-28 12:37:23.13379+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N clinic \N \N \N \N \N \N whatsapp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N \N \N \N +ad3bb70e-881e-4ee1-b44e-436eaae2caf1 Larissa Souza Santos teste7@agenciapsi.com.br 16988280038 2026-03-28 12:18:06.204794+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f São Carlos 1984-05-14 914872638 88993691274 \N Não-binário Casado(a) teste1@agenciapsi.com.br Brasil 13561-260 São Carlos SP Rua Conde do Pinhal 457 Centro Apartamento Ensino médio completo Professora \N \N 16996005268 Indicação teste Responsavel 11111111111 11111111111 Obs responsavel t Próximo ao posto de saúde do centro. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-28 12:20:59.238927+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N Híbrido \N \N ela/dela Indígena \N \N WhatsApp 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N 08h–20h teste ec7aa65f-cbd2-48a5-9ec0-368a788438a7 +542156ce-9266-487c-85e6-91a00cdfb3ea Felipe Santos Pereira teste2@agenciapsi.com.br 16988280038 2026-03-28 12:43:28.632137+00 aaaaaaaa-0002-0002-0002-000000000002 \N Ativo \N f São Carlos 2001-10-05 922577885 55094969838 \N Masculino Casado(a) teste3@agenciapsi.com.br Brasil 13561-260 São Carlos SP Rua Conde do Pinhal 457 Centro Apartamento Superior incompleto Autônomo \N \N 16996005268 Instagram teste2 (2), teste \N \N \N \N f Próximo ao posto de saúde do centro. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-28 12:43:28.632137+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N Híbrido \N \N ele/dele Preta \N \N Telefone 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N 13h–18h teste ec7aa65f-cbd2-48a5-9ec0-368a788438a7 +fe670066-0d81-49ea-b177-61e83b455c59 Henrique Ferreira Ferreira 88 teste2@agenciapsi.com.br 16988280038 2026-03-28 12:18:31.642656+00 aaaaaaaa-0002-0002-0002-000000000002 http://127.0.0.1:54321/storage/v1/object/public/avatars/owners/aaaaaaaa-0002-0002-0002-000000000002/patients/fe670066-0d81-49ea-b177-61e83b455c59/avatar.png Ativo \N f São Carlos 1958-08-16 851689197 29628589709 \N Masculino União estável teste3@agenciapsi.com.br Brasil 13561-260 São Carlos SP Rua Conde do Pinhal 457 Centro Apartamento Mestrado Professora \N \N 16996005268 Site teste, teste2 (2) resposnavel 11111111111 11111111111 obs t Próximo ao posto de saúde do centro. Paciente apresenta discurso organizado. Acompanhar evolução clínica. 2026-03-29 12:18:54.15896+00 \N bbbbbbbb-0002-0002-0002-000000000002 72be63ef-bc2c-4c8e-8522-0c6d64746bbc \N Híbrido \N teste ela/dela Amarela \N \N Telefone 08:00:00 20:00:00 pt-BR \N \N \N \N \N f \N \N \N 08h–12h teste ec7aa65f-cbd2-48a5-9ec0-368a788438a7 +\. + + +-- +-- Data for Name: payment_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.payment_settings (id, owner_id, tenant_id, pix_ativo, pix_tipo, pix_chave, pix_nome_titular, deposito_ativo, deposito_banco, deposito_agencia, deposito_conta, deposito_tipo_conta, deposito_titular, deposito_cpf_cnpj, dinheiro_ativo, cartao_ativo, cartao_instrucao, convenio_ativo, convenio_lista, observacoes_pagamento, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: plan_features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_features (plan_id, feature_id, enabled, limits, created_at) FROM stdin; +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 t {"max_patients": 9999, "max_therapists": 999} 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a56482a1-0787-49da-90a7-e1857488734a t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 9999} 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 bf133ad1-da8e-4ea9-bd66-21901cb50075 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a830e45b-3bb4-4b17-812d-fe83777a2377 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 9999} 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 336aeeba-b18e-4e68-8303-d42ba09f4b20 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 30c9cdd5-7c8c-44d9-8c0b-614165bb9496 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 74fc1321-4d17-49c3-b72e-db3a7f4be451 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 90e92108-8124-40ee-88a0-f0ecafb57d76 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 f393178c-284d-422f-b096-8793f85428d5 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 d6f54674-ea8b-484b-af0e-99127a510da2 t \N 2026-03-23 14:18:07.731209+00 +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 a5593d96-dd95-46bb-bef0-bd379b56ad50 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 t {"max_patients": 30, "max_therapists": 5} 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a56482a1-0787-49da-90a7-e1857488734a t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 40} 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 bf133ad1-da8e-4ea9-bd66-21901cb50075 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a830e45b-3bb4-4b17-812d-fe83777a2377 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 50} 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 90e92108-8124-40ee-88a0-f0ecafb57d76 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 336aeeba-b18e-4e68-8303-d42ba09f4b20 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 30c9cdd5-7c8c-44d9-8c0b-614165bb9496 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 74fc1321-4d17-49c3-b72e-db3a7f4be451 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 f393178c-284d-422f-b096-8793f85428d5 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 d6f54674-ea8b-484b-af0e-99127a510da2 f \N 2026-03-23 14:18:07.731209+00 +01a5867f-0705-4714-ac97-a23470949157 a5593d96-dd95-46bb-bef0-bd379b56ad50 f \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 9b36c65d-b3b3-4bed-b6d5-f7ee8c087c80 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 9999} 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 bf133ad1-da8e-4ea9-bd66-21901cb50075 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 a830e45b-3bb4-4b17-812d-fe83777a2377 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 9999} 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 336aeeba-b18e-4e68-8303-d42ba09f4b20 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 90e92108-8124-40ee-88a0-f0ecafb57d76 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 f393178c-284d-422f-b096-8793f85428d5 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 d6f54674-ea8b-484b-af0e-99127a510da2 t \N 2026-03-23 14:18:07.731209+00 +82067ba7-16f0-4803-b36f-4c4e8919d4b4 a5593d96-dd95-46bb-bef0-bd379b56ad50 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 5e539124-630f-4c2a-a9de-7999317085e6 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a a74fef14-c9d9-4884-ba45-f81c60e0783a t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 57f631a1-9ebe-480b-a2cb-144ad32ff5f0 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 4e5bc50b-e339-42fe-9d91-61e8555f83e7 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 53a48c3b-0617-4618-adf8-f3a255c51ee4 t {"sessions_per_month": 40} 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 5739aa27-b089-4b15-b149-31b13d768825 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 0bfe0b1c-8c3d-4c0c-af29-2ddc24f31bc7 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a b3efa25d-60a4-4974-8153-6ec098b3d477 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a bf133ad1-da8e-4ea9-bd66-21901cb50075 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a f5d66212-fd73-4472-a306-07928e5deaec t {"reminders_per_month": 50} 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 8cc81988-d02a-4542-9cb2-ce2ed7c18d60 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a c109ad27-0edf-4774-91a7-94dac4faab49 t \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a 90e92108-8124-40ee-88a0-f0ecafb57d76 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a f393178c-284d-422f-b096-8793f85428d5 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a d6f54674-ea8b-484b-af0e-99127a510da2 f \N 2026-03-23 14:18:07.731209+00 +c56fe2a8-2c17-4048-adc7-ff7fbd89461a a5593d96-dd95-46bb-bef0-bd379b56ad50 f \N 2026-03-23 14:18:07.731209+00 +8c4895a3-e12d-48de-a078-efb8a4ea2eb2 9ab8bdbb-838b-4946-aa5d-fd9cfdd257b3 t \N 2026-03-23 14:18:07.731209+00 +8c4895a3-e12d-48de-a078-efb8a4ea2eb2 1167b54a-0e93-43a2-94d7-c12e64eb56de t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 9ab8bdbb-838b-4946-aa5d-fd9cfdd257b3 t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 1167b54a-0e93-43a2-94d7-c12e64eb56de t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 761e4495-b46a-4791-9519-86ffe48dc47f t \N 2026-03-23 14:18:07.731209+00 +ca28e46c-0687-45d5-9406-0a0f56a5b625 7e82ee01-44f6-4b3f-9861-840c58e13f58 t \N 2026-03-23 14:18:07.731209+00 +\. + + +-- +-- Data for Name: plan_prices; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_prices (id, plan_id, currency, "interval", amount_cents, is_active, active_from, active_to, source, provider, provider_price_id, created_at) FROM stdin; +37510504-4617-4421-9979-4249778bd5ae 82067ba7-16f0-4803-b36f-4c4e8919d4b4 BRL month 4900 t 2026-03-01 09:25:03.878498+00 \N manual \N \N 2026-03-01 09:25:03.878498+00 +225afd5a-9f30-46bc-a0df-5eb8f91660cb 82067ba7-16f0-4803-b36f-4c4e8919d4b4 BRL year 49000 t 2026-03-01 09:25:03.878498+00 \N manual \N \N 2026-03-01 09:25:03.878498+00 +124779b4-362d-4890-9631-747021ecc1c0 a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 BRL month 14900 t 2026-03-01 09:30:06.50975+00 \N manual \N \N 2026-03-01 09:30:06.50975+00 +73908784-6299-45c8-b547-e1556b45c292 a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 BRL year 149000 t 2026-03-01 09:30:06.50975+00 \N manual \N \N 2026-03-01 09:30:06.50975+00 +\. + + +-- +-- Data for Name: plan_public; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_public (plan_id, public_name, public_description, badge, is_featured, is_visible, sort_order, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: plan_public_bullets; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plan_public_bullets (id, plan_id, text, sort_order, highlight, created_at) FROM stdin; +\. + + +-- +-- Data for Name: plans; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.plans (id, key, name, description, is_active, created_at, price_cents, currency, billing_interval, target, max_supervisees) FROM stdin; +984c1f29-a975-4208-93ac-2118ed1039b7 patient_free Paciente Free Plano gratuito para pacientes t 2026-03-03 22:40:11.413107+00 0 BRL month patient \N +c56fe2a8-2c17-4048-adc7-ff7fbd89461a therapist_free THERAPIST FREE Plano gratuito para terapeutas. t 2026-03-01 09:40:48.439668+00 0 BRL month therapist \N +82067ba7-16f0-4803-b36f-4c4e8919d4b4 therapist_pro THERAPIST PRO Plano completo para terapeutas. t 2026-03-01 09:25:03.878498+00 4900 BRL month therapist \N +01a5867f-0705-4714-ac97-a23470949157 clinic_free CLINIC FREE Plano gratuito para clínicas iniciarem. t 2026-03-01 09:25:03.878498+00 0 BRL month clinic \N +a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145 clinic_pro CLINIC PRO Plano completo para clínicas. t 2026-03-01 09:30:06.50975+00 14900 BRL month clinic \N +8c4895a3-e12d-48de-a078-efb8a4ea2eb2 supervisor_free Supervisor Free Plano gratuito de supervisão. Até 3 terapeutas supervisionados. t 2026-03-05 00:58:17.218326+00 0 BRL month supervisor 3 +ca28e46c-0687-45d5-9406-0a0f56a5b625 supervisor_pro Supervisor PRO Plano profissional de supervisão. Até 20 terapeutas supervisionados. t 2026-03-05 00:58:17.218326+00 0 BRL month supervisor 20 +\. + + +-- +-- Data for Name: professional_pricing; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.professional_pricing (id, owner_id, tenant_id, determined_commitment_id, price, notes, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: profiles; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.profiles (id, role, full_name, created_at, updated_at, avatar_url, phone, bio, language, timezone, notify_system_email, notify_reminders, notify_news, account_type, platform_roles, nickname, work_description, work_description_other, site_url, social_instagram, social_youtube, social_facebook, social_x, social_custom) FROM stdin; +aaaaaaaa-0001-0001-0001-000000000001 portal_user Ana Paciente 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N pt-BR America/Sao_Paulo t t f patient {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0002-0002-0002-000000000002 tenant_member Leonardo Nohama223 2026-03-23 10:46:29.876072+00 2026-03-27 14:18:26.567019+00 http://127.0.0.1:54321/storage/v1/object/public/avatars/aaaaaaaa-0002-0002-0002-000000000002/avatar.webp (11) 11111-1111 Bio 2asd pt-BR America/Sao_Paulo t t t therapist {} Léo psicologo_clinico \N \N \N \N \N \N [] +aaaaaaaa-0006-0006-0006-000000000006 saas_admin Admin Plataforma 2026-03-23 10:46:29.876072+00 2026-03-23 10:46:29.876072+00 \N \N \N pt-BR America/Sao_Paulo t t f free {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0007-0007-0007-000000000007 tenant_member Carlos Supervisor 2026-03-23 14:18:05.215881+00 2026-03-23 14:18:05.215881+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0009-0009-0009-000000000009 tenant_member Eva Terapeuta 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0010-0010-0010-000000000010 tenant_member Felipe Terapeuta 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0011-0011-0011-000000000011 tenant_member Gabriela Secretária 2026-03-23 14:18:06.087973+00 2026-03-23 14:18:06.087973+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0003-0003-0003-000000000003 tenant_member Clínica Espaço Psi 2026-03-23 10:46:29.876072+00 2026-03-23 14:18:43.066684+00 \N \N \N pt-BR America/Sao_Paulo t t f clinic {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0004-0004-0004-000000000004 tenant_member Clínica Mente Sã 2026-03-23 10:46:29.876072+00 2026-03-23 14:18:59.312611+00 \N \N \N pt-BR America/Sao_Paulo t t f clinic {} \N \N \N \N \N \N \N \N [] +aaaaaaaa-0005-0005-0005-000000000005 tenant_member Leonardo Nohama 2026-03-23 10:46:29.876072+00 2026-03-26 17:19:07.27483+00 \N (16) 98828-0038 bio curta pt-BR America/Sao_Paulo t t f clinic {} Léo psicanalista \N \N \N \N \N \N [] +aaaaaaaa-0008-0008-0008-000000000008 tenant_member Diana Editora 2026-03-23 14:18:05.215881+00 2026-03-26 19:46:29.161046+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {editor} \N \N \N \N \N \N \N \N [] +384a69d8-b7cd-40ac-9d3c-764c93532b66 portal_user \N 2026-03-26 19:47:30.477551+00 2026-03-26 19:47:30.68134+00 \N \N \N pt-BR America/Sao_Paulo t t f therapist {} \N \N \N \N \N \N \N \N [] +\. + + +-- +-- Data for Name: recurrence_exceptions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.recurrence_exceptions (id, recurrence_id, tenant_id, original_date, type, new_date, new_start_time, new_end_time, modalidade, observacoes, titulo_custom, extra_fields, reason, agenda_evento_id, created_at) FROM stdin; +\. + + +-- +-- Data for Name: recurrence_rule_services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.recurrence_rule_services (id, rule_id, service_id, quantity, unit_price, discount_pct, discount_flat, final_price, created_at) FROM stdin; +\. + + +-- +-- Data for Name: recurrence_rules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.recurrence_rules (id, tenant_id, owner_id, therapist_id, patient_id, determined_commitment_id, type, "interval", weekdays, start_time, end_time, timezone, duration_min, start_date, end_date, max_occurrences, open_ended, modalidade, titulo_custom, observacoes, extra_fields, status, created_at, updated_at, price, insurance_plan_id, insurance_guide_number, insurance_value, insurance_plan_service_id) FROM stdin; +\. + + +-- +-- Data for Name: saas_admins; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_admins (user_id, created_at) FROM stdin; +aaaaaaaa-0006-0006-0006-000000000006 2026-03-23 10:46:29.876072+00 +\. + + +-- +-- Data for Name: saas_doc_votos; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_doc_votos (id, doc_id, user_id, util, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: saas_docs; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_docs (id, titulo, conteudo, medias, tipo_acesso, pagina_path, docs_relacionados, ativo, ordem, created_at, updated_at, categoria, exibir_no_faq, votos_util, votos_nao_util) FROM stdin; +\. + + +-- +-- Data for Name: saas_faq; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_faq (id, pergunta, categoria, publico, votos, titulo, conteudo, tipo_acesso, pagina_path, pagina_label, medias, faqs_relacionados, ativo, ordem, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: saas_faq_itens; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.saas_faq_itens (id, doc_id, pergunta, resposta, ordem, ativo, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: services; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.services (id, owner_id, tenant_id, name, description, price, duration_min, active, created_at, updated_at) FROM stdin; +2f2d31a7-abef-4efc-89fc-794a458682cb aaaaaaaa-0005-0005-0005-000000000005 bbbbbbbb-0005-0005-0005-000000000005 Atendimento padrão \N 0.00 50 t 2026-03-26 16:50:09.010706+00 2026-03-26 16:50:09.010706+00 +0166ac88-669d-4f0c-8769-17eb0029c8a9 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 Atendimento padrão \N 40.00 50 t 2026-03-26 21:48:12.84259+00 2026-03-26 21:50:44.411125+00 +045843e1-815a-437b-b11a-ea9136e10cdf aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste \N 1.00 50 t 2026-03-27 11:40:12.054774+00 2026-03-27 11:40:12.054774+00 +36cc58d9-7beb-451b-b630-e81271d45de4 aaaaaaaa-0002-0002-0002-000000000002 bbbbbbbb-0002-0002-0002-000000000002 teste \N 2.00 50 t 2026-03-27 13:03:35.570064+00 2026-03-27 13:03:35.570064+00 +\. + + +-- +-- Data for Name: subscription_events; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_events (id, subscription_id, owner_id, event_type, old_plan_id, new_plan_id, created_at, created_by, source, reason, metadata, owner_type, owner_ref) FROM stdin; +b9180edb-76a7-4f0b-be24-92f056091f14 6864f71c-c4d4-4d7e-9897-89a644002b6d aaaaaaaa-0002-0002-0002-000000000002 plan_changed c56fe2a8-2c17-4048-adc7-ff7fbd89461a 82067ba7-16f0-4803-b36f-4c4e8919d4b4 2026-03-27 09:41:23.50964+00 aaaaaaaa-0002-0002-0002-000000000002 dev_menu Plan change via DEV menu {"new_plan": "82067ba7-16f0-4803-b36f-4c4e8919d4b4", "new_plan_key": "therapist_pro", "previous_plan": "c56fe2a8-2c17-4048-adc7-ff7fbd89461a", "new_plan_target": "therapist"} therapist aaaaaaaa-0002-0002-0002-000000000002 +\. + + +-- +-- Data for Name: subscription_intents_legacy; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_intents_legacy (id, user_id, email, plan_key, "interval", amount_cents, currency, status, source, notes, created_at, paid_at, tenant_id, created_by_user_id) FROM stdin; +\. + + +-- +-- Data for Name: subscription_intents_personal; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_intents_personal (id, user_id, created_by_user_id, email, plan_id, plan_key, "interval", amount_cents, currency, status, source, notes, created_at, paid_at, subscription_id) FROM stdin; +\. + + +-- +-- Data for Name: subscription_intents_tenant; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscription_intents_tenant (id, user_id, created_by_user_id, email, plan_id, plan_key, "interval", amount_cents, currency, status, source, notes, created_at, paid_at, tenant_id, subscription_id) FROM stdin; +\. + + +-- +-- Data for Name: subscriptions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.subscriptions (id, user_id, plan_id, status, current_period_start, current_period_end, cancel_at_period_end, provider, provider_customer_id, provider_subscription_id, created_at, updated_at, tenant_id, plan_key, "interval", source, started_at, canceled_at, activated_at, past_due_since, suspended_at, suspended_reason, cancelled_at, cancel_reason, expired_at) FROM stdin; +61f81f06-c718-4d52-8063-67c38c1c1df9 aaaaaaaa-0001-0001-0001-000000000001 984c1f29-a975-4208-93ac-2118ed1039b7 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N patient_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +8716fb6d-372f-4560-98a5-68c40aec96dc aaaaaaaa-0007-0007-0007-000000000007 8c4895a3-e12d-48de-a078-efb8a4ea2eb2 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N supervisor_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +3fc090ba-3e9a-47ef-9711-edf9a3fd1795 aaaaaaaa-0008-0008-0008-000000000008 c56fe2a8-2c17-4048-adc7-ff7fbd89461a active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N therapist_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +cc4c4eb4-c9e5-4ce7-a705-9f5dc415fcb4 aaaaaaaa-0009-0009-0009-000000000009 c56fe2a8-2c17-4048-adc7-ff7fbd89461a active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N therapist_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +d2516bea-712d-453a-a5ed-9e40456f9aca aaaaaaaa-0010-0010-0010-000000000010 c56fe2a8-2c17-4048-adc7-ff7fbd89461a active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 \N therapist_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +bd6dd2c0-9c50-4939-8fc9-41fd67423a3e \N 01a5867f-0705-4714-ac97-a23470949157 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 bbbbbbbb-0003-0003-0003-000000000003 clinic_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +505d05fa-a6db-46b7-8cbf-bb6b12fb8e2b \N 01a5867f-0705-4714-ac97-a23470949157 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 bbbbbbbb-0004-0004-0004-000000000004 clinic_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +27eef221-7db2-442a-99f3-989f910cdcb4 \N 01a5867f-0705-4714-ac97-a23470949157 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-23 14:18:08.130849+00 bbbbbbbb-0005-0005-0005-000000000005 clinic_free month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +6864f71c-c4d4-4d7e-9897-89a644002b6d aaaaaaaa-0002-0002-0002-000000000002 82067ba7-16f0-4803-b36f-4c4e8919d4b4 active 2026-03-23 14:18:08.130849+00 2027-03-23 14:18:08.130849+00 f manual \N \N 2026-03-23 14:18:08.130849+00 2026-03-27 09:41:23.50964+00 \N therapist_pro month seed 2026-03-23 14:18:08.130849+00 \N 2026-03-23 14:18:08.130849+00 \N \N \N \N \N \N +\. + + +-- +-- Data for Name: support_sessions; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.support_sessions (id, tenant_id, admin_id, token, expires_at, created_at) FROM stdin; +\. + + +-- +-- Data for Name: tenant_feature_exceptions_log; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_feature_exceptions_log (id, tenant_id, feature_key, enabled, reason, created_by, created_at) FROM stdin; +\. + + +-- +-- Data for Name: tenant_features; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_features (tenant_id, feature_key, enabled, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: tenant_invites; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_invites (id, tenant_id, email, role, token, invited_by, created_at, expires_at, accepted_at, accepted_by, revoked_at, revoked_by) FROM stdin; +\. + + +-- +-- Data for Name: tenant_members; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_members (id, tenant_id, user_id, role, status, created_at) FROM stdin; +72be63ef-bc2c-4c8e-8522-0c6d64746bbc bbbbbbbb-0002-0002-0002-000000000002 aaaaaaaa-0002-0002-0002-000000000002 tenant_admin active 2026-03-23 10:46:29.876072+00 +07974953-677e-4941-b90f-1ae15ffbbbf6 bbbbbbbb-0003-0003-0003-000000000003 aaaaaaaa-0003-0003-0003-000000000003 tenant_admin active 2026-03-23 10:46:29.876072+00 +11417585-46af-4faa-a34a-dc23a36c5704 bbbbbbbb-0004-0004-0004-000000000004 aaaaaaaa-0004-0004-0004-000000000004 tenant_admin active 2026-03-23 10:46:29.876072+00 +cd22a662-2b16-4cfe-a9e6-15ccb8e7d67e bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0005-0005-0005-000000000005 tenant_admin active 2026-03-23 10:46:29.876072+00 +e40559cd-43af-4547-b447-2cc7bbff5ad7 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0002-0002-0002-000000000002 therapist active 2026-03-23 10:46:29.876072+00 +0ee00481-77f2-4bc0-ab09-3bc441c03b1b bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0007-0007-0007-000000000007 supervisor active 2026-03-23 14:18:05.215881+00 +344a6ae3-379a-4776-9441-8ac9f8733c99 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0008-0008-0008-000000000008 therapist active 2026-03-23 14:18:05.215881+00 +aac8e805-da4f-4089-b2e1-0b32b679d0dd bbbbbbbb-0009-0009-0009-000000000009 aaaaaaaa-0009-0009-0009-000000000009 tenant_admin active 2026-03-23 14:18:06.087973+00 +2633ed47-76e8-4a30-8e3c-07bec4f7c9cd bbbbbbbb-0010-0010-0010-000000000010 aaaaaaaa-0010-0010-0010-000000000010 tenant_admin active 2026-03-23 14:18:06.087973+00 +a72fa56d-04e4-4e23-853c-fd4926b263c3 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0009-0009-0009-000000000009 therapist active 2026-03-23 14:18:06.087973+00 +677f130c-06bd-44d7-b283-844351d65c45 bbbbbbbb-0005-0005-0005-000000000005 aaaaaaaa-0010-0010-0010-000000000010 therapist active 2026-03-23 14:18:06.087973+00 +978ce15a-8dc5-4540-8430-f9e1e19d4952 bbbbbbbb-0004-0004-0004-000000000004 aaaaaaaa-0011-0011-0011-000000000011 clinic_admin active 2026-03-23 14:18:06.087973+00 +da8fd413-1710-40f2-85a0-5af9cf2c78d8 a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 aaaaaaaa-0008-0008-0008-000000000008 tenant_admin active 2026-03-26 19:46:29.161046+00 +fb7a3517-0479-42ac-8c37-ec6ffc03c5be 1e98ca49-a46c-4701-847b-145a14d53d19 384a69d8-b7cd-40ac-9d3c-764c93532b66 tenant_admin active 2026-03-26 19:47:30.68134+00 +\. + + +-- +-- Data for Name: tenant_modules; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenant_modules (id, owner_id, module_id, status, settings, provider, provider_item_id, installed_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: tenants; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.tenants (id, name, created_at, kind) FROM stdin; +bbbbbbbb-0009-0009-0009-000000000009 Eva Terapeuta 2026-03-23 10:47:12.826179+00 therapist +bbbbbbbb-0010-0010-0010-000000000010 Felipe Terapeuta 2026-03-23 10:47:12.826179+00 therapist +bbbbbbbb-0003-0003-0003-000000000003 Clínica Espaço Psi 2026-03-23 10:46:29.876072+00 clinic_coworking +bbbbbbbb-0004-0004-0004-000000000004 Clínica Mente Sã 2026-03-23 10:46:29.876072+00 clinic_reception +bbbbbbbb-0005-0005-0005-000000000005 Clínica Bem Estar 2026-03-23 10:46:29.876072+00 clinic_full +a424b2cb-d9a5-474c-8c3e-f4008a2c31d1 Diana Editora 2026-03-26 19:46:29.161046+00 therapist +1e98ca49-a46c-4701-847b-145a14d53d19 terapeuta2 2026-03-26 19:47:30.68134+00 therapist +bbbbbbbb-0002-0002-0002-000000000002 Bruno Terapeuta 2026-03-23 10:46:29.876072+00 therapist +\. + + +-- +-- Data for Name: therapist_payout_records; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.therapist_payout_records (payout_id, financial_record_id) FROM stdin; +\. + + +-- +-- Data for Name: therapist_payouts; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.therapist_payouts (id, owner_id, tenant_id, period_start, period_end, total_sessions, gross_amount, clinic_fee_total, net_amount, status, paid_at, notes, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: twilio_subaccount_usage; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.twilio_subaccount_usage (id, tenant_id, channel_id, twilio_subaccount_sid, period_start, period_end, messages_sent, messages_delivered, messages_failed, cost_usd, cost_brl, revenue_brl, usd_brl_rate, synced_at, created_at) FROM stdin; +\. + + +-- +-- Data for Name: user_settings; Type: TABLE DATA; Schema: public; Owner: - +-- + +COPY public.user_settings (user_id, theme_mode, preset, primary_color, surface_color, menu_mode, created_at, updated_at, layout_variant) FROM stdin; +aaaaaaaa-0002-0002-0002-000000000002 light Lara rose soho static 2026-03-25 18:47:05.458455+00 2026-03-28 10:24:35.930276+00 classic +aaaaaaaa-0005-0005-0005-000000000005 dark Aura orange slate static 2026-03-26 10:14:04.329765+00 2026-03-26 17:19:07.587294+00 classic +\. + + +-- +-- Data for Name: messages_2026_03_26; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.messages_2026_03_26 (topic, extension, payload, event, private, updated_at, inserted_at, id) FROM stdin; +\. + + +-- +-- Data for Name: messages_2026_03_27; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.messages_2026_03_27 (topic, extension, payload, event, private, updated_at, inserted_at, id) FROM stdin; +\. + + +-- +-- Data for Name: messages_2026_03_28; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.messages_2026_03_28 (topic, extension, payload, event, private, updated_at, inserted_at, id) FROM stdin; +\. + + +-- +-- Data for Name: messages_2026_03_29; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.messages_2026_03_29 (topic, extension, payload, event, private, updated_at, inserted_at, id) FROM stdin; +\. + + +-- +-- Data for Name: messages_2026_03_30; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.messages_2026_03_30 (topic, extension, payload, event, private, updated_at, inserted_at, id) FROM stdin; +\. + + +-- +-- Data for Name: messages_2026_03_31; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.messages_2026_03_31 (topic, extension, payload, event, private, updated_at, inserted_at, id) FROM stdin; +\. + + +-- +-- Data for Name: messages_2026_04_01; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.messages_2026_04_01 (topic, extension, payload, event, private, updated_at, inserted_at, id) FROM stdin; +\. + + +-- +-- Data for Name: schema_migrations; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.schema_migrations (version, inserted_at) FROM stdin; +20211116024918 2026-03-23 10:13:17 +20211116045059 2026-03-23 10:13:17 +20211116050929 2026-03-23 10:13:17 +20211116051442 2026-03-23 10:13:17 +20211116212300 2026-03-23 10:13:17 +20211116213355 2026-03-23 10:13:17 +20211116213934 2026-03-23 10:13:17 +20211116214523 2026-03-23 10:13:17 +20211122062447 2026-03-23 10:13:17 +20211124070109 2026-03-23 10:13:17 +20211202204204 2026-03-23 10:13:17 +20211202204605 2026-03-23 10:13:17 +20211210212804 2026-03-23 10:13:17 +20211228014915 2026-03-23 10:13:17 +20220107221237 2026-03-23 10:13:17 +20220228202821 2026-03-23 10:13:17 +20220312004840 2026-03-23 10:13:17 +20220603231003 2026-03-23 10:13:17 +20220603232444 2026-03-23 10:13:17 +20220615214548 2026-03-23 10:13:17 +20220712093339 2026-03-23 10:13:17 +20220908172859 2026-03-23 10:13:17 +20220916233421 2026-03-23 10:13:17 +20230119133233 2026-03-23 10:13:17 +20230128025114 2026-03-23 10:13:17 +20230128025212 2026-03-23 10:13:17 +20230227211149 2026-03-23 10:13:17 +20230228184745 2026-03-23 10:13:17 +20230308225145 2026-03-23 10:13:17 +20230328144023 2026-03-23 10:13:17 +20231018144023 2026-03-23 10:13:17 +20231204144023 2026-03-23 10:13:17 +20231204144024 2026-03-23 10:13:17 +20231204144025 2026-03-23 10:13:17 +20240108234812 2026-03-23 10:13:17 +20240109165339 2026-03-23 10:13:17 +20240227174441 2026-03-23 10:13:17 +20240311171622 2026-03-23 10:13:17 +20240321100241 2026-03-23 10:13:17 +20240401105812 2026-03-23 10:13:17 +20240418121054 2026-03-23 10:13:17 +20240523004032 2026-03-23 10:13:17 +20240618124746 2026-03-23 10:13:18 +20240801235015 2026-03-23 10:13:18 +20240805133720 2026-03-23 10:13:18 +20240827160934 2026-03-23 10:13:18 +20240919163303 2026-03-23 10:13:18 +20240919163305 2026-03-23 10:13:18 +20241019105805 2026-03-23 10:13:18 +20241030150047 2026-03-23 10:13:18 +20241108114728 2026-03-23 10:13:18 +20241121104152 2026-03-23 10:13:18 +20241130184212 2026-03-23 10:13:18 +20241220035512 2026-03-23 10:13:18 +20241220123912 2026-03-23 10:13:18 +20241224161212 2026-03-23 10:13:18 +20250107150512 2026-03-23 10:13:18 +20250110162412 2026-03-23 10:13:18 +20250123174212 2026-03-23 10:13:18 +20250128220012 2026-03-23 10:13:18 +20250506224012 2026-03-23 10:13:18 +20250523164012 2026-03-23 10:13:18 +20250714121412 2026-03-23 10:13:18 +20250905041441 2026-03-23 10:13:18 +20251103001201 2026-03-23 10:13:18 +\. + + +-- +-- Data for Name: subscription; Type: TABLE DATA; Schema: realtime; Owner: - +-- + +COPY realtime.subscription (id, subscription_id, entity, filters, claims, created_at) FROM stdin; +1029 602af36c-2b6f-11f1-9a20-16cf5c19ef25 public.notifications {"(owner_id,eq,aaaaaaaa-0002-0002-0002-000000000002)"} {"aal": "aal1", "amr": [{"method": "password", "timestamp": 1774783630}], "aud": "authenticated", "exp": 1774790902, "iat": 1774787302, "iss": "http://127.0.0.1:54321/auth/v1", "sub": "aaaaaaaa-0002-0002-0002-000000000002", "role": "authenticated", "email": "terapeuta@agenciapsi.com.br", "phone": "", "session_id": "5af1b6d5-ae69-4eaa-b951-34e5ee9df49a", "app_metadata": {"provider": "email", "providers": ["email"]}, "is_anonymous": false, "user_metadata": {"name": "Bruno Terapeuta", "full_name": "Leonardo Nohama", "avatar_url": "http://127.0.0.1:54321/storage/v1/object/public/avatars/aaaaaaaa-0002-0002-0002-000000000002/avatar.jpg"}} 2026-03-29 13:01:18.000384 +\. + + +-- +-- Data for Name: buckets; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.buckets (id, name, owner, created_at, updated_at, public, avif_autodetection, file_size_limit, allowed_mime_types, owner_id, type) FROM stdin; +avatars avatars \N 2026-03-26 21:49:42.507669+00 2026-03-26 21:49:42.507669+00 t f \N \N \N STANDARD +logos logos \N 2026-03-26 21:49:42.507669+00 2026-03-26 21:49:42.507669+00 t f \N \N \N STANDARD +\. + + +-- +-- Data for Name: buckets_analytics; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.buckets_analytics (name, type, format, created_at, updated_at, id, deleted_at) FROM stdin; +\. + + +-- +-- Data for Name: buckets_vectors; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.buckets_vectors (id, type, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: iceberg_namespaces; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.iceberg_namespaces (id, bucket_name, name, created_at, updated_at, metadata, catalog_id) FROM stdin; +\. + + +-- +-- Data for Name: iceberg_tables; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.iceberg_tables (id, namespace_id, bucket_name, name, location, created_at, updated_at, remote_table_id, shard_key, shard_id, catalog_id) FROM stdin; +\. + + +-- +-- Data for Name: migrations; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.migrations (id, name, hash, executed_at) FROM stdin; +0 create-migrations-table e18db593bcde2aca2a408c4d1100f6abba2195df 2026-03-23 10:13:30.796432 +1 initialmigration 6ab16121fbaa08bbd11b712d05f358f9b555d777 2026-03-23 10:13:30.806948 +2 storage-schema f6a1fa2c93cbcd16d4e487b362e45fca157a8dbd 2026-03-23 10:13:30.810381 +3 pathtoken-column 2cb1b0004b817b29d5b0a971af16bafeede4b70d 2026-03-23 10:13:30.829325 +4 add-migrations-rls 427c5b63fe1c5937495d9c635c263ee7a5905058 2026-03-23 10:13:30.844667 +5 add-size-functions 79e081a1455b63666c1294a440f8ad4b1e6a7f84 2026-03-23 10:13:30.849797 +6 change-column-name-in-get-size ded78e2f1b5d7e616117897e6443a925965b30d2 2026-03-23 10:13:30.853961 +7 add-rls-to-buckets e7e7f86adbc51049f341dfe8d30256c1abca17aa 2026-03-23 10:13:30.857966 +8 add-public-to-buckets fd670db39ed65f9d08b01db09d6202503ca2bab3 2026-03-23 10:13:30.86089 +9 fix-search-function af597a1b590c70519b464a4ab3be54490712796b 2026-03-23 10:13:30.865914 +10 search-files-search-function b595f05e92f7e91211af1bbfe9c6a13bb3391e16 2026-03-23 10:13:30.870596 +11 add-trigger-to-auto-update-updated_at-column 7425bdb14366d1739fa8a18c83100636d74dcaa2 2026-03-23 10:13:30.877495 +12 add-automatic-avif-detection-flag 8e92e1266eb29518b6a4c5313ab8f29dd0d08df9 2026-03-23 10:13:30.887175 +13 add-bucket-custom-limits cce962054138135cd9a8c4bcd531598684b25e7d 2026-03-23 10:13:30.894644 +14 use-bytes-for-max-size 941c41b346f9802b411f06f30e972ad4744dad27 2026-03-23 10:13:30.903415 +15 add-can-insert-object-function 934146bc38ead475f4ef4b555c524ee5d66799e5 2026-03-23 10:13:30.921243 +16 add-version 76debf38d3fd07dcfc747ca49096457d95b1221b 2026-03-23 10:13:30.925814 +17 drop-owner-foreign-key f1cbb288f1b7a4c1eb8c38504b80ae2a0153d101 2026-03-23 10:13:30.930594 +18 add_owner_id_column_deprecate_owner e7a511b379110b08e2f214be852c35414749fe66 2026-03-23 10:13:30.935872 +19 alter-default-value-objects-id 02e5e22a78626187e00d173dc45f58fa66a4f043 2026-03-23 10:13:30.945545 +20 list-objects-with-delimiter cd694ae708e51ba82bf012bba00caf4f3b6393b7 2026-03-23 10:13:30.949803 +21 s3-multipart-uploads 8c804d4a566c40cd1e4cc5b3725a664a9303657f 2026-03-23 10:13:30.956077 +22 s3-multipart-uploads-big-ints 9737dc258d2397953c9953d9b86920b8be0cdb73 2026-03-23 10:13:30.986283 +23 optimize-search-function 9d7e604cddc4b56a5422dc68c9313f4a1b6f132c 2026-03-23 10:13:30.995314 +24 operation-function 8312e37c2bf9e76bbe841aa5fda889206d2bf8aa 2026-03-23 10:13:31.001863 +25 custom-metadata d974c6057c3db1c1f847afa0e291e6165693b990 2026-03-23 10:13:31.005106 +26 objects-prefixes 215cabcb7f78121892a5a2037a09fedf9a1ae322 2026-03-23 10:13:31.010242 +27 search-v2 859ba38092ac96eb3964d83bf53ccc0b141663a6 2026-03-23 10:13:31.021533 +28 object-bucket-name-sorting c73a2b5b5d4041e39705814fd3a1b95502d38ce4 2026-03-23 10:13:31.025092 +29 create-prefixes ad2c1207f76703d11a9f9007f821620017a66c21 2026-03-23 10:13:31.028503 +30 update-object-levels 2be814ff05c8252fdfdc7cfb4b7f5c7e17f0bed6 2026-03-23 10:13:31.031493 +31 objects-level-index b40367c14c3440ec75f19bbce2d71e914ddd3da0 2026-03-23 10:13:31.033332 +32 backward-compatible-index-on-objects e0c37182b0f7aee3efd823298fb3c76f1042c0f7 2026-03-23 10:13:31.035585 +33 backward-compatible-index-on-prefixes b480e99ed951e0900f033ec4eb34b5bdcb4e3d49 2026-03-23 10:13:31.043153 +34 optimize-search-function-v1 ca80a3dc7bfef894df17108785ce29a7fc8ee456 2026-03-23 10:13:31.045135 +35 add-insert-trigger-prefixes 458fe0ffd07ec53f5e3ce9df51bfdf4861929ccc 2026-03-23 10:13:31.047011 +36 optimise-existing-functions 6ae5fca6af5c55abe95369cd4f93985d1814ca8f 2026-03-23 10:13:31.049187 +37 add-bucket-name-length-trigger 3944135b4e3e8b22d6d4cbb568fe3b0b51df15c1 2026-03-23 10:13:31.052127 +38 iceberg-catalog-flag-on-buckets 02716b81ceec9705aed84aa1501657095b32e5c5 2026-03-23 10:13:31.056547 +39 add-search-v2-sort-support 6706c5f2928846abee18461279799ad12b279b78 2026-03-23 10:13:31.071273 +40 fix-prefix-race-conditions-optimized 7ad69982ae2d372b21f48fc4829ae9752c518f6b 2026-03-23 10:13:31.073508 +41 add-object-level-update-trigger 07fcf1a22165849b7a029deed059ffcde08d1ae0 2026-03-23 10:13:31.075277 +42 rollback-prefix-triggers 771479077764adc09e2ea2043eb627503c034cd4 2026-03-23 10:13:31.07699 +43 fix-object-level 84b35d6caca9d937478ad8a797491f38b8c2979f 2026-03-23 10:13:31.079645 +44 vector-bucket-type 99c20c0ffd52bb1ff1f32fb992f3b351e3ef8fb3 2026-03-23 10:13:31.081463 +45 vector-buckets 049e27196d77a7cb76497a85afae669d8b230953 2026-03-23 10:13:31.084341 +46 buckets-objects-grants fedeb96d60fefd8e02ab3ded9fbde05632f84aed 2026-03-23 10:13:31.090554 +47 iceberg-table-metadata 649df56855c24d8b36dd4cc1aeb8251aa9ad42c2 2026-03-23 10:13:31.09725 +48 iceberg-catalog-ids e0e8b460c609b9999ccd0df9ad14294613eed939 2026-03-23 10:13:31.106653 +49 buckets-objects-grants-postgres 072b1195d0d5a2f888af6b2302a1938dd94b8b3d 2026-03-23 10:13:31.134859 +50 search-v2-optimised 6323ac4f850aa14e7387eb32102869578b5bd478 2026-03-23 10:13:31.141562 +51 index-backward-compatible-search 2ee395d433f76e38bcd3856debaf6e0e5b674011 2026-03-23 10:13:31.236273 +52 drop-not-used-indexes-and-functions bb0cbc7f2206a5a41113363dd22556cc1afd6327 2026-03-23 10:13:31.237617 +53 drop-index-lower-name d0cb18777d9e2a98ebe0bc5cc7a42e57ebe41854 2026-03-23 10:13:31.243999 +54 drop-index-object-level 6289e048b1472da17c31a7eba1ded625a6457e67 2026-03-23 10:13:31.24577 +55 prevent-direct-deletes 262a4798d5e0f2e7c8970232e03ce8be695d5819 2026-03-23 10:13:31.247132 +\. + + +-- +-- Data for Name: objects; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.objects (id, bucket_id, name, owner, created_at, updated_at, last_accessed_at, metadata, version, owner_id, user_metadata) FROM stdin; +075af3e6-3564-45a5-b23f-436703c6c26f avatars aaaaaaaa-0002-0002-0002-000000000002/avatar.png aaaaaaaa-0002-0002-0002-000000000002 2026-03-26 21:50:11.504419+00 2026-03-26 21:50:13.194121+00 2026-03-26 21:50:11.504419+00 {"eTag": "\\"7acd7fe015d57168c9d1f740e6ef9dca\\"", "size": 120039, "mimetype": "image/png", "cacheControl": "max-age=3600", "lastModified": "2026-03-26T21:50:13.181Z", "contentLength": 120039, "httpStatusCode": 200} c6a84a00-860d-44bb-9811-020973cebfb5 aaaaaaaa-0002-0002-0002-000000000002 {} +89cde848-64e4-4977-b80d-451a5d6821f3 logos bbbbbbbb-0002-0002-0002-000000000002/logo.webp aaaaaaaa-0002-0002-0002-000000000002 2026-03-26 21:50:03.179856+00 2026-03-26 22:02:03.167238+00 2026-03-26 21:50:03.179856+00 {"eTag": "\\"af758fda0f2989c0fe7e39c321769aae\\"", "size": 62662, "mimetype": "image/webp", "cacheControl": "max-age=3600", "lastModified": "2026-03-26T22:02:03.153Z", "contentLength": 62662, "httpStatusCode": 200} 3f73d9fd-b955-48f6-9000-9c9b496368a0 aaaaaaaa-0002-0002-0002-000000000002 {} +15bc29b2-3f9d-4e12-a8ca-57d86a86af98 avatars aaaaaaaa-0002-0002-0002-000000000002/avatar.jpg aaaaaaaa-0002-0002-0002-000000000002 2026-03-26 22:33:38.030054+00 2026-03-26 22:38:39.744595+00 2026-03-26 22:33:38.030054+00 {"eTag": "\\"ef32cf7cfbb4f5f02cf6232c16eab2d5\\"", "size": 81994, "mimetype": "image/jpeg", "cacheControl": "max-age=3600", "lastModified": "2026-03-26T22:38:39.735Z", "contentLength": 81994, "httpStatusCode": 200} 3863856f-cab6-426e-b583-c7a5eefd17b9 aaaaaaaa-0002-0002-0002-000000000002 {} +d6b93d8f-349d-4528-aa08-1ec8002a081c logos bbbbbbbb-0002-0002-0002-000000000002/logo.jpg aaaaaaaa-0002-0002-0002-000000000002 2026-03-26 22:12:00.379274+00 2026-03-26 22:12:16.15555+00 2026-03-26 22:12:00.379274+00 {"eTag": "\\"2b730f9f0e7047d098f5143fe2f4cccf\\"", "size": 198972, "mimetype": "image/jpeg", "cacheControl": "max-age=3600", "lastModified": "2026-03-26T22:12:16.145Z", "contentLength": 198972, "httpStatusCode": 200} 1dfcf0cc-9416-4eac-9d76-daed5851d689 aaaaaaaa-0002-0002-0002-000000000002 {} +b4b03b3c-d69f-4243-aec1-f37518ad181f avatars aaaaaaaa-0002-0002-0002-000000000002/avatar.webp aaaaaaaa-0002-0002-0002-000000000002 2026-03-27 14:17:24.227826+00 2026-03-27 14:18:26.525375+00 2026-03-27 14:17:24.227826+00 {"eTag": "\\"7d090e1ccd302488cb8ac03ea09e5354\\"", "size": 94090, "mimetype": "image/webp", "cacheControl": "max-age=3600", "lastModified": "2026-03-27T14:18:26.500Z", "contentLength": 94090, "httpStatusCode": 200} 1bc2430d-1ab7-43eb-af6f-5f96fea36361 aaaaaaaa-0002-0002-0002-000000000002 {} +090b9a1e-79de-4b29-81e8-9a52a31f2740 avatars owners/aaaaaaaa-0002-0002-0002-000000000002/patients/fe670066-0d81-49ea-b177-61e83b455c59/avatar.png aaaaaaaa-0002-0002-0002-000000000002 2026-03-28 12:18:31.745186+00 2026-03-28 12:18:31.745186+00 2026-03-28 12:18:31.745186+00 {"eTag": "\\"8ff97e8238a368c6f59596d7a8b9e052\\"", "size": 48710, "mimetype": "image/png", "cacheControl": "max-age=3600", "lastModified": "2026-03-28T12:18:31.737Z", "contentLength": 48710, "httpStatusCode": 200} 5cf5597e-6fc6-4dab-90ee-5ac5d5e6382a aaaaaaaa-0002-0002-0002-000000000002 {} +7e19b5ee-127c-43a2-b14d-b1760780d18d logos bbbbbbbb-0002-0002-0002-000000000002/logo.png aaaaaaaa-0002-0002-0002-000000000002 2026-03-27 11:02:41.945678+00 2026-03-27 14:18:20.310831+00 2026-03-27 11:02:41.945678+00 {"eTag": "\\"60d363c60025fd47681d9b9d50939a8d\\"", "size": 158427, "mimetype": "image/png", "cacheControl": "max-age=3600", "lastModified": "2026-03-27T14:18:20.304Z", "contentLength": 158427, "httpStatusCode": 200} 9c580c92-3cc2-4237-b42c-4bd5e5a9a7f0 aaaaaaaa-0002-0002-0002-000000000002 {} +\. + + +-- +-- Data for Name: s3_multipart_uploads; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.s3_multipart_uploads (id, in_progress_size, upload_signature, bucket_id, key, version, owner_id, created_at, user_metadata) FROM stdin; +\. + + +-- +-- Data for Name: s3_multipart_uploads_parts; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.s3_multipart_uploads_parts (id, upload_id, size, part_number, bucket_id, key, etag, owner_id, version, created_at) FROM stdin; +\. + + +-- +-- Data for Name: vector_indexes; Type: TABLE DATA; Schema: storage; Owner: - +-- + +COPY storage.vector_indexes (id, name, bucket_id, data_type, dimension, distance_metric, metadata_configuration, created_at, updated_at) FROM stdin; +\. + + +-- +-- Data for Name: hooks; Type: TABLE DATA; Schema: supabase_functions; Owner: - +-- + +COPY supabase_functions.hooks (id, hook_table_id, hook_name, created_at, request_id) FROM stdin; +\. + + +-- +-- Data for Name: migrations; Type: TABLE DATA; Schema: supabase_functions; Owner: - +-- + +COPY supabase_functions.migrations (version, inserted_at) FROM stdin; +initial 2026-03-23 10:13:12.298935+00 +20210809183423_update_grants 2026-03-23 10:13:12.298935+00 +\. + + +-- +-- Data for Name: secrets; Type: TABLE DATA; Schema: vault; Owner: - +-- + +COPY vault.secrets (id, name, description, secret, key_id, nonce, created_at, updated_at) FROM stdin; +\. + + +-- +-- Name: refresh_tokens_id_seq; Type: SEQUENCE SET; Schema: auth; Owner: - +-- + +SELECT pg_catalog.setval('auth.refresh_tokens_id_seq', 155, true); + + +-- +-- Name: jobid_seq; Type: SEQUENCE SET; Schema: cron; Owner: - +-- + +SELECT pg_catalog.setval('cron.jobid_seq', 1, false); + + +-- +-- Name: runid_seq; Type: SEQUENCE SET; Schema: cron; Owner: - +-- + +SELECT pg_catalog.setval('cron.runid_seq', 1, false); + + +-- +-- Name: _db_migrations_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public._db_migrations_id_seq', 15, true); + + +-- +-- Name: agenda_online_slots_id_seq; Type: SEQUENCE SET; Schema: public; Owner: - +-- + +SELECT pg_catalog.setval('public.agenda_online_slots_id_seq', 1, false); + + +-- +-- Name: subscription_id_seq; Type: SEQUENCE SET; Schema: realtime; Owner: - +-- + +SELECT pg_catalog.setval('realtime.subscription_id_seq', 1029, true); + + +-- +-- Name: hooks_id_seq; Type: SEQUENCE SET; Schema: supabase_functions; Owner: - +-- + +SELECT pg_catalog.setval('supabase_functions.hooks_id_seq', 1, false); + + +-- +-- Name: extensions extensions_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.extensions + ADD CONSTRAINT extensions_pkey PRIMARY KEY (id); + + +-- +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.schema_migrations + ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.tenants + ADD CONSTRAINT tenants_pkey PRIMARY KEY (id); + + +-- +-- Name: mfa_amr_claims amr_id_pk; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_amr_claims + ADD CONSTRAINT amr_id_pk PRIMARY KEY (id); + + +-- +-- Name: audit_log_entries audit_log_entries_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.audit_log_entries + ADD CONSTRAINT audit_log_entries_pkey PRIMARY KEY (id); + + +-- +-- Name: flow_state flow_state_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.flow_state + ADD CONSTRAINT flow_state_pkey PRIMARY KEY (id); + + +-- +-- Name: identities identities_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.identities + ADD CONSTRAINT identities_pkey PRIMARY KEY (id); + + +-- +-- Name: identities identities_provider_id_provider_unique; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.identities + ADD CONSTRAINT identities_provider_id_provider_unique UNIQUE (provider_id, provider); + + +-- +-- Name: instances instances_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.instances + ADD CONSTRAINT instances_pkey PRIMARY KEY (id); + + +-- +-- Name: mfa_amr_claims mfa_amr_claims_session_id_authentication_method_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_amr_claims + ADD CONSTRAINT mfa_amr_claims_session_id_authentication_method_pkey UNIQUE (session_id, authentication_method); + + +-- +-- Name: mfa_challenges mfa_challenges_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_challenges + ADD CONSTRAINT mfa_challenges_pkey PRIMARY KEY (id); + + +-- +-- Name: mfa_factors mfa_factors_last_challenged_at_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_factors + ADD CONSTRAINT mfa_factors_last_challenged_at_key UNIQUE (last_challenged_at); + + +-- +-- Name: mfa_factors mfa_factors_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_factors + ADD CONSTRAINT mfa_factors_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_authorizations oauth_authorizations_authorization_code_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_authorization_code_key UNIQUE (authorization_code); + + +-- +-- Name: oauth_authorizations oauth_authorizations_authorization_id_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_authorization_id_key UNIQUE (authorization_id); + + +-- +-- Name: oauth_authorizations oauth_authorizations_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_client_states oauth_client_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_client_states + ADD CONSTRAINT oauth_client_states_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_clients oauth_clients_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_clients + ADD CONSTRAINT oauth_clients_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_consents oauth_consents_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_consents oauth_consents_user_client_unique; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_user_client_unique UNIQUE (user_id, client_id); + + +-- +-- Name: one_time_tokens one_time_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.one_time_tokens + ADD CONSTRAINT one_time_tokens_pkey PRIMARY KEY (id); + + +-- +-- Name: refresh_tokens refresh_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens + ADD CONSTRAINT refresh_tokens_pkey PRIMARY KEY (id); + + +-- +-- Name: refresh_tokens refresh_tokens_token_unique; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens + ADD CONSTRAINT refresh_tokens_token_unique UNIQUE (token); + + +-- +-- Name: saml_providers saml_providers_entity_id_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_providers + ADD CONSTRAINT saml_providers_entity_id_key UNIQUE (entity_id); + + +-- +-- Name: saml_providers saml_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_providers + ADD CONSTRAINT saml_providers_pkey PRIMARY KEY (id); + + +-- +-- Name: saml_relay_states saml_relay_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_relay_states + ADD CONSTRAINT saml_relay_states_pkey PRIMARY KEY (id); + + +-- +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.schema_migrations + ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: sessions sessions_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sessions + ADD CONSTRAINT sessions_pkey PRIMARY KEY (id); + + +-- +-- Name: sso_domains sso_domains_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sso_domains + ADD CONSTRAINT sso_domains_pkey PRIMARY KEY (id); + + +-- +-- Name: sso_providers sso_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sso_providers + ADD CONSTRAINT sso_providers_pkey PRIMARY KEY (id); + + +-- +-- Name: users users_phone_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.users + ADD CONSTRAINT users_phone_key UNIQUE (phone); + + +-- +-- Name: users users_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.users + ADD CONSTRAINT users_pkey PRIMARY KEY (id); + + +-- +-- Name: _db_migrations _db_migrations_filename_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._db_migrations + ADD CONSTRAINT _db_migrations_filename_key UNIQUE (filename); + + +-- +-- Name: _db_migrations _db_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._db_migrations + ADD CONSTRAINT _db_migrations_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_credits addon_credits_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT addon_credits_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_products addon_products_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_products + ADD CONSTRAINT addon_products_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_products addon_products_slug_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_products + ADD CONSTRAINT addon_products_slug_key UNIQUE (slug); + + +-- +-- Name: addon_transactions addon_transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_bloqueios + ADD CONSTRAINT agenda_bloqueios_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_configuracoes + ADD CONSTRAINT agenda_configuracoes_pkey PRIMARY KEY (owner_id); + + +-- +-- Name: agenda_eventos agenda_eventos_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_eventos agenda_eventos_sem_sobreposicao; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_sem_sobreposicao EXCLUDE USING gist (owner_id WITH =, tstzrange(inicio_em, fim_em, '[)'::text) WITH &&); + + +-- +-- Name: agenda_excecoes agenda_excecoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_excecoes + ADD CONSTRAINT agenda_excecoes_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_online_slots agenda_online_slots_owner_id_weekday_time_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_owner_id_weekday_time_key UNIQUE (owner_id, weekday, "time"); + + +-- +-- Name: agenda_online_slots agenda_online_slots_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_regras_semanais + ADD CONSTRAINT agenda_regras_semanais_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_regras_semanais + ADD CONSTRAINT agenda_regras_semanais_unique UNIQUE (owner_id, dia_semana, hora_inicio, hora_fim, modalidade); + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_seman_owner_id_dia_semana_hora_inic_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais + ADD CONSTRAINT agenda_slots_bloqueados_seman_owner_id_dia_semana_hora_inic_key UNIQUE (owner_id, dia_semana, hora_inicio); + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais + ADD CONSTRAINT agenda_slots_bloqueados_semanais_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_owner_id_dia_semana_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_regras + ADD CONSTRAINT agenda_slots_regras_owner_id_dia_semana_key UNIQUE (owner_id, dia_semana); + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_regras + ADD CONSTRAINT agenda_slots_regras_pkey PRIMARY KEY (id); + + +-- +-- Name: agendador_configuracoes agendador_configuracoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_configuracoes + ADD CONSTRAINT agendador_configuracoes_pkey PRIMARY KEY (owner_id); + + +-- +-- Name: agendador_solicitacoes agendador_solicitacoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_solicitacoes + ADD CONSTRAINT agendador_solicitacoes_pkey PRIMARY KEY (id); + + +-- +-- Name: billing_contracts billing_contracts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.billing_contracts + ADD CONSTRAINT billing_contracts_pkey PRIMARY KEY (id); + + +-- +-- Name: commitment_services commitment_services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_services + ADD CONSTRAINT commitment_services_pkey PRIMARY KEY (id); + + +-- +-- Name: commitment_time_logs commitment_time_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_pkey PRIMARY KEY (id); + + +-- +-- Name: company_profiles company_profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.company_profiles + ADD CONSTRAINT company_profiles_pkey PRIMARY KEY (id); + + +-- +-- Name: company_profiles company_profiles_tenant_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.company_profiles + ADD CONSTRAINT company_profiles_tenant_id_key UNIQUE (tenant_id); + + +-- +-- Name: determined_commitment_fields determined_commitment_fields_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitment_fields + ADD CONSTRAINT determined_commitment_fields_pkey PRIMARY KEY (id); + + +-- +-- Name: determined_commitments determined_commitments_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitments + ADD CONSTRAINT determined_commitments_pkey PRIMARY KEY (id); + + +-- +-- Name: determined_commitments determined_commitments_tenant_native_key_uq; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitments + ADD CONSTRAINT determined_commitments_tenant_native_key_uq UNIQUE (tenant_id, native_key); + + +-- +-- Name: dev_user_credentials dev_user_credentials_email_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.dev_user_credentials + ADD CONSTRAINT dev_user_credentials_email_key UNIQUE (email); + + +-- +-- Name: dev_user_credentials dev_user_credentials_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.dev_user_credentials + ADD CONSTRAINT dev_user_credentials_pkey PRIMARY KEY (id); + + +-- +-- Name: email_layout_config email_layout_config_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_layout_config + ADD CONSTRAINT email_layout_config_pkey PRIMARY KEY (id); + + +-- +-- Name: email_layout_config email_layout_config_tenant_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_layout_config + ADD CONSTRAINT email_layout_config_tenant_id_key UNIQUE (tenant_id); + + +-- +-- Name: email_templates_global email_templates_global_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_global + ADD CONSTRAINT email_templates_global_key_key UNIQUE (key); + + +-- +-- Name: email_templates_global email_templates_global_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_global + ADD CONSTRAINT email_templates_global_pkey PRIMARY KEY (id); + + +-- +-- Name: email_templates_tenant email_templates_tenant_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_pkey PRIMARY KEY (id); + + +-- +-- Name: email_templates_tenant email_templates_tenant_tenant_id_owner_id_template_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_tenant_id_owner_id_template_key_key UNIQUE (tenant_id, owner_id, template_key); + + +-- +-- Name: entitlements_invalidation entitlements_invalidation_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entitlements_invalidation + ADD CONSTRAINT entitlements_invalidation_pkey PRIMARY KEY (owner_id); + + +-- +-- Name: features features_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.features + ADD CONSTRAINT features_key_key UNIQUE (key); + + +-- +-- Name: features features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.features + ADD CONSTRAINT features_pkey PRIMARY KEY (id); + + +-- +-- Name: feriados feriados_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_pkey PRIMARY KEY (id); + + +-- +-- Name: feriados feriados_tenant_id_data_nome_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_tenant_id_data_nome_key UNIQUE (tenant_id, data, nome); + + +-- +-- Name: financial_categories financial_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_categories + ADD CONSTRAINT financial_categories_pkey PRIMARY KEY (id); + + +-- +-- Name: financial_exceptions financial_exceptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_exceptions + ADD CONSTRAINT financial_exceptions_pkey PRIMARY KEY (id); + + +-- +-- Name: financial_records financial_records_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_pkey PRIMARY KEY (id); + + +-- +-- Name: global_notices global_notices_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.global_notices + ADD CONSTRAINT global_notices_pkey PRIMARY KEY (id); + + +-- +-- Name: insurance_plan_services insurance_plan_services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plan_services + ADD CONSTRAINT insurance_plan_services_pkey PRIMARY KEY (id); + + +-- +-- Name: insurance_plans insurance_plans_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plans + ADD CONSTRAINT insurance_plans_pkey PRIMARY KEY (id); + + +-- +-- Name: login_carousel_slides login_carousel_slides_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.login_carousel_slides + ADD CONSTRAINT login_carousel_slides_pkey PRIMARY KEY (id); + + +-- +-- Name: medicos medicos_crm_owner_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.medicos + ADD CONSTRAINT medicos_crm_owner_unique UNIQUE NULLS NOT DISTINCT (owner_id, crm); + + +-- +-- Name: medicos medicos_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.medicos + ADD CONSTRAINT medicos_pkey PRIMARY KEY (id); + + +-- +-- Name: module_features module_features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.module_features + ADD CONSTRAINT module_features_pkey PRIMARY KEY (module_id, feature_id); + + +-- +-- Name: modules modules_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.modules + ADD CONSTRAINT modules_key_key UNIQUE (key); + + +-- +-- Name: modules modules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.modules + ADD CONSTRAINT modules_pkey PRIMARY KEY (id); + + +-- +-- Name: notice_dismissals notice_dismissals_notice_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_notice_id_user_id_key UNIQUE (notice_id, user_id); + + +-- +-- Name: notice_dismissals notice_dismissals_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_channels notification_channels_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_channels + ADD CONSTRAINT notification_channels_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_logs notification_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_logs + ADD CONSTRAINT notification_logs_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_preferences notification_preferences_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_preferences + ADD CONSTRAINT notification_preferences_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_queue notification_queue_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_queue + ADD CONSTRAINT notification_queue_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_schedules notification_schedules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_schedules + ADD CONSTRAINT notification_schedules_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_templates notification_templates_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_templates + ADD CONSTRAINT notification_templates_pkey PRIMARY KEY (id); + + +-- +-- Name: notifications notifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT notifications_pkey PRIMARY KEY (id); + + +-- +-- Name: owner_users owner_users_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.owner_users + ADD CONSTRAINT owner_users_pkey PRIMARY KEY (owner_id, user_id); + + +-- +-- Name: patient_contacts patient_contacts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_contacts + ADD CONSTRAINT patient_contacts_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_discounts patient_discounts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_discounts + ADD CONSTRAINT patient_discounts_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_group_patient patient_group_patient_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_group_patient + ADD CONSTRAINT patient_group_patient_pkey PRIMARY KEY (patient_group_id, patient_id); + + +-- +-- Name: patient_groups patient_groups_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_groups + ADD CONSTRAINT patient_groups_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_intake_requests patient_intake_requests_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_intake_requests + ADD CONSTRAINT patient_intake_requests_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_invites patient_invites_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_invites + ADD CONSTRAINT patient_invites_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_invites patient_invites_token_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_invites + ADD CONSTRAINT patient_invites_token_key UNIQUE (token); + + +-- +-- Name: patient_patient_tag patient_patient_tag_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT patient_patient_tag_pkey PRIMARY KEY (patient_id, tag_id); + + +-- +-- Name: patient_status_history patient_status_history_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_support_contacts patient_support_contacts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_support_contacts + ADD CONSTRAINT patient_support_contacts_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_tags patient_tags_owner_name_uniq; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_tags + ADD CONSTRAINT patient_tags_owner_name_uniq UNIQUE (owner_id, nome); + + +-- +-- Name: patient_tags patient_tags_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_tags + ADD CONSTRAINT patient_tags_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_timeline patient_timeline_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_pkey PRIMARY KEY (id); + + +-- +-- Name: patients patients_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_pkey PRIMARY KEY (id); + + +-- +-- Name: payment_settings payment_settings_owner_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_owner_id_key UNIQUE (owner_id); + + +-- +-- Name: payment_settings payment_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_pkey PRIMARY KEY (id); + + +-- +-- Name: plan_features plan_features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_features + ADD CONSTRAINT plan_features_pkey PRIMARY KEY (plan_id, feature_id); + + +-- +-- Name: plan_prices plan_prices_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_prices + ADD CONSTRAINT plan_prices_pkey PRIMARY KEY (id); + + +-- +-- Name: plan_public_bullets plan_public_bullets_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public_bullets + ADD CONSTRAINT plan_public_bullets_pkey PRIMARY KEY (id); + + +-- +-- Name: plan_public plan_public_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public + ADD CONSTRAINT plan_public_pkey PRIMARY KEY (plan_id); + + +-- +-- Name: plans plans_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plans + ADD CONSTRAINT plans_key_key UNIQUE (key); + + +-- +-- Name: plans plans_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plans + ADD CONSTRAINT plans_pkey PRIMARY KEY (id); + + +-- +-- Name: professional_pricing professional_pricing_owner_commitment_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.professional_pricing + ADD CONSTRAINT professional_pricing_owner_commitment_key UNIQUE (owner_id, determined_commitment_id); + + +-- +-- Name: professional_pricing professional_pricing_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.professional_pricing + ADD CONSTRAINT professional_pricing_pkey PRIMARY KEY (id); + + +-- +-- Name: profiles profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.profiles + ADD CONSTRAINT profiles_pkey PRIMARY KEY (id); + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_pkey PRIMARY KEY (id); + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_unique UNIQUE (recurrence_id, original_date); + + +-- +-- Name: recurrence_rule_services recurrence_rule_services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rule_services + ADD CONSTRAINT recurrence_rule_services_pkey PRIMARY KEY (id); + + +-- +-- Name: recurrence_rules recurrence_rules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rules + ADD CONSTRAINT recurrence_rules_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_admins saas_admins_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_admins + ADD CONSTRAINT saas_admins_pkey PRIMARY KEY (user_id); + + +-- +-- Name: saas_doc_votos saas_doc_votos_doc_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_doc_id_user_id_key UNIQUE (doc_id, user_id); + + +-- +-- Name: saas_doc_votos saas_doc_votos_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_docs saas_docs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_docs + ADD CONSTRAINT saas_docs_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_faq_itens saas_faq_itens_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_faq_itens + ADD CONSTRAINT saas_faq_itens_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_faq saas_faq_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_faq + ADD CONSTRAINT saas_faq_pkey PRIMARY KEY (id); + + +-- +-- Name: services services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.services + ADD CONSTRAINT services_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_events subscription_events_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_events + ADD CONSTRAINT subscription_events_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_intents_personal subscription_intents_personal_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_personal + ADD CONSTRAINT subscription_intents_personal_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_intents_legacy subscription_intents_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_legacy + ADD CONSTRAINT subscription_intents_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_intents_tenant subscription_intents_tenant_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_tenant + ADD CONSTRAINT subscription_intents_tenant_pkey PRIMARY KEY (id); + + +-- +-- Name: subscriptions subscriptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscriptions + ADD CONSTRAINT subscriptions_pkey PRIMARY KEY (id); + + +-- +-- Name: support_sessions support_sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_pkey PRIMARY KEY (id); + + +-- +-- Name: support_sessions support_sessions_token_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_token_unique UNIQUE (token); + + +-- +-- Name: tenant_feature_exceptions_log tenant_feature_exceptions_log_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_feature_exceptions_log + ADD CONSTRAINT tenant_feature_exceptions_log_pkey PRIMARY KEY (id); + + +-- +-- Name: tenant_features tenant_features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_features + ADD CONSTRAINT tenant_features_pkey PRIMARY KEY (tenant_id, feature_key); + + +-- +-- Name: tenant_invites tenant_invites_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_pkey PRIMARY KEY (id); + + +-- +-- Name: tenant_members tenant_members_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_pkey PRIMARY KEY (id); + + +-- +-- Name: tenant_members tenant_members_tenant_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_tenant_id_user_id_key UNIQUE (tenant_id, user_id); + + +-- +-- Name: tenant_modules tenant_modules_owner_id_module_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_owner_id_module_id_key UNIQUE (owner_id, module_id); + + +-- +-- Name: tenant_modules tenant_modules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_pkey PRIMARY KEY (id); + + +-- +-- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenants + ADD CONSTRAINT tenants_pkey PRIMARY KEY (id); + + +-- +-- Name: therapist_payout_records therapist_payout_records_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payout_records + ADD CONSTRAINT therapist_payout_records_pkey PRIMARY KEY (payout_id, financial_record_id); + + +-- +-- Name: therapist_payouts therapist_payouts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payouts + ADD CONSTRAINT therapist_payouts_pkey PRIMARY KEY (id); + + +-- +-- Name: twilio_subaccount_usage twilio_subaccount_usage_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.twilio_subaccount_usage + ADD CONSTRAINT twilio_subaccount_usage_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_credits uq_addon_credits_tenant_type; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT uq_addon_credits_tenant_type UNIQUE (tenant_id, addon_type); + + +-- +-- Name: notification_channels uq_channel_per_owner; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_channels + ADD CONSTRAINT uq_channel_per_owner UNIQUE NULLS NOT DISTINCT (owner_id, channel, deleted_at); + + +-- +-- Name: notification_preferences uq_notif_prefs_patient_owner; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_preferences + ADD CONSTRAINT uq_notif_prefs_patient_owner UNIQUE NULLS NOT DISTINCT (owner_id, patient_id, deleted_at); + + +-- +-- Name: notification_queue uq_notif_queue_idempotency; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_queue + ADD CONSTRAINT uq_notif_queue_idempotency UNIQUE (idempotency_key); + + +-- +-- Name: notification_schedules uq_notif_schedule_owner; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_schedules + ADD CONSTRAINT uq_notif_schedule_owner UNIQUE NULLS NOT DISTINCT (owner_id, schedule_key, deleted_at); + + +-- +-- Name: notification_templates uq_notif_template_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_templates + ADD CONSTRAINT uq_notif_template_key UNIQUE NULLS NOT DISTINCT (tenant_id, owner_id, key, deleted_at); + + +-- +-- Name: user_settings user_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_settings + ADD CONSTRAINT user_settings_pkey PRIMARY KEY (user_id); + + +-- +-- Name: messages messages_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages + ADD CONSTRAINT messages_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_26 messages_2026_03_26_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_26 + ADD CONSTRAINT messages_2026_03_26_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_27 messages_2026_03_27_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_27 + ADD CONSTRAINT messages_2026_03_27_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_28 messages_2026_03_28_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_28 + ADD CONSTRAINT messages_2026_03_28_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_29 messages_2026_03_29_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_29 + ADD CONSTRAINT messages_2026_03_29_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_30 messages_2026_03_30_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_30 + ADD CONSTRAINT messages_2026_03_30_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_31 messages_2026_03_31_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_31 + ADD CONSTRAINT messages_2026_03_31_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_04_01 messages_2026_04_01_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_04_01 + ADD CONSTRAINT messages_2026_04_01_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: subscription pk_subscription; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.subscription + ADD CONSTRAINT pk_subscription PRIMARY KEY (id); + + +-- +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.schema_migrations + ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: buckets_analytics buckets_analytics_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.buckets_analytics + ADD CONSTRAINT buckets_analytics_pkey PRIMARY KEY (id); + + +-- +-- Name: buckets buckets_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.buckets + ADD CONSTRAINT buckets_pkey PRIMARY KEY (id); + + +-- +-- Name: buckets_vectors buckets_vectors_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.buckets_vectors + ADD CONSTRAINT buckets_vectors_pkey PRIMARY KEY (id); + + +-- +-- Name: iceberg_namespaces iceberg_namespaces_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_namespaces + ADD CONSTRAINT iceberg_namespaces_pkey PRIMARY KEY (id); + + +-- +-- Name: iceberg_tables iceberg_tables_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_tables + ADD CONSTRAINT iceberg_tables_pkey PRIMARY KEY (id); + + +-- +-- Name: migrations migrations_name_key; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.migrations + ADD CONSTRAINT migrations_name_key UNIQUE (name); + + +-- +-- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.migrations + ADD CONSTRAINT migrations_pkey PRIMARY KEY (id); + + +-- +-- Name: objects objects_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.objects + ADD CONSTRAINT objects_pkey PRIMARY KEY (id); + + +-- +-- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads_parts + ADD CONSTRAINT s3_multipart_uploads_parts_pkey PRIMARY KEY (id); + + +-- +-- Name: s3_multipart_uploads s3_multipart_uploads_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads + ADD CONSTRAINT s3_multipart_uploads_pkey PRIMARY KEY (id); + + +-- +-- Name: vector_indexes vector_indexes_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.vector_indexes + ADD CONSTRAINT vector_indexes_pkey PRIMARY KEY (id); + + +-- +-- Name: hooks hooks_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: - +-- + +ALTER TABLE ONLY supabase_functions.hooks + ADD CONSTRAINT hooks_pkey PRIMARY KEY (id); + + +-- +-- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: - +-- + +ALTER TABLE ONLY supabase_functions.migrations + ADD CONSTRAINT migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: extensions_tenant_external_id_index; Type: INDEX; Schema: _realtime; Owner: - +-- + +CREATE INDEX extensions_tenant_external_id_index ON _realtime.extensions USING btree (tenant_external_id); + + +-- +-- Name: extensions_tenant_external_id_type_index; Type: INDEX; Schema: _realtime; Owner: - +-- + +CREATE UNIQUE INDEX extensions_tenant_external_id_type_index ON _realtime.extensions USING btree (tenant_external_id, type); + + +-- +-- Name: tenants_external_id_index; Type: INDEX; Schema: _realtime; Owner: - +-- + +CREATE UNIQUE INDEX tenants_external_id_index ON _realtime.tenants USING btree (external_id); + + +-- +-- Name: audit_logs_instance_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX audit_logs_instance_id_idx ON auth.audit_log_entries USING btree (instance_id); + + +-- +-- Name: confirmation_token_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX confirmation_token_idx ON auth.users USING btree (confirmation_token) WHERE ((confirmation_token)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: email_change_token_current_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX email_change_token_current_idx ON auth.users USING btree (email_change_token_current) WHERE ((email_change_token_current)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: email_change_token_new_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX email_change_token_new_idx ON auth.users USING btree (email_change_token_new) WHERE ((email_change_token_new)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: factor_id_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX factor_id_created_at_idx ON auth.mfa_factors USING btree (user_id, created_at); + + +-- +-- Name: flow_state_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX flow_state_created_at_idx ON auth.flow_state USING btree (created_at DESC); + + +-- +-- Name: identities_email_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX identities_email_idx ON auth.identities USING btree (email text_pattern_ops); + + +-- +-- Name: INDEX identities_email_idx; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON INDEX auth.identities_email_idx IS 'Auth: Ensures indexed queries on the email column'; + + +-- +-- Name: identities_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX identities_user_id_idx ON auth.identities USING btree (user_id); + + +-- +-- Name: idx_auth_code; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX idx_auth_code ON auth.flow_state USING btree (auth_code); + + +-- +-- Name: idx_oauth_client_states_created_at; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX idx_oauth_client_states_created_at ON auth.oauth_client_states USING btree (created_at); + + +-- +-- Name: idx_user_id_auth_method; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX idx_user_id_auth_method ON auth.flow_state USING btree (user_id, authentication_method); + + +-- +-- Name: mfa_challenge_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX mfa_challenge_created_at_idx ON auth.mfa_challenges USING btree (created_at DESC); + + +-- +-- Name: mfa_factors_user_friendly_name_unique; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX mfa_factors_user_friendly_name_unique ON auth.mfa_factors USING btree (friendly_name, user_id) WHERE (TRIM(BOTH FROM friendly_name) <> ''::text); + + +-- +-- Name: mfa_factors_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX mfa_factors_user_id_idx ON auth.mfa_factors USING btree (user_id); + + +-- +-- Name: oauth_auth_pending_exp_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_auth_pending_exp_idx ON auth.oauth_authorizations USING btree (expires_at) WHERE (status = 'pending'::auth.oauth_authorization_status); + + +-- +-- Name: oauth_clients_deleted_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_clients_deleted_at_idx ON auth.oauth_clients USING btree (deleted_at); + + +-- +-- Name: oauth_consents_active_client_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_consents_active_client_idx ON auth.oauth_consents USING btree (client_id) WHERE (revoked_at IS NULL); + + +-- +-- Name: oauth_consents_active_user_client_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_consents_active_user_client_idx ON auth.oauth_consents USING btree (user_id, client_id) WHERE (revoked_at IS NULL); + + +-- +-- Name: oauth_consents_user_order_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_consents_user_order_idx ON auth.oauth_consents USING btree (user_id, granted_at DESC); + + +-- +-- Name: one_time_tokens_relates_to_hash_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX one_time_tokens_relates_to_hash_idx ON auth.one_time_tokens USING hash (relates_to); + + +-- +-- Name: one_time_tokens_token_hash_hash_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX one_time_tokens_token_hash_hash_idx ON auth.one_time_tokens USING hash (token_hash); + + +-- +-- Name: one_time_tokens_user_id_token_type_key; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX one_time_tokens_user_id_token_type_key ON auth.one_time_tokens USING btree (user_id, token_type); + + +-- +-- Name: reauthentication_token_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX reauthentication_token_idx ON auth.users USING btree (reauthentication_token) WHERE ((reauthentication_token)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: recovery_token_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX recovery_token_idx ON auth.users USING btree (recovery_token) WHERE ((recovery_token)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: refresh_tokens_instance_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_instance_id_idx ON auth.refresh_tokens USING btree (instance_id); + + +-- +-- Name: refresh_tokens_instance_id_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_instance_id_user_id_idx ON auth.refresh_tokens USING btree (instance_id, user_id); + + +-- +-- Name: refresh_tokens_parent_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_parent_idx ON auth.refresh_tokens USING btree (parent); + + +-- +-- Name: refresh_tokens_session_id_revoked_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_session_id_revoked_idx ON auth.refresh_tokens USING btree (session_id, revoked); + + +-- +-- Name: refresh_tokens_updated_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_updated_at_idx ON auth.refresh_tokens USING btree (updated_at DESC); + + +-- +-- Name: saml_providers_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_providers_sso_provider_id_idx ON auth.saml_providers USING btree (sso_provider_id); + + +-- +-- Name: saml_relay_states_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_relay_states_created_at_idx ON auth.saml_relay_states USING btree (created_at DESC); + + +-- +-- Name: saml_relay_states_for_email_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_relay_states_for_email_idx ON auth.saml_relay_states USING btree (for_email); + + +-- +-- Name: saml_relay_states_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_relay_states_sso_provider_id_idx ON auth.saml_relay_states USING btree (sso_provider_id); + + +-- +-- Name: sessions_not_after_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sessions_not_after_idx ON auth.sessions USING btree (not_after DESC); + + +-- +-- Name: sessions_oauth_client_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sessions_oauth_client_id_idx ON auth.sessions USING btree (oauth_client_id); + + +-- +-- Name: sessions_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sessions_user_id_idx ON auth.sessions USING btree (user_id); + + +-- +-- Name: sso_domains_domain_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX sso_domains_domain_idx ON auth.sso_domains USING btree (lower(domain)); + + +-- +-- Name: sso_domains_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sso_domains_sso_provider_id_idx ON auth.sso_domains USING btree (sso_provider_id); + + +-- +-- Name: sso_providers_resource_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX sso_providers_resource_id_idx ON auth.sso_providers USING btree (lower(resource_id)); + + +-- +-- Name: sso_providers_resource_id_pattern_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sso_providers_resource_id_pattern_idx ON auth.sso_providers USING btree (resource_id text_pattern_ops); + + +-- +-- Name: unique_phone_factor_per_user; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX unique_phone_factor_per_user ON auth.mfa_factors USING btree (user_id, phone); + + +-- +-- Name: user_id_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX user_id_created_at_idx ON auth.sessions USING btree (user_id, created_at); + + +-- +-- Name: users_email_partial_key; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX users_email_partial_key ON auth.users USING btree (email) WHERE (is_sso_user = false); + + +-- +-- Name: INDEX users_email_partial_key; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON INDEX auth.users_email_partial_key IS 'Auth: A partial unique index that applies only when is_sso_user is false'; + + +-- +-- Name: users_instance_id_email_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX users_instance_id_email_idx ON auth.users USING btree (instance_id, lower((email)::text)); + + +-- +-- Name: users_instance_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX users_instance_id_idx ON auth.users USING btree (instance_id); + + +-- +-- Name: users_is_anonymous_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX users_is_anonymous_idx ON auth.users USING btree (is_anonymous); + + +-- +-- Name: agenda_bloqueios_owner_data_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_owner_data_idx ON public.agenda_bloqueios USING btree (owner_id, data_inicio, data_fim); + + +-- +-- Name: agenda_bloqueios_owner_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_owner_id_idx ON public.agenda_bloqueios USING btree (owner_id); + + +-- +-- Name: agenda_bloqueios_recorrente_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_recorrente_idx ON public.agenda_bloqueios USING btree (owner_id, dia_semana) WHERE (recorrente = true); + + +-- +-- Name: agenda_bloqueios_tenant_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_tenant_id_idx ON public.agenda_bloqueios USING btree (tenant_id); + + +-- +-- Name: agenda_configuracoes_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_configuracoes_tenant_idx ON public.agenda_configuracoes USING btree (tenant_id); + + +-- +-- Name: agenda_configuracoes_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_configuracoes_tenant_owner_idx ON public.agenda_configuracoes USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_eventos_billing_contract_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_billing_contract_idx ON public.agenda_eventos USING btree (billing_contract_id) WHERE (billing_contract_id IS NOT NULL); + + +-- +-- Name: agenda_eventos_insurance_plan_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_insurance_plan_idx ON public.agenda_eventos USING btree (insurance_plan_id); + + +-- +-- Name: agenda_eventos_owner_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_owner_inicio_idx ON public.agenda_eventos USING btree (owner_id, inicio_em); + + +-- +-- Name: agenda_eventos_owner_terapeuta_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_owner_terapeuta_inicio_idx ON public.agenda_eventos USING btree (owner_id, terapeuta_id, inicio_em); + + +-- +-- Name: agenda_eventos_recurrence_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_recurrence_idx ON public.agenda_eventos USING btree (recurrence_id) WHERE (recurrence_id IS NOT NULL); + + +-- +-- Name: agenda_eventos_tenant_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_tenant_inicio_idx ON public.agenda_eventos USING btree (tenant_id, inicio_em); + + +-- +-- Name: agenda_eventos_tenant_owner_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_tenant_owner_inicio_idx ON public.agenda_eventos USING btree (tenant_id, owner_id, inicio_em); + + +-- +-- Name: agenda_excecoes_owner_data_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_excecoes_owner_data_idx ON public.agenda_excecoes USING btree (owner_id, data); + + +-- +-- Name: agenda_excecoes_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_excecoes_tenant_idx ON public.agenda_excecoes USING btree (tenant_id); + + +-- +-- Name: agenda_excecoes_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_excecoes_tenant_owner_idx ON public.agenda_excecoes USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_online_slots_owner_weekday_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_online_slots_owner_weekday_idx ON public.agenda_online_slots USING btree (owner_id, weekday); + + +-- +-- Name: agenda_online_slots_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_online_slots_tenant_idx ON public.agenda_online_slots USING btree (tenant_id); + + +-- +-- Name: agenda_online_slots_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_online_slots_tenant_owner_idx ON public.agenda_online_slots USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_regras_semanais_owner_dia_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_regras_semanais_owner_dia_idx ON public.agenda_regras_semanais USING btree (owner_id, dia_semana); + + +-- +-- Name: agenda_regras_semanais_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_regras_semanais_tenant_idx ON public.agenda_regras_semanais USING btree (tenant_id); + + +-- +-- Name: agenda_regras_semanais_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_regras_semanais_tenant_owner_idx ON public.agenda_regras_semanais USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_slots_bloqueados_semanais_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_bloqueados_semanais_tenant_idx ON public.agenda_slots_bloqueados_semanais USING btree (tenant_id); + + +-- +-- Name: agenda_slots_bloqueados_semanais_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_bloqueados_semanais_tenant_owner_idx ON public.agenda_slots_bloqueados_semanais USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_slots_regras_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_regras_tenant_idx ON public.agenda_slots_regras USING btree (tenant_id); + + +-- +-- Name: agenda_slots_regras_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_regras_tenant_owner_idx ON public.agenda_slots_regras USING btree (tenant_id, owner_id); + + +-- +-- Name: agendador_cfg_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_cfg_tenant_idx ON public.agendador_configuracoes USING btree (tenant_id); + + +-- +-- Name: agendador_sol_data_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_sol_data_idx ON public.agendador_solicitacoes USING btree (data_solicitada, hora_solicitada); + + +-- +-- Name: agendador_sol_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_sol_owner_idx ON public.agendador_solicitacoes USING btree (owner_id, status); + + +-- +-- Name: agendador_sol_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_sol_tenant_idx ON public.agendador_solicitacoes USING btree (tenant_id); + + +-- +-- Name: billing_contracts_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX billing_contracts_owner_idx ON public.billing_contracts USING btree (owner_id); + + +-- +-- Name: billing_contracts_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX billing_contracts_patient_idx ON public.billing_contracts USING btree (patient_id); + + +-- +-- Name: billing_contracts_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX billing_contracts_tenant_idx ON public.billing_contracts USING btree (tenant_id); + + +-- +-- Name: commitment_services_commitment_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_services_commitment_idx ON public.commitment_services USING btree (commitment_id); + + +-- +-- Name: commitment_services_service_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_services_service_idx ON public.commitment_services USING btree (service_id); + + +-- +-- Name: commitment_time_logs_calendar_event_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_time_logs_calendar_event_idx ON public.commitment_time_logs USING btree (calendar_event_id); + + +-- +-- Name: commitment_time_logs_commitment_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_time_logs_commitment_idx ON public.commitment_time_logs USING btree (commitment_id, created_at DESC); + + +-- +-- Name: commitment_time_logs_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_time_logs_tenant_idx ON public.commitment_time_logs USING btree (tenant_id, created_at DESC); + + +-- +-- Name: determined_commitment_fields_commitment_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitment_fields_commitment_idx ON public.determined_commitment_fields USING btree (commitment_id, sort_order); + + +-- +-- Name: determined_commitment_fields_key_uniq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX determined_commitment_fields_key_uniq ON public.determined_commitment_fields USING btree (commitment_id, key); + + +-- +-- Name: determined_commitment_fields_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitment_fields_tenant_idx ON public.determined_commitment_fields USING btree (tenant_id); + + +-- +-- Name: determined_commitments_active_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitments_active_idx ON public.determined_commitments USING btree (tenant_id, active); + + +-- +-- Name: determined_commitments_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitments_tenant_idx ON public.determined_commitments USING btree (tenant_id); + + +-- +-- Name: determined_commitments_tenant_name_uniq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX determined_commitments_tenant_name_uniq ON public.determined_commitments USING btree (tenant_id, lower(name)); + + +-- +-- Name: feriados_global_unique; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX feriados_global_unique ON public.feriados USING btree (data, nome) WHERE (tenant_id IS NULL); + + +-- +-- Name: financial_exceptions_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX financial_exceptions_owner_idx ON public.financial_exceptions USING btree (owner_id); + + +-- +-- Name: financial_exceptions_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX financial_exceptions_tenant_idx ON public.financial_exceptions USING btree (tenant_id); + + +-- +-- Name: idx_addon_credits_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_credits_tenant ON public.addon_credits USING btree (tenant_id) WHERE (is_active = true); + + +-- +-- Name: idx_addon_credits_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_credits_type ON public.addon_credits USING btree (addon_type) WHERE (is_active = true); + + +-- +-- Name: idx_addon_products_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_products_active ON public.addon_products USING btree (is_active, is_visible) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_addon_products_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_products_type ON public.addon_products USING btree (addon_type) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_addon_tx_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_created ON public.addon_transactions USING btree (created_at DESC); + + +-- +-- Name: idx_addon_tx_queue; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_queue ON public.addon_transactions USING btree (queue_id) WHERE (queue_id IS NOT NULL); + + +-- +-- Name: idx_addon_tx_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_tenant ON public.addon_transactions USING btree (tenant_id, addon_type); + + +-- +-- Name: idx_addon_tx_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_type ON public.addon_transactions USING btree (type); + + +-- +-- Name: idx_agenda_eventos_determined_commitment_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_agenda_eventos_determined_commitment_id ON public.agenda_eventos USING btree (determined_commitment_id); + + +-- +-- Name: idx_agenda_excecoes_owner_data; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_agenda_excecoes_owner_data ON public.agenda_excecoes USING btree (owner_id, data); + + +-- +-- Name: idx_agenda_slots_regras_owner_dia; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_agenda_slots_regras_owner_dia ON public.agenda_slots_regras USING btree (owner_id, dia_semana); + + +-- +-- Name: idx_email_templates_global_domain; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_global_domain ON public.email_templates_global USING btree (domain) WHERE (is_active = true); + + +-- +-- Name: idx_email_templates_global_key; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_global_key ON public.email_templates_global USING btree (key) WHERE (is_active = true); + + +-- +-- Name: idx_email_templates_tenant_lookup; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_tenant_lookup ON public.email_templates_tenant USING btree (tenant_id, template_key) WHERE (enabled = true); + + +-- +-- Name: idx_email_templates_tenant_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_tenant_owner ON public.email_templates_tenant USING btree (owner_id, template_key) WHERE ((enabled = true) AND (owner_id IS NOT NULL)); + + +-- +-- Name: idx_financial_categories_user_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_categories_user_id ON public.financial_categories USING btree (user_id); + + +-- +-- Name: idx_financial_records_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_active ON public.financial_records USING btree (owner_id, paid_at DESC) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_financial_records_agenda_evento_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_agenda_evento_id ON public.financial_records USING btree (agenda_evento_id) WHERE (agenda_evento_id IS NOT NULL); + + +-- +-- Name: idx_financial_records_category_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_category_id ON public.financial_records USING btree (category_id) WHERE (category_id IS NOT NULL); + + +-- +-- Name: idx_financial_records_due_date; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_due_date ON public.financial_records USING btree (due_date); + + +-- +-- Name: idx_financial_records_installment_group; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_installment_group ON public.financial_records USING btree (installment_group) WHERE (installment_group IS NOT NULL); + + +-- +-- Name: idx_financial_records_owner_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_owner_id ON public.financial_records USING btree (owner_id); + + +-- +-- Name: idx_financial_records_paid_at; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_paid_at ON public.financial_records USING btree (paid_at DESC); + + +-- +-- Name: idx_financial_records_patient_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_patient_id ON public.financial_records USING btree (patient_id); + + +-- +-- Name: idx_financial_records_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_status ON public.financial_records USING btree (status) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_financial_records_tenant_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_tenant_active ON public.financial_records USING btree (tenant_id, paid_at DESC) WHERE ((deleted_at IS NULL) AND (tenant_id IS NOT NULL)); + + +-- +-- Name: idx_financial_records_tenant_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_tenant_id ON public.financial_records USING btree (tenant_id); + + +-- +-- Name: idx_financial_records_type_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_type_status ON public.financial_records USING btree (type, status); + + +-- +-- Name: idx_global_notices_active_priority; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_global_notices_active_priority ON public.global_notices USING btree (is_active, priority DESC, starts_at, ends_at); + + +-- +-- Name: idx_intakes_converted_patient_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_converted_patient_id ON public.patient_intake_requests USING btree (converted_patient_id); + + +-- +-- Name: idx_intakes_owner_cpf; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_owner_cpf ON public.patient_intake_requests USING btree (owner_id, cpf); + + +-- +-- Name: idx_intakes_owner_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_owner_created ON public.patient_intake_requests USING btree (owner_id, created_at DESC); + + +-- +-- Name: idx_intakes_owner_status_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_owner_status_created ON public.patient_intake_requests USING btree (owner_id, status, created_at DESC); + + +-- +-- Name: idx_intakes_status_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_status_created ON public.patient_intake_requests USING btree (status, created_at DESC); + + +-- +-- Name: idx_notice_dismissals_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notice_dismissals_user ON public.notice_dismissals USING btree (user_id, notice_id); + + +-- +-- Name: idx_notif_channels_owner_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_channels_owner_active ON public.notification_channels USING btree (owner_id, channel) WHERE ((is_active = true) AND (deleted_at IS NULL)); + + +-- +-- Name: idx_notif_channels_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_channels_tenant ON public.notification_channels USING btree (tenant_id) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_logs_owner_date; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_owner_date ON public.notification_logs USING btree (owner_id, created_at DESC); + + +-- +-- Name: idx_notif_logs_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_patient ON public.notification_logs USING btree (patient_id, created_at DESC); + + +-- +-- Name: idx_notif_logs_provider_msg; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_provider_msg ON public.notification_logs USING btree (provider_message_id) WHERE (provider_message_id IS NOT NULL); + + +-- +-- Name: idx_notif_logs_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_status ON public.notification_logs USING btree (status, created_at DESC); + + +-- +-- Name: idx_notif_logs_tenant_date; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_tenant_date ON public.notification_logs USING btree (tenant_id, created_at DESC); + + +-- +-- Name: idx_notif_prefs_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_prefs_owner ON public.notification_preferences USING btree (owner_id) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_prefs_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_prefs_patient ON public.notification_preferences USING btree (patient_id) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_prefs_whatsapp_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_prefs_whatsapp_active ON public.notification_preferences USING btree (owner_id, patient_id) WHERE ((whatsapp_opt_in = true) AND (deleted_at IS NULL)); + + +-- +-- Name: idx_notif_queue_evento; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_evento ON public.notification_queue USING btree (agenda_evento_id) WHERE (status = ANY (ARRAY['pendente'::text, 'processando'::text])); + + +-- +-- Name: idx_notif_queue_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_patient ON public.notification_queue USING btree (patient_id, channel) WHERE (status = 'pendente'::text); + + +-- +-- Name: idx_notif_queue_pending; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_pending ON public.notification_queue USING btree (scheduled_at) WHERE (status = 'pendente'::text); + + +-- +-- Name: idx_notif_queue_processing; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_processing ON public.notification_queue USING btree (status, updated_at) WHERE (status = 'processando'::text); + + +-- +-- Name: idx_notif_queue_retry; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_retry ON public.notification_queue USING btree (next_retry_at) WHERE ((status = 'pendente'::text) AND (attempts > 0)); + + +-- +-- Name: idx_notif_queue_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_tenant ON public.notification_queue USING btree (tenant_id, created_at DESC); + + +-- +-- Name: idx_notif_schedules_owner_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_schedules_owner_active ON public.notification_schedules USING btree (owner_id, event_type) WHERE ((is_active = true) AND (deleted_at IS NULL)); + + +-- +-- Name: idx_notif_templates_default; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_templates_default ON public.notification_templates USING btree (channel, event_type) WHERE ((is_default = true) AND (deleted_at IS NULL) AND (tenant_id IS NULL)); + + +-- +-- Name: idx_notif_templates_lookup; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_templates_lookup ON public.notification_templates USING btree (channel, event_type, is_active) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_templates_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_templates_tenant ON public.notification_templates USING btree (tenant_id, channel, event_type) WHERE ((deleted_at IS NULL) AND (is_active = true)); + + +-- +-- Name: idx_notification_channels_twilio_subaccount_sid; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notification_channels_twilio_subaccount_sid ON public.notification_channels USING btree (twilio_subaccount_sid) WHERE (twilio_subaccount_sid IS NOT NULL); + + +-- +-- Name: idx_patient_contacts_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_contacts_patient ON public.patient_contacts USING btree (patient_id); + + +-- +-- Name: idx_patient_contacts_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_contacts_tenant ON public.patient_contacts USING btree (tenant_id); + + +-- +-- Name: idx_patient_group_patient_group_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_group_patient_group_id ON public.patient_group_patient USING btree (patient_group_id); + + +-- +-- Name: idx_patient_groups_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_groups_owner ON public.patient_groups USING btree (owner_id); + + +-- +-- Name: idx_patient_groups_owner_system_nome; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_groups_owner_system_nome ON public.patient_groups USING btree (owner_id, is_system, nome); + + +-- +-- Name: idx_patient_tags_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_tags_owner ON public.patient_tags USING btree (owner_id); + + +-- +-- Name: idx_patients_created_at; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_created_at ON public.patients USING btree (created_at DESC); + + +-- +-- Name: idx_patients_last_attended; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_last_attended ON public.patients USING btree (last_attended_at DESC); + + +-- +-- Name: idx_patients_origem; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_origem ON public.patients USING btree (tenant_id, origem) WHERE (origem IS NOT NULL); + + +-- +-- Name: idx_patients_owner_email_principal; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_owner_email_principal ON public.patients USING btree (owner_id, email_principal); + + +-- +-- Name: idx_patients_owner_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_owner_id ON public.patients USING btree (owner_id); + + +-- +-- Name: idx_patients_owner_nome; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_owner_nome ON public.patients USING btree (owner_id, nome_completo); + + +-- +-- Name: idx_patients_responsible_member; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_responsible_member ON public.patients USING btree (responsible_member_id); + + +-- +-- Name: idx_patients_risco_elevado; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_risco_elevado ON public.patients USING btree (tenant_id, risco_elevado) WHERE (risco_elevado = true); + + +-- +-- Name: idx_patients_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_status ON public.patients USING btree (status); + + +-- +-- Name: idx_patients_status_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_status_tenant ON public.patients USING btree (tenant_id, status); + + +-- +-- Name: idx_patients_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_tenant ON public.patients USING btree (tenant_id); + + +-- +-- Name: idx_patients_tenant_email_norm; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_tenant_email_norm ON public.patients USING btree (tenant_id, lower(TRIM(BOTH FROM email_principal))); + + +-- +-- Name: idx_pgp_group; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pgp_group ON public.patient_group_patient USING btree (patient_group_id); + + +-- +-- Name: idx_pgp_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pgp_patient ON public.patient_group_patient USING btree (patient_id); + + +-- +-- Name: idx_ppt_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_ppt_patient ON public.patient_patient_tag USING btree (patient_id); + + +-- +-- Name: idx_ppt_tag; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_ppt_tag ON public.patient_patient_tag USING btree (tag_id); + + +-- +-- Name: idx_psh_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_psh_patient ON public.patient_status_history USING btree (patient_id, alterado_em DESC); + + +-- +-- Name: idx_psh_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_psh_tenant ON public.patient_status_history USING btree (tenant_id, alterado_em DESC); + + +-- +-- Name: idx_pt_evento_tipo; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pt_evento_tipo ON public.patient_timeline USING btree (patient_id, evento_tipo); + + +-- +-- Name: idx_pt_patient_ocorrido; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pt_patient_ocorrido ON public.patient_timeline USING btree (patient_id, ocorrido_em DESC); + + +-- +-- Name: idx_pt_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pt_tenant ON public.patient_timeline USING btree (tenant_id, ocorrido_em DESC); + + +-- +-- Name: idx_slots_bloq_owner_dia; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_slots_bloq_owner_dia ON public.agenda_slots_bloqueados_semanais USING btree (owner_id, dia_semana); + + +-- +-- Name: idx_subscription_intents_plan_interval; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_subscription_intents_plan_interval ON public.subscription_intents_legacy USING btree (plan_key, "interval"); + + +-- +-- Name: idx_subscription_intents_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_subscription_intents_status ON public.subscription_intents_legacy USING btree (status); + + +-- +-- Name: idx_subscription_intents_user_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_subscription_intents_user_id ON public.subscription_intents_legacy USING btree (user_id); + + +-- +-- Name: idx_tenant_features_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_tenant_features_tenant ON public.tenant_features USING btree (tenant_id); + + +-- +-- Name: idx_tenant_invites_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_tenant_invites_tenant ON public.tenant_invites USING btree (tenant_id); + + +-- +-- Name: idx_tenant_invites_token; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_tenant_invites_token ON public.tenant_invites USING btree (token); + + +-- +-- Name: idx_therapist_payout_records_financial_record_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payout_records_financial_record_id ON public.therapist_payout_records USING btree (financial_record_id); + + +-- +-- Name: idx_therapist_payouts_owner_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_owner_id ON public.therapist_payouts USING btree (owner_id); + + +-- +-- Name: idx_therapist_payouts_period; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_period ON public.therapist_payouts USING btree (period_start, period_end); + + +-- +-- Name: idx_therapist_payouts_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_status ON public.therapist_payouts USING btree (status) WHERE (status = 'pending'::text); + + +-- +-- Name: idx_therapist_payouts_tenant_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_tenant_id ON public.therapist_payouts USING btree (tenant_id); + + +-- +-- Name: idx_twilio_usage_channel; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_twilio_usage_channel ON public.twilio_subaccount_usage USING btree (channel_id, period_start DESC); + + +-- +-- Name: idx_twilio_usage_tenant_period; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_twilio_usage_tenant_period ON public.twilio_subaccount_usage USING btree (tenant_id, period_start DESC); + + +-- +-- Name: idx_twilio_usage_unique_period; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX idx_twilio_usage_unique_period ON public.twilio_subaccount_usage USING btree (channel_id, period_start, period_end); + + +-- +-- Name: insurance_plans_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX insurance_plans_owner_idx ON public.insurance_plans USING btree (owner_id); + + +-- +-- Name: insurance_plans_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX insurance_plans_tenant_idx ON public.insurance_plans USING btree (tenant_id); + + +-- +-- Name: ix_plan_prices_plan; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ix_plan_prices_plan ON public.plan_prices USING btree (plan_id); + + +-- +-- Name: ix_plan_public_bullets_plan; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ix_plan_public_bullets_plan ON public.plan_public_bullets USING btree (plan_id); + + +-- +-- Name: ix_plan_public_sort; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ix_plan_public_sort ON public.plan_public USING btree (sort_order); + + +-- +-- Name: medicos_especialidade_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX medicos_especialidade_idx ON public.medicos USING btree (especialidade); + + +-- +-- Name: medicos_nome_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX medicos_nome_idx ON public.medicos USING btree (nome); + + +-- +-- Name: medicos_nome_trgm_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX medicos_nome_trgm_idx ON public.medicos USING gin (nome public.gin_trgm_ops); + + +-- +-- Name: medicos_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX medicos_owner_idx ON public.medicos USING btree (owner_id); + + +-- +-- Name: medicos_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX medicos_tenant_idx ON public.medicos USING btree (tenant_id); + + +-- +-- Name: notifications_owner_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX notifications_owner_created ON public.notifications USING btree (owner_id, created_at DESC); + + +-- +-- Name: notifications_owner_unread; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX notifications_owner_unread ON public.notifications USING btree (owner_id, read_at) WHERE (read_at IS NULL); + + +-- +-- Name: patient_discounts_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_discounts_owner_idx ON public.patient_discounts USING btree (owner_id); + + +-- +-- Name: patient_discounts_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_discounts_patient_idx ON public.patient_discounts USING btree (patient_id); + + +-- +-- Name: patient_discounts_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_discounts_tenant_idx ON public.patient_discounts USING btree (tenant_id); + + +-- +-- Name: patient_group_patient_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_group_patient_tenant_idx ON public.patient_group_patient USING btree (tenant_id); + + +-- +-- Name: patient_groups_owner_nome_uniq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX patient_groups_owner_nome_uniq ON public.patient_groups USING btree (owner_id, nome); + + +-- +-- Name: patient_groups_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_groups_tenant_idx ON public.patient_groups USING btree (tenant_id); + + +-- +-- Name: patient_intake_owner_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_owner_id_idx ON public.patient_intake_requests USING btree (owner_id); + + +-- +-- Name: patient_intake_requests_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_requests_tenant_idx ON public.patient_intake_requests USING btree (tenant_id); + + +-- +-- Name: patient_intake_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_status_idx ON public.patient_intake_requests USING btree (status); + + +-- +-- Name: patient_intake_token_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_token_idx ON public.patient_intake_requests USING btree (token); + + +-- +-- Name: patient_invites_one_active_per_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX patient_invites_one_active_per_owner ON public.patient_invites USING btree (owner_id) WHERE (active = true); + + +-- +-- Name: patient_invites_owner_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_invites_owner_id_idx ON public.patient_invites USING btree (owner_id); + + +-- +-- Name: patient_invites_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_invites_tenant_idx ON public.patient_invites USING btree (tenant_id); + + +-- +-- Name: patient_invites_token_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_invites_token_idx ON public.patient_invites USING btree (token); + + +-- +-- Name: patient_patient_tag_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_patient_tag_tenant_idx ON public.patient_patient_tag USING btree (tenant_id); + + +-- +-- Name: patient_tags_owner_name_uq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX patient_tags_owner_name_uq ON public.patient_tags USING btree (owner_id, lower(nome)); + + +-- +-- Name: patient_tags_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_tags_tenant_idx ON public.patient_tags USING btree (tenant_id); + + +-- +-- Name: patients_convenio_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patients_convenio_id_idx ON public.patients USING btree (convenio_id); + + +-- +-- Name: patients_etnia_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patients_etnia_idx ON public.patients USING btree (etnia); + + +-- +-- Name: patients_pronomes_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patients_pronomes_idx ON public.patients USING btree (pronomes); + + +-- +-- Name: payment_settings_tenant_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX payment_settings_tenant_id_idx ON public.payment_settings USING btree (tenant_id); + + +-- +-- Name: ppt_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ppt_owner_idx ON public.patient_patient_tag USING btree (owner_id); + + +-- +-- Name: ppt_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ppt_patient_idx ON public.patient_patient_tag USING btree (patient_id); + + +-- +-- Name: ppt_tag_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ppt_tag_idx ON public.patient_patient_tag USING btree (tag_id); + + +-- +-- Name: professional_pricing_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX professional_pricing_tenant_idx ON public.professional_pricing USING btree (tenant_id); + + +-- +-- Name: profiles_work_description_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX profiles_work_description_idx ON public.profiles USING btree (work_description) WHERE (work_description IS NOT NULL); + + +-- +-- Name: psc_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX psc_owner_idx ON public.patient_support_contacts USING btree (owner_id); + + +-- +-- Name: psc_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX psc_patient_idx ON public.patient_support_contacts USING btree (patient_id); + + +-- +-- Name: recurrence_exceptions_rule_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_exceptions_rule_idx ON public.recurrence_exceptions USING btree (recurrence_id); + + +-- +-- Name: recurrence_exceptions_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_exceptions_tenant_idx ON public.recurrence_exceptions USING btree (tenant_id); + + +-- +-- Name: recurrence_rule_services_rule_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rule_services_rule_idx ON public.recurrence_rule_services USING btree (rule_id); + + +-- +-- Name: recurrence_rule_services_service_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rule_services_service_idx ON public.recurrence_rule_services USING btree (service_id); + + +-- +-- Name: recurrence_rules_active_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_active_idx ON public.recurrence_rules USING btree (owner_id, status) WHERE (status = 'ativo'::text); + + +-- +-- Name: recurrence_rules_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_owner_idx ON public.recurrence_rules USING btree (owner_id); + + +-- +-- Name: recurrence_rules_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_patient_idx ON public.recurrence_rules USING btree (patient_id); + + +-- +-- Name: recurrence_rules_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_tenant_idx ON public.recurrence_rules USING btree (tenant_id); + + +-- +-- Name: saas_doc_votos_doc_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_doc_votos_doc_id_idx ON public.saas_doc_votos USING btree (doc_id); + + +-- +-- Name: saas_doc_votos_user_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_doc_votos_user_id_idx ON public.saas_doc_votos USING btree (user_id); + + +-- +-- Name: saas_docs_categoria_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_docs_categoria_idx ON public.saas_docs USING btree (categoria); + + +-- +-- Name: saas_docs_exibir_no_faq_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_docs_exibir_no_faq_idx ON public.saas_docs USING btree (exibir_no_faq) WHERE (exibir_no_faq = true); + + +-- +-- Name: saas_docs_path_ativo_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_docs_path_ativo_idx ON public.saas_docs USING btree (pagina_path, ativo); + + +-- +-- Name: saas_faq_ativo_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_ativo_idx ON public.saas_faq USING btree (ativo); + + +-- +-- Name: saas_faq_categoria_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_categoria_idx ON public.saas_faq USING btree (categoria); + + +-- +-- Name: saas_faq_fts_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_fts_idx ON public.saas_faq USING gin (to_tsvector('portuguese'::regconfig, ((COALESCE(pergunta, ''::text) || ' '::text) || COALESCE(conteudo, ''::text)))); + + +-- +-- Name: saas_faq_itens_ativo_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_itens_ativo_idx ON public.saas_faq_itens USING btree (ativo); + + +-- +-- Name: saas_faq_itens_doc_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_itens_doc_id_idx ON public.saas_faq_itens USING btree (doc_id); + + +-- +-- Name: saas_faq_pagina_path_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_pagina_path_idx ON public.saas_faq USING btree (pagina_path); + + +-- +-- Name: saas_faq_publico_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_publico_idx ON public.saas_faq USING btree (publico); + + +-- +-- Name: saas_faq_votos_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_votos_idx ON public.saas_faq USING btree (votos DESC); + + +-- +-- Name: services_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX services_owner_idx ON public.services USING btree (owner_id); + + +-- +-- Name: services_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX services_tenant_idx ON public.services USING btree (tenant_id); + + +-- +-- Name: sint_personal_created_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_personal_created_idx ON public.subscription_intents_personal USING btree (created_at DESC); + + +-- +-- Name: sint_personal_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_personal_status_idx ON public.subscription_intents_personal USING btree (status); + + +-- +-- Name: sint_tenant_created_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_tenant_created_idx ON public.subscription_intents_tenant USING btree (created_at DESC); + + +-- +-- Name: sint_tenant_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_tenant_status_idx ON public.subscription_intents_tenant USING btree (status); + + +-- +-- Name: sint_tenant_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_tenant_tenant_idx ON public.subscription_intents_tenant USING btree (tenant_id); + + +-- +-- Name: subscription_events_created_at_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_created_at_idx ON public.subscription_events USING btree (created_at DESC); + + +-- +-- Name: subscription_events_owner_ref_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_owner_ref_idx ON public.subscription_events USING btree (owner_type, owner_ref); + + +-- +-- Name: subscription_events_sub_created_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_sub_created_idx ON public.subscription_events USING btree (subscription_id, created_at DESC); + + +-- +-- Name: subscription_events_subscription_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_subscription_id_idx ON public.subscription_events USING btree (subscription_id); + + +-- +-- Name: subscriptions_one_active_per_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX subscriptions_one_active_per_tenant ON public.subscriptions USING btree (tenant_id) WHERE (status = 'active'::text); + + +-- +-- Name: subscriptions_one_active_per_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX subscriptions_one_active_per_user ON public.subscriptions USING btree (user_id) WHERE (status = 'active'::text); + + +-- +-- Name: subscriptions_one_active_per_user_personal; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX subscriptions_one_active_per_user_personal ON public.subscriptions USING btree (user_id) WHERE ((tenant_id IS NULL) AND (status = 'active'::text)); + + +-- +-- Name: subscriptions_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_owner_idx ON public.subscriptions USING btree (user_id); + + +-- +-- Name: subscriptions_plan_key_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_plan_key_idx ON public.subscriptions USING btree (plan_key); + + +-- +-- Name: subscriptions_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_status_idx ON public.subscriptions USING btree (status); + + +-- +-- Name: subscriptions_tenant_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_tenant_id_idx ON public.subscriptions USING btree (tenant_id); + + +-- +-- Name: subscriptions_tenant_period_end_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_tenant_period_end_idx ON public.subscriptions USING btree (tenant_id, current_period_end); + + +-- +-- Name: subscriptions_tenant_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_tenant_status_idx ON public.subscriptions USING btree (tenant_id, status); + + +-- +-- Name: subscriptions_user_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_user_status_idx ON public.subscriptions USING btree (user_id, status, created_at DESC); + + +-- +-- Name: support_sessions_expires_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX support_sessions_expires_idx ON public.support_sessions USING btree (expires_at); + + +-- +-- Name: support_sessions_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX support_sessions_tenant_idx ON public.support_sessions USING btree (tenant_id); + + +-- +-- Name: support_sessions_token_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX support_sessions_token_idx ON public.support_sessions USING btree (token); + + +-- +-- Name: tenant_members_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX tenant_members_tenant_idx ON public.tenant_members USING btree (tenant_id); + + +-- +-- Name: tenant_members_user_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX tenant_members_user_idx ON public.tenant_members USING btree (user_id); + + +-- +-- Name: tenant_modules_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX tenant_modules_owner_idx ON public.tenant_modules USING btree (owner_id); + + +-- +-- Name: unique_member_per_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX unique_member_per_tenant ON public.tenant_members USING btree (tenant_id, user_id); + + +-- +-- Name: uq_patient_contacts_primario; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_patient_contacts_primario ON public.patient_contacts USING btree (patient_id) WHERE ((is_primario = true) AND (ativo = true)); + + +-- +-- Name: uq_patients_tenant_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_patients_tenant_user ON public.patients USING btree (tenant_id, user_id) WHERE (user_id IS NOT NULL); + + +-- +-- Name: uq_plan_price_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_plan_price_active ON public.plan_prices USING btree (plan_id, "interval", currency) WHERE ((is_active = true) AND (active_to IS NULL)); + + +-- +-- Name: uq_plan_prices_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_plan_prices_active ON public.plan_prices USING btree (plan_id, "interval") WHERE (is_active = true); + + +-- +-- Name: uq_subscriptions_active_by_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_subscriptions_active_by_tenant ON public.subscriptions USING btree (tenant_id) WHERE ((tenant_id IS NOT NULL) AND (status = 'active'::text)); + + +-- +-- Name: uq_subscriptions_active_personal_by_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_subscriptions_active_personal_by_user ON public.subscriptions USING btree (user_id) WHERE ((tenant_id IS NULL) AND (status = 'active'::text)); + + +-- +-- Name: uq_tenant_invites_pending; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_tenant_invites_pending ON public.tenant_invites USING btree (tenant_id, lower(email), role) WHERE ((accepted_at IS NULL) AND (revoked_at IS NULL)); + + +-- +-- Name: uq_tenant_members_tenant_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_tenant_members_tenant_user ON public.tenant_members USING btree (tenant_id, user_id); + + +-- +-- Name: ux_subscriptions_active_per_personal_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX ux_subscriptions_active_per_personal_user ON public.subscriptions USING btree (user_id) WHERE ((status = 'active'::text) AND (tenant_id IS NULL)); + + +-- +-- Name: ux_subscriptions_active_per_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX ux_subscriptions_active_per_tenant ON public.subscriptions USING btree (tenant_id) WHERE ((status = 'active'::text) AND (tenant_id IS NOT NULL)); + + +-- +-- Name: ix_realtime_subscription_entity; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX ix_realtime_subscription_entity ON realtime.subscription USING btree (entity); + + +-- +-- Name: messages_inserted_at_topic_index; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_inserted_at_topic_index ON ONLY realtime.messages USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_26_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_26_inserted_at_topic_idx ON realtime.messages_2026_03_26 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_27_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_27_inserted_at_topic_idx ON realtime.messages_2026_03_27 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_28_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_28_inserted_at_topic_idx ON realtime.messages_2026_03_28 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_29_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_29_inserted_at_topic_idx ON realtime.messages_2026_03_29 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_30_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_30_inserted_at_topic_idx ON realtime.messages_2026_03_30 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_31_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_31_inserted_at_topic_idx ON realtime.messages_2026_03_31 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_04_01_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_04_01_inserted_at_topic_idx ON realtime.messages_2026_04_01 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: subscription_subscription_id_entity_filters_key; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE UNIQUE INDEX subscription_subscription_id_entity_filters_key ON realtime.subscription USING btree (subscription_id, entity, filters); + + +-- +-- Name: bname; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX bname ON storage.buckets USING btree (name); + + +-- +-- Name: bucketid_objname; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX bucketid_objname ON storage.objects USING btree (bucket_id, name); + + +-- +-- Name: buckets_analytics_unique_name_idx; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX buckets_analytics_unique_name_idx ON storage.buckets_analytics USING btree (name) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_iceberg_namespaces_bucket_id; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX idx_iceberg_namespaces_bucket_id ON storage.iceberg_namespaces USING btree (catalog_id, name); + + +-- +-- Name: idx_iceberg_tables_location; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX idx_iceberg_tables_location ON storage.iceberg_tables USING btree (location); + + +-- +-- Name: idx_iceberg_tables_namespace_id; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX idx_iceberg_tables_namespace_id ON storage.iceberg_tables USING btree (catalog_id, namespace_id, name); + + +-- +-- Name: idx_multipart_uploads_list; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX idx_multipart_uploads_list ON storage.s3_multipart_uploads USING btree (bucket_id, key, created_at); + + +-- +-- Name: idx_objects_bucket_id_name; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX idx_objects_bucket_id_name ON storage.objects USING btree (bucket_id, name COLLATE "C"); + + +-- +-- Name: idx_objects_bucket_id_name_lower; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX idx_objects_bucket_id_name_lower ON storage.objects USING btree (bucket_id, lower(name) COLLATE "C"); + + +-- +-- Name: name_prefix_search; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX name_prefix_search ON storage.objects USING btree (name text_pattern_ops); + + +-- +-- Name: vector_indexes_name_bucket_id_idx; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX vector_indexes_name_bucket_id_idx ON storage.vector_indexes USING btree (name, bucket_id); + + +-- +-- Name: supabase_functions_hooks_h_table_id_h_name_idx; Type: INDEX; Schema: supabase_functions; Owner: - +-- + +CREATE INDEX supabase_functions_hooks_h_table_id_h_name_idx ON supabase_functions.hooks USING btree (hook_table_id, hook_name); + + +-- +-- Name: supabase_functions_hooks_request_id_idx; Type: INDEX; Schema: supabase_functions; Owner: - +-- + +CREATE INDEX supabase_functions_hooks_request_id_idx ON supabase_functions.hooks USING btree (request_id); + + +-- +-- Name: messages_2026_03_26_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_26_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_26_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_26_pkey; + + +-- +-- Name: messages_2026_03_27_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_27_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_27_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_27_pkey; + + +-- +-- Name: messages_2026_03_28_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_28_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_28_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_28_pkey; + + +-- +-- Name: messages_2026_03_29_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_29_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_29_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_29_pkey; + + +-- +-- Name: messages_2026_03_30_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_30_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_30_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_30_pkey; + + +-- +-- Name: messages_2026_03_31_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_31_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_31_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_31_pkey; + + +-- +-- Name: messages_2026_04_01_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_04_01_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_04_01_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_04_01_pkey; + + +-- +-- Name: users on_auth_user_created; Type: TRIGGER; Schema: auth; Owner: - +-- + +CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.handle_new_user(); + + +-- +-- Name: users trg_seed_patient_groups; Type: TRIGGER; Schema: auth; Owner: - +-- + +CREATE TRIGGER trg_seed_patient_groups AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.on_new_user_seed_patient_groups(); + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER agenda_bloqueios_updated_at BEFORE UPDATE ON public.agenda_bloqueios FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agendador_configuracoes agendador_slug_trigger; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER agendador_slug_trigger BEFORE INSERT OR UPDATE ON public.agendador_configuracoes FOR EACH ROW EXECUTE FUNCTION public.agendador_gerar_slug(); + + +-- +-- Name: tenant_members prevent_saas_membership_trigger; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER prevent_saas_membership_trigger BEFORE INSERT ON public.tenant_members FOR EACH ROW EXECUTE FUNCTION public.prevent_saas_membership(); + + +-- +-- Name: insurance_plan_services set_insurance_plan_services_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER set_insurance_plan_services_updated_at BEFORE UPDATE ON public.insurance_plan_services FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: user_settings t_user_settings_set_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER t_user_settings_set_updated_at BEFORE UPDATE ON public.user_settings FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_configuracoes tg_agenda_configuracoes_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_configuracoes_updated_at BEFORE UPDATE ON public.agenda_configuracoes FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_eventos tg_agenda_eventos_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_eventos_updated_at BEFORE UPDATE ON public.agenda_eventos FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_excecoes tg_agenda_excecoes_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_excecoes_updated_at BEFORE UPDATE ON public.agenda_excecoes FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_regras_semanais tg_agenda_regras_semanais_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_regras_semanais_updated_at BEFORE UPDATE ON public.agenda_regras_semanais FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: recurrence_rules tg_recurrence_rules_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_recurrence_rules_updated_at BEFORE UPDATE ON public.recurrence_rules FOR EACH ROW EXECUTE FUNCTION public.set_updated_at_recurrence(); + + +-- +-- Name: plan_public tr_plan_public_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tr_plan_public_updated_at BEFORE UPDATE ON public.plan_public FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: profiles trg_account_type_immutable; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_account_type_immutable BEFORE UPDATE OF account_type ON public.profiles FOR EACH ROW EXECUTE FUNCTION public.guard_account_type_immutable(); + + +-- +-- Name: agenda_configuracoes trg_agenda_cfg_sync; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_cfg_sync BEFORE INSERT OR UPDATE ON public.agenda_configuracoes FOR EACH ROW EXECUTE FUNCTION public.agenda_cfg_sync(); + + +-- +-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_del; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_eventos_busy_mirror_del AFTER DELETE ON public.agenda_eventos FOR EACH ROW WHEN (((old.mirror_of_event_id IS NULL) AND (old.tenant_id = old.owner_id))) EXECUTE FUNCTION public.sync_busy_mirror_agenda_eventos(); + + +-- +-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_ins; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_eventos_busy_mirror_ins AFTER INSERT ON public.agenda_eventos FOR EACH ROW WHEN (((new.mirror_of_event_id IS NULL) AND (new.tenant_id = new.owner_id) AND (new.visibility_scope = ANY (ARRAY['busy_only'::text, 'private'::text])))) EXECUTE FUNCTION public.sync_busy_mirror_agenda_eventos(); + + +-- +-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_upd; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_eventos_busy_mirror_upd AFTER UPDATE ON public.agenda_eventos FOR EACH ROW WHEN (((new.mirror_of_event_id IS NULL) AND (new.tenant_id = new.owner_id) AND ((new.visibility_scope IS DISTINCT FROM old.visibility_scope) OR (new.inicio_em IS DISTINCT FROM old.inicio_em) OR (new.fim_em IS DISTINCT FROM old.fim_em) OR (new.owner_id IS DISTINCT FROM old.owner_id) OR (new.tenant_id IS DISTINCT FROM old.tenant_id)))) EXECUTE FUNCTION public.sync_busy_mirror_agenda_eventos(); + + +-- +-- Name: agenda_regras_semanais trg_agenda_regras_semanais_no_overlap; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_regras_semanais_no_overlap BEFORE INSERT OR UPDATE ON public.agenda_regras_semanais FOR EACH ROW EXECUTE FUNCTION public.fn_agenda_regras_semanais_no_overlap(); + + +-- +-- Name: agenda_eventos trg_auto_financial_from_session; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_auto_financial_from_session AFTER UPDATE OF status ON public.agenda_eventos FOR EACH ROW EXECUTE FUNCTION public.auto_create_financial_record_from_session(); + + +-- +-- Name: notification_preferences trg_cancel_notifs_on_opt_out; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_cancel_notifs_on_opt_out AFTER UPDATE ON public.notification_preferences FOR EACH ROW EXECUTE FUNCTION public.cancel_notifications_on_opt_out(); + + +-- +-- Name: agenda_eventos trg_cancel_notifs_on_session_cancel; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_cancel_notifs_on_session_cancel AFTER UPDATE ON public.agenda_eventos FOR EACH ROW WHEN ((new.status IS DISTINCT FROM old.status)) EXECUTE FUNCTION public.cancel_notifications_on_session_cancel(); + + +-- +-- Name: company_profiles trg_company_profiles_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_company_profiles_updated_at BEFORE UPDATE ON public.company_profiles FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: determined_commitment_fields trg_determined_commitment_fields_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_determined_commitment_fields_updated_at BEFORE UPDATE ON public.determined_commitment_fields FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: determined_commitments trg_determined_commitments_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_determined_commitments_updated_at BEFORE UPDATE ON public.determined_commitments FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: email_layout_config trg_email_layout_config_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_email_layout_config_updated_at BEFORE UPDATE ON public.email_layout_config FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: email_templates_global trg_email_templates_global_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_email_templates_global_updated_at BEFORE UPDATE ON public.email_templates_global FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: email_templates_tenant trg_email_templates_tenant_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_email_templates_tenant_updated_at BEFORE UPDATE ON public.email_templates_tenant FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: financial_exceptions trg_financial_exceptions_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_financial_exceptions_updated_at BEFORE UPDATE ON public.financial_exceptions FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: financial_records trg_financial_records_auto_overdue; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_financial_records_auto_overdue BEFORE UPDATE ON public.financial_records FOR EACH ROW EXECUTE FUNCTION public.trg_fn_financial_records_auto_overdue(); + + +-- +-- Name: financial_records trg_financial_records_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_financial_records_updated_at BEFORE UPDATE ON public.financial_records FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: global_notices trg_global_notices_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_global_notices_updated_at BEFORE UPDATE ON public.global_notices FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: insurance_plans trg_insurance_plans_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_insurance_plans_updated_at BEFORE UPDATE ON public.insurance_plans FOR EACH ROW EXECUTE FUNCTION public.set_insurance_plans_updated_at(); + + +-- +-- Name: medicos trg_medicos_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_medicos_updated_at BEFORE UPDATE ON public.medicos FOR EACH ROW EXECUTE FUNCTION public.set_medicos_updated_at(); + + +-- +-- Name: plans trg_no_change_core_plan_key; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_no_change_core_plan_key BEFORE UPDATE ON public.plans FOR EACH ROW EXECUTE FUNCTION public.guard_no_change_core_plan_key(); + + +-- +-- Name: plans trg_no_change_plan_target; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_no_change_plan_target BEFORE UPDATE ON public.plans FOR EACH ROW EXECUTE FUNCTION public.guard_no_change_plan_target(); + + +-- +-- Name: plans trg_no_delete_core_plans; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_no_delete_core_plans BEFORE DELETE ON public.plans FOR EACH ROW EXECUTE FUNCTION public.guard_no_delete_core_plans(); + + +-- +-- Name: notification_channels trg_notification_channels_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_channels_updated_at BEFORE UPDATE ON public.notification_channels FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_logs trg_notification_logs_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_logs_updated_at BEFORE UPDATE ON public.notification_logs FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_preferences trg_notification_preferences_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_preferences_updated_at BEFORE UPDATE ON public.notification_preferences FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_queue trg_notification_queue_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_queue_updated_at BEFORE UPDATE ON public.notification_queue FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_schedules trg_notification_schedules_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_schedules_updated_at BEFORE UPDATE ON public.notification_schedules FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_templates trg_notification_templates_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_templates_updated_at BEFORE UPDATE ON public.notification_templates FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patient_intake_requests trg_notify_on_intake; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notify_on_intake AFTER INSERT ON public.patient_intake_requests FOR EACH ROW EXECUTE FUNCTION public.notify_on_intake(); + + +-- +-- Name: agendador_solicitacoes trg_notify_on_scheduling; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notify_on_scheduling AFTER INSERT ON public.agendador_solicitacoes FOR EACH ROW EXECUTE FUNCTION public.notify_on_scheduling(); + + +-- +-- Name: agenda_eventos trg_notify_on_session_status; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notify_on_session_status AFTER UPDATE OF status ON public.agenda_eventos FOR EACH ROW EXECUTE FUNCTION public.notify_on_session_status(); + + +-- +-- Name: tenant_members trg_patient_cannot_own_tenant; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_cannot_own_tenant BEFORE INSERT OR UPDATE ON public.tenant_members FOR EACH ROW EXECUTE FUNCTION public.guard_patient_cannot_own_tenant(); + + +-- +-- Name: patient_contacts trg_patient_contacts_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_contacts_updated_at BEFORE UPDATE ON public.patient_contacts FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patient_groups trg_patient_groups_set_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_groups_set_updated_at BEFORE UPDATE ON public.patient_groups FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patient_intake_requests trg_patient_intake_requests_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_intake_requests_updated_at BEFORE UPDATE ON public.patient_intake_requests FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patients trg_patient_risco_timeline; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_risco_timeline AFTER UPDATE OF risco_elevado ON public.patients FOR EACH ROW EXECUTE FUNCTION public.trg_fn_patient_risco_timeline(); + + +-- +-- Name: patients trg_patient_status_history; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_status_history AFTER INSERT OR UPDATE OF status ON public.patients FOR EACH ROW EXECUTE FUNCTION public.trg_fn_patient_status_history(); + + +-- +-- Name: patients trg_patient_status_timeline; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_status_timeline AFTER INSERT OR UPDATE OF status ON public.patients FOR EACH ROW EXECUTE FUNCTION public.trg_fn_patient_status_timeline(); + + +-- +-- Name: patient_tags trg_patient_tags_set_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_tags_set_updated_at BEFORE UPDATE ON public.patient_tags FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patients trg_patients_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patients_updated_at BEFORE UPDATE ON public.patients FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patients trg_patients_validate_members; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patients_validate_members BEFORE INSERT OR UPDATE OF tenant_id, responsible_member_id, patient_scope, therapist_member_id ON public.patients FOR EACH ROW EXECUTE FUNCTION public.patients_validate_member_consistency(); + + +-- +-- Name: payment_settings trg_payment_settings_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_payment_settings_updated_at BEFORE UPDATE ON public.payment_settings FOR EACH ROW EXECUTE FUNCTION public.update_payment_settings_updated_at(); + + +-- +-- Name: patient_groups trg_prevent_promoting_to_system; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_prevent_promoting_to_system BEFORE UPDATE ON public.patient_groups FOR EACH ROW EXECUTE FUNCTION public.prevent_promoting_to_system(); + + +-- +-- Name: patient_groups trg_prevent_system_group_changes; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_prevent_system_group_changes BEFORE DELETE OR UPDATE ON public.patient_groups FOR EACH ROW EXECUTE FUNCTION public.prevent_system_group_changes(); + + +-- +-- Name: professional_pricing trg_professional_pricing_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_professional_pricing_updated_at BEFORE UPDATE ON public.professional_pricing FOR EACH ROW EXECUTE FUNCTION public.update_professional_pricing_updated_at(); + + +-- +-- Name: profiles trg_profiles_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_profiles_updated_at BEFORE UPDATE ON public.profiles FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patient_support_contacts trg_psc_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_psc_updated_at BEFORE UPDATE ON public.patient_support_contacts FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: services trg_services_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_services_updated_at BEFORE UPDATE ON public.services FOR EACH ROW EXECUTE FUNCTION public.set_services_updated_at(); + + +-- +-- Name: subscription_intents trg_subscription_intents_view_insert; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_subscription_intents_view_insert INSTEAD OF INSERT ON public.subscription_intents FOR EACH ROW EXECUTE FUNCTION public.subscription_intents_view_insert(); + + +-- +-- Name: subscriptions trg_subscriptions_validate_scope; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_subscriptions_validate_scope BEFORE INSERT OR UPDATE ON public.subscriptions FOR EACH ROW EXECUTE FUNCTION public.subscriptions_validate_scope(); + + +-- +-- Name: tenant_features trg_tenant_features_guard_with_plan; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_tenant_features_guard_with_plan BEFORE INSERT OR UPDATE ON public.tenant_features FOR EACH ROW EXECUTE FUNCTION public.tenant_features_guard_with_plan(); + + +-- +-- Name: tenant_features trg_tenant_features_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_tenant_features_updated_at BEFORE UPDATE ON public.tenant_features FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: tenants trg_tenant_kind_immutable; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_tenant_kind_immutable BEFORE UPDATE OF kind ON public.tenants FOR EACH ROW EXECUTE FUNCTION public.guard_tenant_kind_immutable(); + + +-- +-- Name: therapist_payouts trg_therapist_payouts_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_therapist_payouts_updated_at BEFORE UPDATE ON public.therapist_payouts FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: user_settings trg_user_settings_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_user_settings_updated_at BEFORE UPDATE ON public.user_settings FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: subscription tr_check_filters; Type: TRIGGER; Schema: realtime; Owner: - +-- + +CREATE TRIGGER tr_check_filters BEFORE INSERT OR UPDATE ON realtime.subscription FOR EACH ROW EXECUTE FUNCTION realtime.subscription_check_filters(); + + +-- +-- Name: buckets enforce_bucket_name_length_trigger; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER enforce_bucket_name_length_trigger BEFORE INSERT OR UPDATE OF name ON storage.buckets FOR EACH ROW EXECUTE FUNCTION storage.enforce_bucket_name_length(); + + +-- +-- Name: buckets protect_buckets_delete; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER protect_buckets_delete BEFORE DELETE ON storage.buckets FOR EACH STATEMENT EXECUTE FUNCTION storage.protect_delete(); + + +-- +-- Name: objects protect_objects_delete; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER protect_objects_delete BEFORE DELETE ON storage.objects FOR EACH STATEMENT EXECUTE FUNCTION storage.protect_delete(); + + +-- +-- Name: objects update_objects_updated_at; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER update_objects_updated_at BEFORE UPDATE ON storage.objects FOR EACH ROW EXECUTE FUNCTION storage.update_updated_at_column(); + + +-- +-- Name: extensions extensions_tenant_external_id_fkey; Type: FK CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.extensions + ADD CONSTRAINT extensions_tenant_external_id_fkey FOREIGN KEY (tenant_external_id) REFERENCES _realtime.tenants(external_id) ON DELETE CASCADE; + + +-- +-- Name: identities identities_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.identities + ADD CONSTRAINT identities_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: mfa_amr_claims mfa_amr_claims_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_amr_claims + ADD CONSTRAINT mfa_amr_claims_session_id_fkey FOREIGN KEY (session_id) REFERENCES auth.sessions(id) ON DELETE CASCADE; + + +-- +-- Name: mfa_challenges mfa_challenges_auth_factor_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_challenges + ADD CONSTRAINT mfa_challenges_auth_factor_id_fkey FOREIGN KEY (factor_id) REFERENCES auth.mfa_factors(id) ON DELETE CASCADE; + + +-- +-- Name: mfa_factors mfa_factors_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_factors + ADD CONSTRAINT mfa_factors_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_authorizations oauth_authorizations_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_client_id_fkey FOREIGN KEY (client_id) REFERENCES auth.oauth_clients(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_authorizations oauth_authorizations_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_consents oauth_consents_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_client_id_fkey FOREIGN KEY (client_id) REFERENCES auth.oauth_clients(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_consents oauth_consents_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: one_time_tokens one_time_tokens_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.one_time_tokens + ADD CONSTRAINT one_time_tokens_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: refresh_tokens refresh_tokens_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens + ADD CONSTRAINT refresh_tokens_session_id_fkey FOREIGN KEY (session_id) REFERENCES auth.sessions(id) ON DELETE CASCADE; + + +-- +-- Name: saml_providers saml_providers_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_providers + ADD CONSTRAINT saml_providers_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; + + +-- +-- Name: saml_relay_states saml_relay_states_flow_state_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_relay_states + ADD CONSTRAINT saml_relay_states_flow_state_id_fkey FOREIGN KEY (flow_state_id) REFERENCES auth.flow_state(id) ON DELETE CASCADE; + + +-- +-- Name: saml_relay_states saml_relay_states_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_relay_states + ADD CONSTRAINT saml_relay_states_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; + + +-- +-- Name: sessions sessions_oauth_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sessions + ADD CONSTRAINT sessions_oauth_client_id_fkey FOREIGN KEY (oauth_client_id) REFERENCES auth.oauth_clients(id) ON DELETE CASCADE; + + +-- +-- Name: sessions sessions_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sessions + ADD CONSTRAINT sessions_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: sso_domains sso_domains_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sso_domains + ADD CONSTRAINT sso_domains_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; + + +-- +-- Name: addon_credits addon_credits_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT addon_credits_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id); + + +-- +-- Name: addon_credits addon_credits_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT addon_credits_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id); + + +-- +-- Name: addon_transactions addon_transactions_admin_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_admin_user_id_fkey FOREIGN KEY (admin_user_id) REFERENCES auth.users(id); + + +-- +-- Name: addon_transactions addon_transactions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id); + + +-- +-- Name: addon_transactions addon_transactions_product_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.addon_products(id); + + +-- +-- Name: addon_transactions addon_transactions_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id); + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_bloqueios + ADD CONSTRAINT agenda_bloqueios_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_bloqueios + ADD CONSTRAINT agenda_bloqueios_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_configuracoes + ADD CONSTRAINT agenda_configuracoes_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_eventos agenda_eventos_billing_contract_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_billing_contract_id_fkey FOREIGN KEY (billing_contract_id) REFERENCES public.billing_contracts(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_determined_commitment_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_determined_commitment_fk FOREIGN KEY (determined_commitment_id) REFERENCES public.determined_commitments(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_insurance_plan_id_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_insurance_plan_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_insurance_plan_service_id_fkey FOREIGN KEY (insurance_plan_service_id) REFERENCES public.insurance_plan_services(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_recurrence_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_recurrence_id_fkey FOREIGN KEY (recurrence_id) REFERENCES public.recurrence_rules(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_terapeuta_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_terapeuta_fk FOREIGN KEY (terapeuta_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_excecoes agenda_excecoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_excecoes + ADD CONSTRAINT agenda_excecoes_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_online_slots agenda_online_slots_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_online_slots agenda_online_slots_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_regras_semanais + ADD CONSTRAINT agenda_regras_semanais_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais + ADD CONSTRAINT agenda_slots_bloqueados_semanais_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_regras + ADD CONSTRAINT agenda_slots_regras_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_configuracoes agendador_configuracoes_owner_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_configuracoes + ADD CONSTRAINT agendador_configuracoes_owner_fk FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_configuracoes agendador_configuracoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_configuracoes + ADD CONSTRAINT agendador_configuracoes_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_solicitacoes agendador_sol_owner_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_solicitacoes + ADD CONSTRAINT agendador_sol_owner_fk FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_solicitacoes agendador_sol_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_solicitacoes + ADD CONSTRAINT agendador_sol_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: billing_contracts billing_contracts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.billing_contracts + ADD CONSTRAINT billing_contracts_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: billing_contracts billing_contracts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.billing_contracts + ADD CONSTRAINT billing_contracts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: commitment_services commitment_services_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_services + ADD CONSTRAINT commitment_services_commitment_id_fkey FOREIGN KEY (commitment_id) REFERENCES public.agenda_eventos(id) ON DELETE CASCADE; + + +-- +-- Name: commitment_services commitment_services_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_services + ADD CONSTRAINT commitment_services_service_id_fkey FOREIGN KEY (service_id) REFERENCES public.services(id) ON DELETE RESTRICT; + + +-- +-- Name: commitment_time_logs commitment_time_logs_calendar_event_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_calendar_event_id_fkey FOREIGN KEY (calendar_event_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; + + +-- +-- Name: commitment_time_logs commitment_time_logs_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_commitment_id_fkey FOREIGN KEY (commitment_id) REFERENCES public.determined_commitments(id) ON DELETE CASCADE; + + +-- +-- Name: commitment_time_logs commitment_time_logs_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: company_profiles company_profiles_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.company_profiles + ADD CONSTRAINT company_profiles_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: determined_commitment_fields determined_commitment_fields_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitment_fields + ADD CONSTRAINT determined_commitment_fields_commitment_id_fkey FOREIGN KEY (commitment_id) REFERENCES public.determined_commitments(id) ON DELETE CASCADE; + + +-- +-- Name: determined_commitment_fields determined_commitment_fields_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitment_fields + ADD CONSTRAINT determined_commitment_fields_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: determined_commitments determined_commitments_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitments + ADD CONSTRAINT determined_commitments_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: email_layout_config email_layout_config_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_layout_config + ADD CONSTRAINT email_layout_config_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: email_templates_tenant email_templates_tenant_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: email_templates_tenant email_templates_tenant_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: feriados feriados_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: feriados feriados_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: financial_categories financial_categories_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_categories + ADD CONSTRAINT financial_categories_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: financial_exceptions financial_exceptions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_exceptions + ADD CONSTRAINT financial_exceptions_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: financial_records financial_records_agenda_evento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_agenda_evento_id_fkey FOREIGN KEY (agenda_evento_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_category_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_category_id_fkey FOREIGN KEY (category_id) REFERENCES public.financial_categories(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_insurance_plan_id_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_user_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: global_notices global_notices_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.global_notices + ADD CONSTRAINT global_notices_created_by_fkey FOREIGN KEY (created_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: insurance_plan_services insurance_plan_services_plan_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plan_services + ADD CONSTRAINT insurance_plan_services_plan_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE CASCADE; + + +-- +-- Name: insurance_plans insurance_plans_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plans + ADD CONSTRAINT insurance_plans_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: module_features module_features_feature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.module_features + ADD CONSTRAINT module_features_feature_id_fkey FOREIGN KEY (feature_id) REFERENCES public.features(id) ON DELETE CASCADE; + + +-- +-- Name: module_features module_features_module_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.module_features + ADD CONSTRAINT module_features_module_id_fkey FOREIGN KEY (module_id) REFERENCES public.modules(id) ON DELETE CASCADE; + + +-- +-- Name: notice_dismissals notice_dismissals_notice_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_notice_id_fkey FOREIGN KEY (notice_id) REFERENCES public.global_notices(id) ON DELETE CASCADE; + + +-- +-- Name: notice_dismissals notice_dismissals_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notification_channels notification_channels_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_channels + ADD CONSTRAINT notification_channels_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notification_logs notification_logs_queue_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_logs + ADD CONSTRAINT notification_logs_queue_id_fkey FOREIGN KEY (queue_id) REFERENCES public.notification_queue(id) ON DELETE SET NULL; + + +-- +-- Name: notification_schedules notification_schedules_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_schedules + ADD CONSTRAINT notification_schedules_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notification_templates notification_templates_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_templates + ADD CONSTRAINT notification_templates_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notifications notifications_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT notifications_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: patient_contacts patient_contacts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_contacts + ADD CONSTRAINT patient_contacts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_contacts patient_contacts_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_contacts + ADD CONSTRAINT patient_contacts_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_discounts patient_discounts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_discounts + ADD CONSTRAINT patient_discounts_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: patient_discounts patient_discounts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_discounts + ADD CONSTRAINT patient_discounts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_group_patient patient_group_patient_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_group_patient + ADD CONSTRAINT patient_group_patient_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_group_patient patient_group_patient_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_group_patient + ADD CONSTRAINT patient_group_patient_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_groups patient_groups_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_groups + ADD CONSTRAINT patient_groups_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_intake_requests patient_intake_requests_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_intake_requests + ADD CONSTRAINT patient_intake_requests_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_invites patient_invites_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_invites + ADD CONSTRAINT patient_invites_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag patient_patient_tag_tag_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT patient_patient_tag_tag_id_fkey FOREIGN KEY (tag_id) REFERENCES public.patient_tags(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag patient_patient_tag_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT patient_patient_tag_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_status_history patient_status_history_alterado_por_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_alterado_por_fkey FOREIGN KEY (alterado_por) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: patient_status_history patient_status_history_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_status_history patient_status_history_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_support_contacts patient_support_contacts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_support_contacts + ADD CONSTRAINT patient_support_contacts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_tags patient_tags_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_tags + ADD CONSTRAINT patient_tags_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_timeline patient_timeline_gerado_por_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_gerado_por_fkey FOREIGN KEY (gerado_por) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: patient_timeline patient_timeline_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_timeline patient_timeline_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patients patients_convenio_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_convenio_id_fkey FOREIGN KEY (convenio_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + + +-- +-- Name: patients patients_responsible_member_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_responsible_member_id_fkey FOREIGN KEY (responsible_member_id) REFERENCES public.tenant_members(id) ON DELETE RESTRICT; + + +-- +-- Name: patients patients_risco_sinalizado_por_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_risco_sinalizado_por_fkey FOREIGN KEY (risco_sinalizado_por) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: patients patients_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patients patients_therapist_member_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_therapist_member_id_fkey FOREIGN KEY (therapist_member_id) REFERENCES public.tenant_members(id); + + +-- +-- Name: patients patients_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: payment_settings payment_settings_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: payment_settings payment_settings_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: plan_features plan_features_feature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_features + ADD CONSTRAINT plan_features_feature_id_fkey FOREIGN KEY (feature_id) REFERENCES public.features(id) ON DELETE CASCADE; + + +-- +-- Name: plan_features plan_features_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_features + ADD CONSTRAINT plan_features_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: plan_prices plan_prices_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_prices + ADD CONSTRAINT plan_prices_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: plan_public_bullets plan_public_bullets_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public_bullets + ADD CONSTRAINT plan_public_bullets_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: plan_public plan_public_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public + ADD CONSTRAINT plan_public_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag ppt_patient_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT ppt_patient_fk FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag ppt_tag_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT ppt_tag_fk FOREIGN KEY (tag_id) REFERENCES public.patient_tags(id) ON DELETE CASCADE; + + +-- +-- Name: professional_pricing professional_pricing_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.professional_pricing + ADD CONSTRAINT professional_pricing_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: profiles profiles_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.profiles + ADD CONSTRAINT profiles_id_fkey FOREIGN KEY (id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_agenda_evento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_agenda_evento_id_fkey FOREIGN KEY (agenda_evento_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_recurrence_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_recurrence_id_fkey FOREIGN KEY (recurrence_id) REFERENCES public.recurrence_rules(id) ON DELETE CASCADE; + + +-- +-- Name: recurrence_rule_services recurrence_rule_services_rule_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rule_services + ADD CONSTRAINT recurrence_rule_services_rule_id_fkey FOREIGN KEY (rule_id) REFERENCES public.recurrence_rules(id) ON DELETE CASCADE; + + +-- +-- Name: recurrence_rule_services recurrence_rule_services_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rule_services + ADD CONSTRAINT recurrence_rule_services_service_id_fkey FOREIGN KEY (service_id) REFERENCES public.services(id) ON DELETE RESTRICT; + + +-- +-- Name: recurrence_rules recurrence_rules_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rules + ADD CONSTRAINT recurrence_rules_insurance_plan_id_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + + +-- +-- Name: recurrence_rules recurrence_rules_insurance_plan_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rules + ADD CONSTRAINT recurrence_rules_insurance_plan_service_id_fkey FOREIGN KEY (insurance_plan_service_id) REFERENCES public.insurance_plan_services(id) ON DELETE SET NULL; + + +-- +-- Name: saas_admins saas_admins_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_admins + ADD CONSTRAINT saas_admins_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: saas_doc_votos saas_doc_votos_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_doc_id_fkey FOREIGN KEY (doc_id) REFERENCES public.saas_docs(id) ON DELETE CASCADE; + + +-- +-- Name: saas_doc_votos saas_doc_votos_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: saas_faq_itens saas_faq_itens_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_faq_itens + ADD CONSTRAINT saas_faq_itens_doc_id_fkey FOREIGN KEY (doc_id) REFERENCES public.saas_docs(id) ON DELETE CASCADE; + + +-- +-- Name: services services_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.services + ADD CONSTRAINT services_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: subscription_intents_personal sint_personal_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_personal + ADD CONSTRAINT sint_personal_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES public.subscriptions(id) ON DELETE SET NULL; + + +-- +-- Name: subscription_intents_tenant sint_tenant_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_tenant + ADD CONSTRAINT sint_tenant_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES public.subscriptions(id) ON DELETE SET NULL; + + +-- +-- Name: subscription_events subscription_events_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_events + ADD CONSTRAINT subscription_events_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES public.subscriptions(id) ON DELETE CASCADE; + + +-- +-- Name: subscription_intents_personal subscription_intents_personal_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_personal + ADD CONSTRAINT subscription_intents_personal_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE RESTRICT; + + +-- +-- Name: subscription_intents_tenant subscription_intents_tenant_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_tenant + ADD CONSTRAINT subscription_intents_tenant_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE RESTRICT; + + +-- +-- Name: subscription_intents_legacy subscription_intents_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_legacy + ADD CONSTRAINT subscription_intents_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: subscriptions subscriptions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscriptions + ADD CONSTRAINT subscriptions_owner_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: subscriptions subscriptions_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscriptions + ADD CONSTRAINT subscriptions_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE RESTRICT; + + +-- +-- Name: support_sessions support_sessions_admin_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_admin_fk FOREIGN KEY (admin_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: support_sessions support_sessions_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_features tenant_features_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_features + ADD CONSTRAINT tenant_features_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_invites tenant_invites_accepted_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_accepted_by_fkey FOREIGN KEY (accepted_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: tenant_invites tenant_invites_invited_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_invited_by_fkey FOREIGN KEY (invited_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: tenant_invites tenant_invites_revoked_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_revoked_by_fkey FOREIGN KEY (revoked_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: tenant_invites tenant_invites_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_members tenant_members_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_members tenant_members_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_modules tenant_modules_module_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_module_id_fkey FOREIGN KEY (module_id) REFERENCES public.modules(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_modules tenant_modules_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: therapist_payout_records therapist_payout_records_financial_record_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payout_records + ADD CONSTRAINT therapist_payout_records_financial_record_id_fkey FOREIGN KEY (financial_record_id) REFERENCES public.financial_records(id) ON DELETE RESTRICT; + + +-- +-- Name: therapist_payout_records therapist_payout_records_payout_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payout_records + ADD CONSTRAINT therapist_payout_records_payout_id_fkey FOREIGN KEY (payout_id) REFERENCES public.therapist_payouts(id) ON DELETE CASCADE; + + +-- +-- Name: therapist_payouts therapist_payouts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payouts + ADD CONSTRAINT therapist_payouts_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: therapist_payouts therapist_payouts_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payouts + ADD CONSTRAINT therapist_payouts_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: twilio_subaccount_usage twilio_subaccount_usage_channel_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.twilio_subaccount_usage + ADD CONSTRAINT twilio_subaccount_usage_channel_fk FOREIGN KEY (channel_id) REFERENCES public.notification_channels(id) ON DELETE CASCADE; + + +-- +-- Name: user_settings user_settings_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_settings + ADD CONSTRAINT user_settings_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: iceberg_namespaces iceberg_namespaces_catalog_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_namespaces + ADD CONSTRAINT iceberg_namespaces_catalog_id_fkey FOREIGN KEY (catalog_id) REFERENCES storage.buckets_analytics(id) ON DELETE CASCADE; + + +-- +-- Name: iceberg_tables iceberg_tables_catalog_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_tables + ADD CONSTRAINT iceberg_tables_catalog_id_fkey FOREIGN KEY (catalog_id) REFERENCES storage.buckets_analytics(id) ON DELETE CASCADE; + + +-- +-- Name: iceberg_tables iceberg_tables_namespace_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_tables + ADD CONSTRAINT iceberg_tables_namespace_id_fkey FOREIGN KEY (namespace_id) REFERENCES storage.iceberg_namespaces(id) ON DELETE CASCADE; + + +-- +-- Name: objects objects_bucketId_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.objects + ADD CONSTRAINT "objects_bucketId_fkey" FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); + + +-- +-- Name: s3_multipart_uploads s3_multipart_uploads_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads + ADD CONSTRAINT s3_multipart_uploads_bucket_id_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); + + +-- +-- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads_parts + ADD CONSTRAINT s3_multipart_uploads_parts_bucket_id_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); + + +-- +-- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_upload_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads_parts + ADD CONSTRAINT s3_multipart_uploads_parts_upload_id_fkey FOREIGN KEY (upload_id) REFERENCES storage.s3_multipart_uploads(id) ON DELETE CASCADE; + + +-- +-- Name: vector_indexes vector_indexes_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.vector_indexes + ADD CONSTRAINT vector_indexes_bucket_id_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets_vectors(id); + + +-- +-- Name: audit_log_entries; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.audit_log_entries ENABLE ROW LEVEL SECURITY; + +-- +-- Name: flow_state; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.flow_state ENABLE ROW LEVEL SECURITY; + +-- +-- Name: identities; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.identities ENABLE ROW LEVEL SECURITY; + +-- +-- Name: instances; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.instances ENABLE ROW LEVEL SECURITY; + +-- +-- Name: mfa_amr_claims; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.mfa_amr_claims ENABLE ROW LEVEL SECURITY; + +-- +-- Name: mfa_challenges; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.mfa_challenges ENABLE ROW LEVEL SECURITY; + +-- +-- Name: mfa_factors; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.mfa_factors ENABLE ROW LEVEL SECURITY; + +-- +-- Name: one_time_tokens; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.one_time_tokens ENABLE ROW LEVEL SECURITY; + +-- +-- Name: refresh_tokens; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.refresh_tokens ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saml_providers; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.saml_providers ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saml_relay_states; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.saml_relay_states ENABLE ROW LEVEL SECURITY; + +-- +-- Name: schema_migrations; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.schema_migrations ENABLE ROW LEVEL SECURITY; + +-- +-- Name: sessions; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.sessions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: sso_domains; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.sso_domains ENABLE ROW LEVEL SECURITY; + +-- +-- Name: sso_providers; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.sso_providers ENABLE ROW LEVEL SECURITY; + +-- +-- Name: users; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.users ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_credits; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.addon_credits ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_credits addon_credits_admin_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_credits_admin_select ON public.addon_credits FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_credits addon_credits_admin_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_credits_admin_write ON public.addon_credits TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_credits addon_credits_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_credits_select_own ON public.addon_credits FOR SELECT TO authenticated USING ((public.is_tenant_member(tenant_id) OR (owner_id = auth.uid()))); + + +-- +-- Name: addon_products; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.addon_products ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_products addon_products_admin_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_products_admin_all ON public.addon_products TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_products addon_products_select_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_products_select_authenticated ON public.addon_products FOR SELECT TO authenticated USING (((deleted_at IS NULL) AND (is_active = true) AND (is_visible = true))); + + +-- +-- Name: addon_transactions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.addon_transactions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_transactions addon_transactions_admin_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_transactions_admin_insert ON public.addon_transactions FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_transactions addon_transactions_admin_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_transactions_admin_select ON public.addon_transactions FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_transactions addon_transactions_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_transactions_select_own ON public.addon_transactions FOR SELECT TO authenticated USING ((public.is_tenant_member(tenant_id) OR (owner_id = auth.uid()))); + + +-- +-- Name: agenda_bloqueios; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_bloqueios ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_configuracoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_configuracoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_configuracoes agenda_configuracoes_clinic_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_configuracoes_clinic_read ON public.agenda_configuracoes FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_clinic_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_configuracoes_clinic_write ON public.agenda_configuracoes USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_configuracoes_owner ON public.agenda_configuracoes USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_eventos; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_eventos ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_eventos agenda_eventos_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_delete ON public.agenda_eventos FOR DELETE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.delete'::text))); + + +-- +-- Name: agenda_eventos agenda_eventos_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_insert ON public.agenda_eventos FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.create'::text))); + + +-- +-- Name: agenda_eventos agenda_eventos_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_owner_all ON public.agenda_eventos TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_eventos agenda_eventos_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_select ON public.agenda_eventos FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_eventos agenda_eventos_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_update ON public.agenda_eventos FOR UPDATE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_excecoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_excecoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_excecoes agenda_excecoes_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_excecoes_owner ON public.agenda_excecoes USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_excecoes agenda_excecoes_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_excecoes_select ON public.agenda_excecoes FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_excecoes agenda_excecoes_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_excecoes_write ON public.agenda_excecoes USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_online_slots; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_online_slots ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_online_slots agenda_online_slots_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_online_slots_owner ON public.agenda_online_slots USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_online_slots agenda_online_slots_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_online_slots_select ON public.agenda_online_slots FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_online_slots agenda_online_slots_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_online_slots_write ON public.agenda_online_slots USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_regras_semanais; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_regras_semanais ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_regras_semanais_owner ON public.agenda_regras_semanais USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_regras_semanais_select ON public.agenda_regras_semanais FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_regras_semanais_write ON public.agenda_regras_semanais USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_slots_bloqueados_semanais; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_slots_bloqueados_semanais ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_bloqueados_semanais_select ON public.agenda_slots_bloqueados_semanais FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_bloqueados_semanais_write ON public.agenda_slots_bloqueados_semanais USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_slots_regras; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_slots_regras ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_slots_regras agenda_slots_regras_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_regras_select ON public.agenda_slots_regras FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_regras_write ON public.agenda_slots_regras USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agendador_configuracoes agendador_cfg_public_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_cfg_public_read ON public.agendador_configuracoes FOR SELECT TO anon USING (((ativo = true) AND (link_slug IS NOT NULL))); + + +-- +-- Name: agendador_configuracoes agendador_cfg_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_cfg_select ON public.agendador_configuracoes FOR SELECT USING ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_configuracoes agendador_cfg_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_cfg_write ON public.agendador_configuracoes USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_configuracoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agendador_configuracoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agendador_solicitacoes agendador_sol_owner_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_owner_select ON public.agendador_solicitacoes FOR SELECT USING ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_solicitacoes agendador_sol_owner_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_owner_write ON public.agendador_solicitacoes USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_solicitacoes agendador_sol_patient_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_patient_read ON public.agendador_solicitacoes FOR SELECT TO authenticated USING (((auth.uid() = user_id) OR (auth.uid() = owner_id))); + + +-- +-- Name: agendador_solicitacoes agendador_sol_public_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_public_insert ON public.agendador_solicitacoes FOR INSERT TO anon WITH CHECK (true); + + +-- +-- Name: agendador_solicitacoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agendador_solicitacoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: billing_contracts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.billing_contracts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: billing_contracts billing_contracts: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "billing_contracts: owner full access" ON public.billing_contracts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_delete ON public.agenda_bloqueios FOR DELETE TO authenticated USING ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_insert ON public.agenda_bloqueios FOR INSERT TO authenticated WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_select_clinic; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_select_clinic ON public.agenda_bloqueios FOR SELECT TO authenticated USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE ((tenant_members.user_id = auth.uid()) AND (tenant_members.role = ANY (ARRAY['admin'::text, 'clinic_admin'::text, 'tenant_admin'::text, 'secretary'::text])))))); + + +-- +-- Name: agenda_bloqueios bloqueios_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_select_own ON public.agenda_bloqueios FOR SELECT TO authenticated USING ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_update ON public.agenda_bloqueios FOR UPDATE TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: saas_docs clinic_admin_read_all_docs; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY clinic_admin_read_all_docs ON public.saas_docs FOR SELECT TO authenticated USING (((ativo = true) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = ANY (ARRAY['clinic_admin'::text, 'tenant_admin'::text]))))))); + + +-- +-- Name: commitment_services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.commitment_services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: commitment_services commitment_services: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "commitment_services: owner full access" ON public.commitment_services USING ((EXISTS ( SELECT 1 + FROM public.services s + WHERE ((s.id = commitment_services.service_id) AND (s.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.services s + WHERE ((s.id = commitment_services.service_id) AND (s.owner_id = auth.uid()))))); + + +-- +-- Name: commitment_time_logs; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.commitment_time_logs ENABLE ROW LEVEL SECURITY; + +-- +-- Name: company_profiles; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.company_profiles ENABLE ROW LEVEL SECURITY; + +-- +-- Name: company_profiles company_profiles_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_delete ON public.company_profiles FOR DELETE USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: company_profiles company_profiles_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_insert ON public.company_profiles FOR INSERT WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: company_profiles company_profiles_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_select ON public.company_profiles FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: company_profiles company_profiles_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_update ON public.company_profiles FOR UPDATE USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: commitment_time_logs ctl_delete_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_delete_for_active_member ON public.commitment_time_logs FOR DELETE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: commitment_time_logs ctl_insert_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_insert_for_active_member ON public.commitment_time_logs FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: commitment_time_logs ctl_select_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_select_for_active_member ON public.commitment_time_logs FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: commitment_time_logs ctl_update_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_update_for_active_member ON public.commitment_time_logs FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitments dc_delete_custom_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_delete_custom_for_active_member ON public.determined_commitments FOR DELETE TO authenticated USING (((is_native = false) AND (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: determined_commitments dc_insert_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_insert_for_active_member ON public.determined_commitments FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitments dc_select_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_select_for_active_member ON public.determined_commitments FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitments dc_update_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_update_for_active_member ON public.determined_commitments FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_delete_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_delete_for_active_member ON public.determined_commitment_fields FOR DELETE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_insert_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_insert_for_active_member ON public.determined_commitment_fields FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_select_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_select_for_active_member ON public.determined_commitment_fields FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_update_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_update_for_active_member ON public.determined_commitment_fields FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: agenda_bloqueios delete own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "delete own" ON public.agenda_bloqueios FOR DELETE USING ((owner_id = auth.uid())); + + +-- +-- Name: determined_commitment_fields; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.determined_commitment_fields ENABLE ROW LEVEL SECURITY; + +-- +-- Name: determined_commitments; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.determined_commitments ENABLE ROW LEVEL SECURITY; + +-- +-- Name: dev_user_credentials dev_creds_select_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dev_creds_select_saas_admin ON public.dev_user_credentials FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles p + WHERE ((p.id = auth.uid()) AND (p.role = 'saas_admin'::text))))); + + +-- +-- Name: dev_user_credentials dev_creds_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dev_creds_write_saas_admin ON public.dev_user_credentials TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles p + WHERE ((p.id = auth.uid()) AND (p.role = 'saas_admin'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.profiles p + WHERE ((p.id = auth.uid()) AND (p.role = 'saas_admin'::text))))); + + +-- +-- Name: dev_user_credentials; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.dev_user_credentials ENABLE ROW LEVEL SECURITY; + +-- +-- Name: email_layout_config; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.email_layout_config ENABLE ROW LEVEL SECURITY; + +-- +-- Name: email_templates_global; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.email_templates_global ENABLE ROW LEVEL SECURITY; + +-- +-- Name: email_templates_tenant; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.email_templates_tenant ENABLE ROW LEVEL SECURITY; + +-- +-- Name: entitlements_invalidation ent_inv_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ent_inv_select_own ON public.entitlements_invalidation FOR SELECT USING (((owner_id = auth.uid()) OR public.is_saas_admin())); + + +-- +-- Name: entitlements_invalidation ent_inv_update_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ent_inv_update_saas ON public.entitlements_invalidation FOR UPDATE USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: entitlements_invalidation ent_inv_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ent_inv_write_saas ON public.entitlements_invalidation FOR INSERT WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: entitlements_invalidation; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.entitlements_invalidation ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_faq faq_admin_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_admin_write ON public.saas_faq TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = ANY (ARRAY['saas_admin'::text, 'tenant_admin'::text, 'clinic_admin'::text])))))); + + +-- +-- Name: saas_faq faq_auth_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_auth_read ON public.saas_faq FOR SELECT TO authenticated USING ((ativo = true)); + + +-- +-- Name: saas_faq_itens faq_itens_admin_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_itens_admin_write ON public.saas_faq_itens TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = ANY (ARRAY['saas_admin'::text, 'tenant_admin'::text, 'clinic_admin'::text])))))); + + +-- +-- Name: saas_faq_itens faq_itens_auth_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_itens_auth_read ON public.saas_faq_itens FOR SELECT TO authenticated USING (((ativo = true) AND (EXISTS ( SELECT 1 + FROM public.saas_docs d + WHERE ((d.id = saas_faq_itens.doc_id) AND (d.ativo = true)))))); + + +-- +-- Name: saas_faq faq_public_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_public_read ON public.saas_faq FOR SELECT USING (((publico = true) AND (ativo = true))); + + +-- +-- Name: features; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.features ENABLE ROW LEVEL SECURITY; + +-- +-- Name: features features_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY features_read_authenticated ON public.features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: features features_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY features_write_saas_admin ON public.features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: feriados; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.feriados ENABLE ROW LEVEL SECURITY; + +-- +-- Name: feriados feriados_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_delete ON public.feriados FOR DELETE USING ((owner_id = auth.uid())); + + +-- +-- Name: feriados feriados_global_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_global_select ON public.feriados FOR SELECT USING ((tenant_id IS NULL)); + + +-- +-- Name: feriados feriados_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_insert ON public.feriados FOR INSERT WITH CHECK ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_saas_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_saas_delete ON public.feriados FOR DELETE USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_saas_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_saas_insert ON public.feriados FOR INSERT WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_saas_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_saas_select ON public.feriados FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_select ON public.feriados FOR SELECT USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: financial_categories; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.financial_categories ENABLE ROW LEVEL SECURITY; + +-- +-- Name: financial_categories financial_categories_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_categories_self ON public.financial_categories USING ((auth.uid() = user_id)) WITH CHECK ((auth.uid() = user_id)); + + +-- +-- Name: financial_exceptions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.financial_exceptions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: financial_exceptions financial_exceptions: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "financial_exceptions: owner full access" ON public.financial_exceptions USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: financial_exceptions financial_exceptions: tenant members read clinic rules; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "financial_exceptions: tenant members read clinic rules" ON public.financial_exceptions FOR SELECT USING (((owner_id IS NULL) AND (EXISTS ( SELECT 1 + FROM public.owner_users ou + WHERE ((ou.owner_id = financial_exceptions.tenant_id) AND (ou.user_id = auth.uid())))))); + + +-- +-- Name: financial_records; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.financial_records ENABLE ROW LEVEL SECURITY; + +-- +-- Name: financial_records financial_records_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_records_self ON public.financial_records USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: financial_records financial_records_tenant_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_records_tenant_admin ON public.financial_records FOR SELECT USING (((tenant_id IS NOT NULL) AND public.is_tenant_admin(tenant_id))); + + +-- +-- Name: financial_records financial_records_tenant_member_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_records_tenant_member_read ON public.financial_records FOR SELECT USING (((tenant_id IS NOT NULL) AND (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = financial_records.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: email_templates_global global templates readable by authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "global templates readable by authenticated" ON public.email_templates_global FOR SELECT USING ((auth.role() = 'authenticated'::text)); + + +-- +-- Name: global_notices; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.global_notices ENABLE ROW LEVEL SECURITY; + +-- +-- Name: global_notices global_notices_saas_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY global_notices_saas_all ON public.global_notices TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: global_notices global_notices_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY global_notices_select ON public.global_notices FOR SELECT TO authenticated USING ((is_active = true)); + + +-- +-- Name: agenda_bloqueios insert own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "insert own" ON public.agenda_bloqueios FOR INSERT WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: insurance_plan_services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.insurance_plan_services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: insurance_plan_services insurance_plan_services_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY insurance_plan_services_owner ON public.insurance_plan_services USING ((EXISTS ( SELECT 1 + FROM public.insurance_plans ip + WHERE ((ip.id = insurance_plan_services.insurance_plan_id) AND (ip.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.insurance_plans ip + WHERE ((ip.id = insurance_plan_services.insurance_plan_id) AND (ip.owner_id = auth.uid()))))); + + +-- +-- Name: insurance_plans; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.insurance_plans ENABLE ROW LEVEL SECURITY; + +-- +-- Name: insurance_plans insurance_plans: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "insurance_plans: owner full access" ON public.insurance_plans USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: login_carousel_slides; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.login_carousel_slides ENABLE ROW LEVEL SECURITY; + +-- +-- Name: medicos; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.medicos ENABLE ROW LEVEL SECURITY; + +-- +-- Name: medicos medicos: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "medicos: owner full access" ON public.medicos USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: module_features; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.module_features ENABLE ROW LEVEL SECURITY; + +-- +-- Name: module_features module_features_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY module_features_read_authenticated ON public.module_features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: module_features module_features_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY module_features_write_saas_admin ON public.module_features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: modules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.modules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: modules modules_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY modules_read_authenticated ON public.modules FOR SELECT TO authenticated USING (true); + + +-- +-- Name: modules modules_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY modules_write_saas_admin ON public.modules TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: notice_dismissals; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notice_dismissals ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notice_dismissals notice_dismissals_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notice_dismissals_own ON public.notice_dismissals TO authenticated USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: notification_logs notif_logs_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_logs_owner ON public.notification_logs FOR SELECT USING ((owner_id = auth.uid())); + + +-- +-- Name: notification_preferences notif_prefs_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_prefs_owner ON public.notification_preferences USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: notification_queue notif_queue_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_queue_owner ON public.notification_queue FOR SELECT USING ((owner_id = auth.uid())); + + +-- +-- Name: notification_schedules notif_schedules_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_schedules_owner ON public.notification_schedules USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: notification_templates notif_templates_admin_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_templates_admin_all ON public.notification_templates TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: notification_templates notif_templates_read_global; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_templates_read_global ON public.notification_templates FOR SELECT TO authenticated USING (((deleted_at IS NULL) AND (((tenant_id IS NULL) AND (is_default = true)) OR (owner_id = auth.uid()) OR public.is_tenant_member(tenant_id)))); + + +-- +-- Name: notification_templates notif_templates_write_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_templates_write_owner ON public.notification_templates TO authenticated USING (((owner_id = auth.uid()) OR public.is_tenant_member(tenant_id))) WITH CHECK (((owner_id = auth.uid()) OR public.is_tenant_member(tenant_id))); + + +-- +-- Name: notification_channels; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_channels ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_channels notification_channels_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notification_channels_owner ON public.notification_channels USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: notification_logs; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_logs ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_preferences; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_preferences ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_queue; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_queue ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_schedules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_schedules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_templates; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_templates ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notifications; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notifications owner only; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "owner only" ON public.notifications USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: owner_users; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.owner_users ENABLE ROW LEVEL SECURITY; + +-- +-- Name: owner_users owner_users: user can read own links; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "owner_users: user can read own links" ON public.owner_users FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: patient_contacts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_contacts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_contacts patient_contacts_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_contacts_select ON public.patient_contacts FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_contacts patient_contacts_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_contacts_write ON public.patient_contacts USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_discounts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_discounts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_discounts patient_discounts: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "patient_discounts: owner full access" ON public.patient_discounts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_group_patient; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_group_patient ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_group_patient patient_group_patient_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_group_patient_owner_all ON public.patient_group_patient TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.patients p + WHERE ((p.id = patient_group_patient.patient_id) AND (p.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.patients p + WHERE ((p.id = patient_group_patient.patient_id) AND (p.owner_id = auth.uid()))))); + + +-- +-- Name: patient_group_patient patient_group_patient_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_group_patient_select ON public.patient_group_patient FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_group_patient patient_group_patient_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_group_patient_write ON public.patient_group_patient USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_groups; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_groups ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_groups patient_groups_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_groups_owner_all ON public.patient_groups TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_groups patient_groups_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_groups_select ON public.patient_groups FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_groups patient_groups_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_groups_write ON public.patient_groups USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_intake_requests; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_intake_requests ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_intake_requests patient_intake_requests_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_intake_requests_owner_all ON public.patient_intake_requests TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_intake_requests patient_intake_requests_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_intake_requests_select ON public.patient_intake_requests FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_intake_requests patient_intake_requests_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_intake_requests_write ON public.patient_intake_requests USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_invites; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_invites ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_invites patient_invites_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_invites_owner_all ON public.patient_invites TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_invites patient_invites_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_invites_select ON public.patient_invites FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_invites patient_invites_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_invites_write ON public.patient_invites USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_patient_tag; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_patient_tag ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_patient_tag patient_patient_tag_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_patient_tag_owner_all ON public.patient_patient_tag TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_patient_tag patient_patient_tag_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_patient_tag_select ON public.patient_patient_tag FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_patient_tag patient_patient_tag_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_patient_tag_write ON public.patient_patient_tag USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_status_history; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_status_history ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_support_contacts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_support_contacts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_tags; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_tags ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_tags patient_tags_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_tags_owner_all ON public.patient_tags TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_tags patient_tags_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_tags_select ON public.patient_tags FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_tags patient_tags_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_tags_write ON public.patient_tags USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_timeline; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_timeline ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patients; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patients ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patients patients_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_delete ON public.patients FOR DELETE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.delete'::text))); + + +-- +-- Name: patients patients_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_insert ON public.patients FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.create'::text))); + + +-- +-- Name: patients patients_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_owner_all ON public.patients TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patients patients_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_select ON public.patients FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patients patients_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_update ON public.patients FOR UPDATE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: payment_settings; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.payment_settings ENABLE ROW LEVEL SECURITY; + +-- +-- Name: payment_settings payment_settings: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "payment_settings: owner full access" ON public.payment_settings USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: plan_features; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.plan_features ENABLE ROW LEVEL SECURITY; + +-- +-- Name: plan_features plan_features_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plan_features_read_authenticated ON public.plan_features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plan_features plan_features_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plan_features_write_saas_admin ON public.plan_features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: plans; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.plans ENABLE ROW LEVEL SECURITY; + +-- +-- Name: plans plans_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plans_read_authenticated ON public.plans FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plans plans_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plans_write_saas_admin ON public.plans TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: professional_pricing; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.professional_pricing ENABLE ROW LEVEL SECURITY; + +-- +-- Name: professional_pricing professional_pricing: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "professional_pricing: owner full access" ON public.professional_pricing USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: profiles; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; + +-- +-- Name: profiles profiles_insert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_insert_own ON public.profiles FOR INSERT WITH CHECK ((id = auth.uid())); + + +-- +-- Name: profiles profiles_read_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_read_saas_admin ON public.profiles FOR SELECT USING (public.is_saas_admin()); + + +-- +-- Name: profiles profiles_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_select_own ON public.profiles FOR SELECT USING ((id = auth.uid())); + + +-- +-- Name: profiles profiles_update_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_update_own ON public.profiles FOR UPDATE USING ((id = auth.uid())) WITH CHECK ((id = auth.uid())); + + +-- +-- Name: patient_support_contacts psc: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "psc: owner full access" ON public.patient_support_contacts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_status_history psh_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY psh_insert ON public.patient_status_history FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_status_history psh_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY psh_select ON public.patient_status_history FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_timeline pt_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY pt_insert ON public.patient_timeline FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_timeline pt_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY pt_select ON public.patient_timeline FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: login_carousel_slides public_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY public_read ON public.login_carousel_slides FOR SELECT USING ((ativo = true)); + + +-- +-- Name: features read features (auth); Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "read features (auth)" ON public.features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plan_features read plan_features (auth); Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "read plan_features (auth)" ON public.plan_features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plans read plans (auth); Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "read plans (auth)" ON public.plans FOR SELECT TO authenticated USING (true); + + +-- +-- Name: recurrence_exceptions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.recurrence_exceptions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: recurrence_exceptions recurrence_exceptions_tenant; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_exceptions_tenant ON public.recurrence_exceptions TO authenticated USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))) WITH CHECK ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: recurrence_rule_services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.recurrence_rule_services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: recurrence_rule_services recurrence_rule_services: clinic read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "recurrence_rule_services: clinic read" ON public.recurrence_rule_services FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND public.is_clinic_tenant(r.tenant_id) AND public.is_tenant_member(r.tenant_id) AND public.tenant_has_feature(r.tenant_id, 'agenda.view'::text))))); + + +-- +-- Name: recurrence_rule_services recurrence_rule_services: clinic write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "recurrence_rule_services: clinic write" ON public.recurrence_rule_services USING ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND public.is_clinic_tenant(r.tenant_id) AND public.is_tenant_member(r.tenant_id) AND public.tenant_has_feature(r.tenant_id, 'agenda.edit'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND public.is_clinic_tenant(r.tenant_id) AND public.is_tenant_member(r.tenant_id) AND public.tenant_has_feature(r.tenant_id, 'agenda.edit'::text))))); + + +-- +-- Name: recurrence_rule_services recurrence_rule_services: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "recurrence_rule_services: owner full access" ON public.recurrence_rule_services TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND (r.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND (r.owner_id = auth.uid()))))); + + +-- +-- Name: recurrence_rules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.recurrence_rules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: recurrence_rules recurrence_rules_clinic_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_rules_clinic_read ON public.recurrence_rules FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: recurrence_rules recurrence_rules_clinic_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_rules_clinic_write ON public.recurrence_rules USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: recurrence_rules recurrence_rules_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_rules_owner ON public.recurrence_rules TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: subscription_intents_legacy saas_admin can read subscription_intents; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "saas_admin can read subscription_intents" ON public.subscription_intents_legacy FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins a + WHERE (a.user_id = auth.uid())))); + + +-- +-- Name: subscription_intents_legacy saas_admin can update subscription_intents; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "saas_admin can update subscription_intents" ON public.subscription_intents_legacy FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins a + WHERE (a.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins a + WHERE (a.user_id = auth.uid())))); + + +-- +-- Name: login_carousel_slides saas_admin_full; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY saas_admin_full ON public.login_carousel_slides USING ((EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text))))); + + +-- +-- Name: saas_docs saas_admin_full_access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY saas_admin_full_access ON public.saas_docs TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: saas_admins; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_admins ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_admins saas_admins_select_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY saas_admins_select_self ON public.saas_admins FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: saas_doc_votos; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_doc_votos ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_docs; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_docs ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_faq; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_faq ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_faq_itens; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_faq_itens ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_bloqueios select own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "select own" ON public.agenda_bloqueios FOR SELECT USING ((owner_id = auth.uid())); + + +-- +-- Name: twilio_subaccount_usage service_role_manage_usage; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY service_role_manage_usage ON public.twilio_subaccount_usage USING ((auth.role() = 'service_role'::text)); + + +-- +-- Name: services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: services services: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "services: owner full access" ON public.services USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: subscription_events; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.subscription_events ENABLE ROW LEVEL SECURITY; + +-- +-- Name: subscription_events subscription_events_read_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_events_read_saas ON public.subscription_events FOR SELECT USING (public.is_saas_admin()); + + +-- +-- Name: subscription_events subscription_events_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_events_write_saas ON public.subscription_events FOR INSERT WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: subscription_intents_legacy subscription_intents_insert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_intents_insert_own ON public.subscription_intents_legacy FOR INSERT TO authenticated WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: subscription_intents_legacy; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.subscription_intents_legacy ENABLE ROW LEVEL SECURITY; + +-- +-- Name: subscription_intents_legacy subscription_intents_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_intents_select_own ON public.subscription_intents_legacy FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: subscriptions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.subscriptions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: subscriptions subscriptions read own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "subscriptions read own" ON public.subscriptions FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: subscriptions subscriptions: read if linked owner_users; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "subscriptions: read if linked owner_users" ON public.subscriptions FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.owner_users ou + WHERE ((ou.owner_id = subscriptions.user_id) AND (ou.user_id = auth.uid()))))); + + +-- +-- Name: subscriptions subscriptions_insert_own_personal; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_insert_own_personal ON public.subscriptions FOR INSERT TO authenticated WITH CHECK (((user_id = auth.uid()) AND (tenant_id IS NULL))); + + +-- +-- Name: subscriptions subscriptions_no_direct_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_no_direct_update ON public.subscriptions FOR UPDATE TO authenticated USING (false) WITH CHECK (false); + + +-- +-- Name: subscriptions subscriptions_read_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_read_own ON public.subscriptions FOR SELECT TO authenticated USING (((user_id = auth.uid()) OR public.is_saas_admin())); + + +-- +-- Name: subscriptions subscriptions_select_for_tenant_members; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_select_for_tenant_members ON public.subscriptions FOR SELECT TO authenticated USING (((tenant_id IS NOT NULL) AND (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = subscriptions.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: subscriptions subscriptions_select_own_personal; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_select_own_personal ON public.subscriptions FOR SELECT TO authenticated USING (((user_id = auth.uid()) AND (tenant_id IS NULL))); + + +-- +-- Name: subscriptions subscriptions_update_only_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_update_only_saas_admin ON public.subscriptions FOR UPDATE TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: support_sessions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.support_sessions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: support_sessions support_sessions_saas_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY support_sessions_saas_delete ON public.support_sessions FOR DELETE USING (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text)))))); + + +-- +-- Name: support_sessions support_sessions_saas_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY support_sessions_saas_insert ON public.support_sessions FOR INSERT WITH CHECK (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text)))))); + + +-- +-- Name: support_sessions support_sessions_saas_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY support_sessions_saas_select ON public.support_sessions FOR SELECT USING (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text)))))); + + +-- +-- Name: email_templates_tenant tenant manages own overrides; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "tenant manages own overrides" ON public.email_templates_tenant USING ((tenant_id = auth.uid())) WITH CHECK ((tenant_id = auth.uid())); + + +-- +-- Name: email_layout_config tenant owns email layout config; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "tenant owns email layout config" ON public.email_layout_config USING ((tenant_id = auth.uid())) WITH CHECK ((tenant_id = auth.uid())); + + +-- +-- Name: tenant_members; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.tenant_members ENABLE ROW LEVEL SECURITY; + +-- +-- Name: tenant_members tenant_members_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_members_write_saas ON public.tenant_members TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: tenant_modules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.tenant_modules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: tenant_modules tenant_modules_read_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_modules_read_own ON public.tenant_modules FOR SELECT TO authenticated USING (((owner_id = auth.uid()) OR public.is_saas_admin())); + + +-- +-- Name: tenant_modules tenant_modules_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_modules_write_saas ON public.tenant_modules TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: twilio_subaccount_usage tenant_select_own_usage; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_select_own_usage ON public.twilio_subaccount_usage FOR SELECT USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: tenants; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.tenants ENABLE ROW LEVEL SECURITY; + +-- +-- Name: tenants tenants_read_members; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenants_read_members ON public.tenants FOR SELECT TO authenticated USING ((public.is_saas_admin() OR (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = tenants.id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: tenants tenants_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenants_write_saas ON public.tenants TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: therapist_payout_records; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.therapist_payout_records ENABLE ROW LEVEL SECURITY; + +-- +-- Name: therapist_payout_records therapist_payout_records_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payout_records_self ON public.therapist_payout_records USING ((EXISTS ( SELECT 1 + FROM public.therapist_payouts tp + WHERE ((tp.id = therapist_payout_records.payout_id) AND (tp.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.therapist_payouts tp + WHERE ((tp.id = therapist_payout_records.payout_id) AND (tp.owner_id = auth.uid()))))); + + +-- +-- Name: therapist_payout_records therapist_payout_records_tenant_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payout_records_tenant_admin ON public.therapist_payout_records FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.therapist_payouts tp + WHERE ((tp.id = therapist_payout_records.payout_id) AND public.is_tenant_admin(tp.tenant_id))))); + + +-- +-- Name: therapist_payouts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.therapist_payouts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: therapist_payouts therapist_payouts_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payouts_self ON public.therapist_payouts USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: therapist_payouts therapist_payouts_tenant_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payouts_tenant_admin ON public.therapist_payouts FOR SELECT USING (((tenant_id IS NOT NULL) AND public.is_tenant_admin(tenant_id))); + + +-- +-- Name: tenant_members tm_select_admin_all_members; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tm_select_admin_all_members ON public.tenant_members FOR SELECT TO authenticated USING (public.is_tenant_admin(tenant_id)); + + +-- +-- Name: tenant_members tm_select_own_membership; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tm_select_own_membership ON public.tenant_members FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: twilio_subaccount_usage; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.twilio_subaccount_usage ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_bloqueios update own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "update own" ON public.agenda_bloqueios FOR UPDATE USING ((owner_id = auth.uid())); + + +-- +-- Name: user_settings; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.user_settings ENABLE ROW LEVEL SECURITY; + +-- +-- Name: user_settings user_settings_insert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY user_settings_insert_own ON public.user_settings FOR INSERT WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: user_settings user_settings_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY user_settings_select_own ON public.user_settings FOR SELECT USING ((user_id = auth.uid())); + + +-- +-- Name: user_settings user_settings_update_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY user_settings_update_own ON public.user_settings FOR UPDATE USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: saas_docs users_read_usuario_docs; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY users_read_usuario_docs ON public.saas_docs FOR SELECT TO authenticated USING (((ativo = true) AND (tipo_acesso = 'usuario'::text))); + + +-- +-- Name: saas_doc_votos votos_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY votos_select_own ON public.saas_doc_votos FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: saas_doc_votos votos_upsert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY votos_upsert_own ON public.saas_doc_votos TO authenticated USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: messages; Type: ROW SECURITY; Schema: realtime; Owner: - +-- + +ALTER TABLE realtime.messages ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects Allow authenticated updates; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "Allow authenticated updates" ON storage.objects FOR UPDATE TO authenticated USING ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); + + +-- +-- Name: objects Allow authenticated uploads; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "Allow authenticated uploads" ON storage.objects FOR INSERT TO authenticated WITH CHECK ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); + + +-- +-- Name: objects Public read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "Public read" ON storage.objects FOR SELECT USING ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); + + +-- +-- Name: objects agendador_storage_owner_delete; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_owner_delete ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects agendador_storage_owner_insert; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_owner_insert ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects agendador_storage_owner_update; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_owner_update ON storage.objects FOR UPDATE TO authenticated USING (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects agendador_storage_public_read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_public_read ON storage.objects FOR SELECT USING ((bucket_id = 'agendador'::text)); + + +-- +-- Name: objects avatars authenticated upload; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars authenticated upload" ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects avatars owner delete; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars owner delete" ON storage.objects FOR DELETE USING (((bucket_id = 'avatars'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects avatars owner update; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars owner update" ON storage.objects FOR UPDATE USING (((bucket_id = 'avatars'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects avatars public read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars public read" ON storage.objects FOR SELECT USING ((bucket_id = 'avatars'::text)); + + +-- +-- Name: objects avatars_delete_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_delete_own ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_delete_own_folder; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_delete_own_folder ON storage.objects FOR DELETE USING (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); + + +-- +-- Name: objects avatars_insert_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_insert_own ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_insert_own_folder; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_insert_own_folder ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); + + +-- +-- Name: objects avatars_read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_read ON storage.objects FOR SELECT USING ((bucket_id = 'avatars'::text)); + + +-- +-- Name: objects avatars_select_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_select_own ON storage.objects FOR SELECT TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_update_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_update_own ON storage.objects FOR UPDATE TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))) WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_update_own_folder; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_update_own_folder ON storage.objects FOR UPDATE USING (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))) WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); + + +-- +-- Name: buckets; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.buckets ENABLE ROW LEVEL SECURITY; + +-- +-- Name: buckets_analytics; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.buckets_analytics ENABLE ROW LEVEL SECURITY; + +-- +-- Name: buckets_vectors; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.buckets_vectors ENABLE ROW LEVEL SECURITY; + +-- +-- Name: iceberg_namespaces; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.iceberg_namespaces ENABLE ROW LEVEL SECURITY; + +-- +-- Name: iceberg_tables; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.iceberg_tables ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects intake_read_anon; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_read_anon ON storage.objects FOR SELECT TO anon USING (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: objects intake_read_public; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_read_public ON storage.objects FOR SELECT USING (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: objects intake_upload_anon; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_upload_anon ON storage.objects FOR INSERT TO anon WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: objects intake_upload_public; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_upload_public ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: migrations; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.migrations ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects public_read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY public_read ON storage.objects FOR SELECT USING ((bucket_id = 'saas-docs'::text)); + + +-- +-- Name: s3_multipart_uploads; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.s3_multipart_uploads ENABLE ROW LEVEL SECURITY; + +-- +-- Name: s3_multipart_uploads_parts; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.s3_multipart_uploads_parts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects saas_admin_delete; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY saas_admin_delete ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'saas-docs'::text) AND (EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid()))))); + + +-- +-- Name: objects saas_admin_upload; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY saas_admin_upload ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'saas-docs'::text) AND (EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid()))))); + + +-- +-- Name: vector_indexes; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.vector_indexes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: supabase_realtime; Type: PUBLICATION; Schema: -; Owner: - +-- + +CREATE PUBLICATION supabase_realtime WITH (publish = 'insert, update, delete, truncate'); + + +-- +-- Name: supabase_realtime_messages_publication; Type: PUBLICATION; Schema: -; Owner: - +-- + +CREATE PUBLICATION supabase_realtime_messages_publication WITH (publish = 'insert, update, delete, truncate'); + + +-- +-- Name: supabase_realtime notifications; Type: PUBLICATION TABLE; Schema: public; Owner: - +-- + +ALTER PUBLICATION supabase_realtime ADD TABLE ONLY public.notifications; + + +-- +-- Name: supabase_realtime_messages_publication messages; Type: PUBLICATION TABLE; Schema: realtime; Owner: - +-- + +ALTER PUBLICATION supabase_realtime_messages_publication ADD TABLE ONLY realtime.messages; + + +-- +-- Name: issue_graphql_placeholder; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_graphql_placeholder ON sql_drop + WHEN TAG IN ('DROP EXTENSION') + EXECUTE FUNCTION extensions.set_graphql_placeholder(); + + +-- +-- Name: issue_pg_cron_access; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_pg_cron_access ON ddl_command_end + WHEN TAG IN ('CREATE EXTENSION') + EXECUTE FUNCTION extensions.grant_pg_cron_access(); + + +-- +-- Name: issue_pg_graphql_access; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_pg_graphql_access ON ddl_command_end + WHEN TAG IN ('CREATE FUNCTION') + EXECUTE FUNCTION extensions.grant_pg_graphql_access(); + + +-- +-- Name: issue_pg_net_access; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_pg_net_access ON ddl_command_end + WHEN TAG IN ('CREATE EXTENSION') + EXECUTE FUNCTION extensions.grant_pg_net_access(); + + +-- +-- Name: pgrst_ddl_watch; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER pgrst_ddl_watch ON ddl_command_end + EXECUTE FUNCTION extensions.pgrst_ddl_watch(); + + +-- +-- Name: pgrst_drop_watch; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER pgrst_drop_watch ON sql_drop + EXECUTE FUNCTION extensions.pgrst_drop_watch(); + + +-- +-- PostgreSQL database dump complete +-- + +\unrestrict sHssDG8nRcgy91i34c0GgSqKTkbvMenWmDdSa1Uuu0ZRvDQMFU2yjZxjPLTMS1x + diff --git a/database-novo/backups/2026-03-29/schema.sql b/database-novo/backups/2026-03-29/schema.sql new file mode 100644 index 0000000..0b3d319 --- /dev/null +++ b/database-novo/backups/2026-03-29/schema.sql @@ -0,0 +1,20279 @@ +-- +-- PostgreSQL database dump +-- + +\restrict svMjIKHsYwcTwAX1IxwEB8W23fUXSRnKpOVx0zBmyOtnoKw2CFaSxxP2G5YRkJY + +-- Dumped from database version 17.6 +-- Dumped by pg_dump version 17.6 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET transaction_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Name: _realtime; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA _realtime; + + +-- +-- Name: auth; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA auth; + + +-- +-- Name: pg_cron; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_cron WITH SCHEMA pg_catalog; + + +-- +-- Name: EXTENSION pg_cron; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_cron IS 'Job scheduler for PostgreSQL'; + + +-- +-- Name: extensions; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA extensions; + + +-- +-- Name: graphql; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA graphql; + + +-- +-- Name: graphql_public; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA graphql_public; + + +-- +-- Name: pg_net; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_net WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION pg_net; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_net IS 'Async HTTP'; + + +-- +-- Name: pgbouncer; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA pgbouncer; + + +-- +-- Name: realtime; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA realtime; + + +-- +-- Name: storage; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA storage; + + +-- +-- Name: supabase_functions; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA supabase_functions; + + +-- +-- Name: vault; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA vault; + + +-- +-- Name: btree_gist; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS btree_gist WITH SCHEMA public; + + +-- +-- Name: EXTENSION btree_gist; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION btree_gist IS 'support for indexing common datatypes in GiST'; + + +-- +-- Name: citext; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public; + + +-- +-- Name: EXTENSION citext; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION citext IS 'data type for case-insensitive character strings'; + + +-- +-- Name: pg_graphql; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_graphql WITH SCHEMA graphql; + + +-- +-- Name: EXTENSION pg_graphql; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_graphql IS 'pg_graphql: GraphQL support'; + + +-- +-- Name: pg_stat_statements; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_stat_statements WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION pg_stat_statements; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_stat_statements IS 'track planning and execution statistics of all SQL statements executed'; + + +-- +-- Name: pg_trgm; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA public; + + +-- +-- Name: EXTENSION pg_trgm; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pg_trgm IS 'text similarity measurement and index searching based on trigrams'; + + +-- +-- Name: pgcrypto; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION pgcrypto; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pgcrypto IS 'cryptographic functions'; + + +-- +-- Name: supabase_vault; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS supabase_vault WITH SCHEMA vault; + + +-- +-- Name: EXTENSION supabase_vault; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION supabase_vault IS 'Supabase Vault Extension'; + + +-- +-- Name: uuid-ossp; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA extensions; + + +-- +-- Name: EXTENSION "uuid-ossp"; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)'; + + +-- +-- Name: aal_level; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.aal_level AS ENUM ( + 'aal1', + 'aal2', + 'aal3' +); + + +-- +-- Name: code_challenge_method; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.code_challenge_method AS ENUM ( + 's256', + 'plain' +); + + +-- +-- Name: factor_status; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.factor_status AS ENUM ( + 'unverified', + 'verified' +); + + +-- +-- Name: factor_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.factor_type AS ENUM ( + 'totp', + 'webauthn', + 'phone' +); + + +-- +-- Name: oauth_authorization_status; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_authorization_status AS ENUM ( + 'pending', + 'approved', + 'denied', + 'expired' +); + + +-- +-- Name: oauth_client_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_client_type AS ENUM ( + 'public', + 'confidential' +); + + +-- +-- Name: oauth_registration_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_registration_type AS ENUM ( + 'dynamic', + 'manual' +); + + +-- +-- Name: oauth_response_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.oauth_response_type AS ENUM ( + 'code' +); + + +-- +-- Name: one_time_token_type; Type: TYPE; Schema: auth; Owner: - +-- + +CREATE TYPE auth.one_time_token_type AS ENUM ( + 'confirmation_token', + 'reauthentication_token', + 'recovery_token', + 'email_change_token_new', + 'email_change_token_current', + 'phone_change_token' +); + + +-- +-- Name: commitment_log_source; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.commitment_log_source AS ENUM ( + 'manual', + 'auto' +); + + +-- +-- Name: determined_field_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.determined_field_type AS ENUM ( + 'text', + 'textarea', + 'number', + 'date', + 'select', + 'boolean' +); + + +-- +-- Name: financial_record_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.financial_record_type AS ENUM ( + 'receita', + 'despesa' +); + + +-- +-- Name: recurrence_exception_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.recurrence_exception_type AS ENUM ( + 'cancel_session', + 'reschedule_session', + 'patient_missed', + 'therapist_canceled', + 'holiday_block' +); + + +-- +-- Name: recurrence_type; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.recurrence_type AS ENUM ( + 'weekly', + 'biweekly', + 'monthly', + 'yearly', + 'custom_weekdays' +); + + +-- +-- Name: status_agenda_serie; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.status_agenda_serie AS ENUM ( + 'ativo', + 'pausado', + 'cancelado' +); + + +-- +-- Name: status_evento_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.status_evento_agenda AS ENUM ( + 'agendado', + 'realizado', + 'faltou', + 'cancelado', + 'remarcar' +); + + +-- +-- Name: status_excecao_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.status_excecao_agenda AS ENUM ( + 'pendente', + 'ativo', + 'arquivado' +); + + +-- +-- Name: tipo_evento_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.tipo_evento_agenda AS ENUM ( + 'sessao', + 'bloqueio' +); + + +-- +-- Name: tipo_excecao_agenda; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.tipo_excecao_agenda AS ENUM ( + 'bloqueio', + 'horario_extra' +); + + +-- +-- Name: action; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.action AS ENUM ( + 'INSERT', + 'UPDATE', + 'DELETE', + 'TRUNCATE', + 'ERROR' +); + + +-- +-- Name: equality_op; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.equality_op AS ENUM ( + 'eq', + 'neq', + 'lt', + 'lte', + 'gt', + 'gte', + 'in' +); + + +-- +-- Name: user_defined_filter; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.user_defined_filter AS ( + column_name text, + op realtime.equality_op, + value text +); + + +-- +-- Name: wal_column; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.wal_column AS ( + name text, + type_name text, + type_oid oid, + value jsonb, + is_pkey boolean, + is_selectable boolean +); + + +-- +-- Name: wal_rls; Type: TYPE; Schema: realtime; Owner: - +-- + +CREATE TYPE realtime.wal_rls AS ( + wal jsonb, + is_rls_enabled boolean, + subscription_ids uuid[], + errors text[] +); + + +-- +-- Name: buckettype; Type: TYPE; Schema: storage; Owner: - +-- + +CREATE TYPE storage.buckettype AS ENUM ( + 'STANDARD', + 'ANALYTICS', + 'VECTOR' +); + + +-- +-- Name: email(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.email() RETURNS text + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim.email', true), ''), + (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'email') + )::text +$$; + + +-- +-- Name: FUNCTION email(); Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON FUNCTION auth.email() IS 'Deprecated. Use auth.jwt() -> ''email'' instead.'; + + +-- +-- Name: jwt(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.jwt() RETURNS jsonb + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim', true), ''), + nullif(current_setting('request.jwt.claims', true), '') + )::jsonb +$$; + + +-- +-- Name: role(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.role() RETURNS text + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim.role', true), ''), + (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'role') + )::text +$$; + + +-- +-- Name: FUNCTION role(); Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON FUNCTION auth.role() IS 'Deprecated. Use auth.jwt() -> ''role'' instead.'; + + +-- +-- Name: uid(); Type: FUNCTION; Schema: auth; Owner: - +-- + +CREATE FUNCTION auth.uid() RETURNS uuid + LANGUAGE sql STABLE + AS $$ + select + coalesce( + nullif(current_setting('request.jwt.claim.sub', true), ''), + (nullif(current_setting('request.jwt.claims', true), '')::jsonb ->> 'sub') + )::uuid +$$; + + +-- +-- Name: FUNCTION uid(); Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON FUNCTION auth.uid() IS 'Deprecated. Use auth.jwt() -> ''sub'' instead.'; + + +-- +-- Name: grant_pg_cron_access(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.grant_pg_cron_access() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT + FROM pg_event_trigger_ddl_commands() AS ev + JOIN pg_extension AS ext + ON ev.objid = ext.oid + WHERE ext.extname = 'pg_cron' + ) + THEN + grant usage on schema cron to postgres with grant option; + + alter default privileges in schema cron grant all on tables to postgres with grant option; + alter default privileges in schema cron grant all on functions to postgres with grant option; + alter default privileges in schema cron grant all on sequences to postgres with grant option; + + alter default privileges for user supabase_admin in schema cron grant all + on sequences to postgres with grant option; + alter default privileges for user supabase_admin in schema cron grant all + on tables to postgres with grant option; + alter default privileges for user supabase_admin in schema cron grant all + on functions to postgres with grant option; + + grant all privileges on all tables in schema cron to postgres with grant option; + revoke all on table cron.job from postgres; + grant select on table cron.job to postgres with grant option; + END IF; +END; +$$; + + +-- +-- Name: FUNCTION grant_pg_cron_access(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.grant_pg_cron_access() IS 'Grants access to pg_cron'; + + +-- +-- Name: grant_pg_graphql_access(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.grant_pg_graphql_access() RETURNS event_trigger + LANGUAGE plpgsql + AS $_$ +DECLARE + func_is_graphql_resolve bool; +BEGIN + func_is_graphql_resolve = ( + SELECT n.proname = 'resolve' + FROM pg_event_trigger_ddl_commands() AS ev + LEFT JOIN pg_catalog.pg_proc AS n + ON ev.objid = n.oid + ); + + IF func_is_graphql_resolve + THEN + -- Update public wrapper to pass all arguments through to the pg_graphql resolve func + DROP FUNCTION IF EXISTS graphql_public.graphql; + create or replace function graphql_public.graphql( + "operationName" text default null, + query text default null, + variables jsonb default null, + extensions jsonb default null + ) + returns jsonb + language sql + as $$ + select graphql.resolve( + query := query, + variables := coalesce(variables, '{}'), + "operationName" := "operationName", + extensions := extensions + ); + $$; + + -- This hook executes when `graphql.resolve` is created. That is not necessarily the last + -- function in the extension so we need to grant permissions on existing entities AND + -- update default permissions to any others that are created after `graphql.resolve` + grant usage on schema graphql to postgres, anon, authenticated, service_role; + grant select on all tables in schema graphql to postgres, anon, authenticated, service_role; + grant execute on all functions in schema graphql to postgres, anon, authenticated, service_role; + grant all on all sequences in schema graphql to postgres, anon, authenticated, service_role; + alter default privileges in schema graphql grant all on tables to postgres, anon, authenticated, service_role; + alter default privileges in schema graphql grant all on functions to postgres, anon, authenticated, service_role; + alter default privileges in schema graphql grant all on sequences to postgres, anon, authenticated, service_role; + + -- Allow postgres role to allow granting usage on graphql and graphql_public schemas to custom roles + grant usage on schema graphql_public to postgres with grant option; + grant usage on schema graphql to postgres with grant option; + END IF; + +END; +$_$; + + +-- +-- Name: FUNCTION grant_pg_graphql_access(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.grant_pg_graphql_access() IS 'Grants access to pg_graphql'; + + +-- +-- Name: grant_pg_net_access(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.grant_pg_net_access() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_event_trigger_ddl_commands() AS ev + JOIN pg_extension AS ext + ON ev.objid = ext.oid + WHERE ext.extname = 'pg_net' + ) + THEN + GRANT USAGE ON SCHEMA net TO supabase_functions_admin, postgres, anon, authenticated, service_role; + + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SECURITY DEFINER; + + ALTER function net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + ALTER function net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) SET search_path = net; + + REVOKE ALL ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + REVOKE ALL ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) FROM PUBLIC; + + GRANT EXECUTE ON FUNCTION net.http_get(url text, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + GRANT EXECUTE ON FUNCTION net.http_post(url text, body jsonb, params jsonb, headers jsonb, timeout_milliseconds integer) TO supabase_functions_admin, postgres, anon, authenticated, service_role; + END IF; +END; +$$; + + +-- +-- Name: FUNCTION grant_pg_net_access(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.grant_pg_net_access() IS 'Grants access to pg_net'; + + +-- +-- Name: pgrst_ddl_watch(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.pgrst_ddl_watch() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +DECLARE + cmd record; +BEGIN + FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands() + LOOP + IF cmd.command_tag IN ( + 'CREATE SCHEMA', 'ALTER SCHEMA' + , 'CREATE TABLE', 'CREATE TABLE AS', 'SELECT INTO', 'ALTER TABLE' + , 'CREATE FOREIGN TABLE', 'ALTER FOREIGN TABLE' + , 'CREATE VIEW', 'ALTER VIEW' + , 'CREATE MATERIALIZED VIEW', 'ALTER MATERIALIZED VIEW' + , 'CREATE FUNCTION', 'ALTER FUNCTION' + , 'CREATE TRIGGER' + , 'CREATE TYPE', 'ALTER TYPE' + , 'CREATE RULE' + , 'COMMENT' + ) + -- don't notify in case of CREATE TEMP table or other objects created on pg_temp + AND cmd.schema_name is distinct from 'pg_temp' + THEN + NOTIFY pgrst, 'reload schema'; + END IF; + END LOOP; +END; $$; + + +-- +-- Name: pgrst_drop_watch(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.pgrst_drop_watch() RETURNS event_trigger + LANGUAGE plpgsql + AS $$ +DECLARE + obj record; +BEGIN + FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() + LOOP + IF obj.object_type IN ( + 'schema' + , 'table' + , 'foreign table' + , 'view' + , 'materialized view' + , 'function' + , 'trigger' + , 'type' + , 'rule' + ) + AND obj.is_temporary IS false -- no pg_temp objects + THEN + NOTIFY pgrst, 'reload schema'; + END IF; + END LOOP; +END; $$; + + +-- +-- Name: set_graphql_placeholder(); Type: FUNCTION; Schema: extensions; Owner: - +-- + +CREATE FUNCTION extensions.set_graphql_placeholder() RETURNS event_trigger + LANGUAGE plpgsql + AS $_$ + DECLARE + graphql_is_dropped bool; + BEGIN + graphql_is_dropped = ( + SELECT ev.schema_name = 'graphql_public' + FROM pg_event_trigger_dropped_objects() AS ev + WHERE ev.schema_name = 'graphql_public' + ); + + IF graphql_is_dropped + THEN + create or replace function graphql_public.graphql( + "operationName" text default null, + query text default null, + variables jsonb default null, + extensions jsonb default null + ) + returns jsonb + language plpgsql + as $$ + DECLARE + server_version float; + BEGIN + server_version = (SELECT (SPLIT_PART((select version()), ' ', 2))::float); + + IF server_version >= 14 THEN + RETURN jsonb_build_object( + 'errors', jsonb_build_array( + jsonb_build_object( + 'message', 'pg_graphql extension is not enabled.' + ) + ) + ); + ELSE + RETURN jsonb_build_object( + 'errors', jsonb_build_array( + jsonb_build_object( + 'message', 'pg_graphql is only available on projects running Postgres 14 onwards.' + ) + ) + ); + END IF; + END; + $$; + END IF; + + END; +$_$; + + +-- +-- Name: FUNCTION set_graphql_placeholder(); Type: COMMENT; Schema: extensions; Owner: - +-- + +COMMENT ON FUNCTION extensions.set_graphql_placeholder() IS 'Reintroduces placeholder function for graphql_public.graphql'; + + +-- +-- Name: get_auth(text); Type: FUNCTION; Schema: pgbouncer; Owner: - +-- + +CREATE FUNCTION pgbouncer.get_auth(p_usename text) RETURNS TABLE(username text, password text) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO '' + AS $_$ +begin + raise debug 'PgBouncer auth request: %', p_usename; + + return query + select + rolname::text, + case when rolvaliduntil < now() + then null + else rolpassword::text + end + from pg_authid + where rolname=$1 and rolcanlogin; +end; +$_$; + + +-- +-- Name: __rls_ping(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.__rls_ping() RETURNS text + LANGUAGE sql STABLE + AS $$ + select 'ok'::text; +$$; + + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: subscriptions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscriptions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid, + plan_id uuid NOT NULL, + status text DEFAULT 'active'::text NOT NULL, + current_period_start timestamp with time zone, + current_period_end timestamp with time zone, + cancel_at_period_end boolean DEFAULT false NOT NULL, + provider text DEFAULT 'manual'::text NOT NULL, + provider_customer_id text, + provider_subscription_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid, + plan_key text, + "interval" text, + source text DEFAULT 'manual'::text NOT NULL, + started_at timestamp with time zone DEFAULT now() NOT NULL, + canceled_at timestamp with time zone, + activated_at timestamp with time zone, + past_due_since timestamp with time zone, + suspended_at timestamp with time zone, + suspended_reason text, + cancelled_at timestamp with time zone, + cancel_reason text, + expired_at timestamp with time zone, + CONSTRAINT subscriptions_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text]))), + CONSTRAINT subscriptions_owner_xor CHECK ((((tenant_id IS NOT NULL) AND (user_id IS NULL)) OR ((tenant_id IS NULL) AND (user_id IS NOT NULL)))), + CONSTRAINT subscriptions_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'active'::text, 'past_due'::text, 'suspended'::text, 'cancelled'::text, 'expired'::text]))) +); + + +-- +-- Name: activate_subscription_from_intent(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.activate_subscription_from_intent(p_intent_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_intent record; + v_sub public.subscriptions; + v_days int; + v_user_id uuid; + v_plan_id uuid; + v_target text; +begin + -- l?? pela VIEW unificada + select * into v_intent + from public.subscription_intents + where id = p_intent_id; + + if not found then + raise exception 'Intent n??o encontrado: %', p_intent_id; + end if; + + if v_intent.status <> 'paid' then + raise exception 'Intent precisa estar paid para ativar assinatura'; + end if; + + -- resolve target e plan_id via plans.key + select p.id, p.target + into v_plan_id, v_target + from public.plans p + where p.key = v_intent.plan_key + limit 1; + + if v_plan_id is null then + raise exception 'Plano n??o encontrado em plans.key = %', v_intent.plan_key; + end if; + + v_target := lower(coalesce(v_target, '')); + + -- ??? supervisor adicionado + if v_target not in ('clinic', 'therapist', 'supervisor') then + raise exception 'Target inv??lido em plans.target: %', v_target; + end if; + + -- regra por target + if v_target = 'clinic' then + if v_intent.tenant_id is null then + raise exception 'Intent sem tenant_id'; + end if; + else + -- therapist ou supervisor: vinculado ao user + v_user_id := v_intent.user_id; + if v_user_id is null then + v_user_id := v_intent.created_by_user_id; + end if; + end if; + + if v_target in ('therapist', 'supervisor') and v_user_id is null then + raise exception 'N??o foi poss??vel determinar user_id para assinatura %.', v_target; + end if; + + -- cancela assinatura ativa anterior + if v_target = 'clinic' then + update public.subscriptions + set status = 'cancelled', + cancelled_at = now() + where tenant_id = v_intent.tenant_id + and plan_id = v_plan_id + and status = 'active'; + else + -- therapist ou supervisor + update public.subscriptions + set status = 'cancelled', + cancelled_at = now() + where user_id = v_user_id + and plan_id = v_plan_id + and status = 'active' + and tenant_id is null; + end if; + + -- dura????o do plano (30 dias para mensal) + v_days := case + when lower(coalesce(v_intent.interval, 'month')) = 'year' then 365 + else 30 + end; + + -- cria nova assinatura + insert into public.subscriptions ( + user_id, + plan_id, + status, + started_at, + expires_at, + cancelled_at, + activated_at, + tenant_id, + plan_key, + interval, + source, + created_at, + updated_at + ) + values ( + case when v_target = 'clinic' then null else v_user_id end, + v_plan_id, + 'active', + now(), + now() + make_interval(days => v_days), + null, + now(), + case when v_target = 'clinic' then v_intent.tenant_id else null end, + v_intent.plan_key, + v_intent.interval, + 'manual', + now(), + now() + ) + returning * into v_sub; + + -- grava v??nculo intent ??? subscription + if v_target = 'clinic' then + update public.subscription_intents_tenant + set subscription_id = v_sub.id + where id = p_intent_id; + else + update public.subscription_intents_personal + set subscription_id = v_sub.id + where id = p_intent_id; + end if; + + return v_sub; +end; +$$; + + +-- +-- Name: admin_credit_addon(uuid, text, integer, uuid, text, text, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Cr??dito manual'::text, p_payment_method text DEFAULT 'manual'::text, p_price_cents integer DEFAULT 0) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_credit addon_credits%ROWTYPE; + v_balance_before INTEGER; + v_balance_after INTEGER; + v_tx_id UUID; +BEGIN + -- Upsert addon_credits + INSERT INTO addon_credits (tenant_id, addon_type, balance, total_purchased) + VALUES (p_tenant_id, p_addon_type, 0, 0) + ON CONFLICT (tenant_id, addon_type) DO NOTHING; + + -- Lock e leitura + SELECT * INTO v_credit + FROM addon_credits + WHERE tenant_id = p_tenant_id AND addon_type = p_addon_type + FOR UPDATE; + + v_balance_before := v_credit.balance; + v_balance_after := v_credit.balance + p_amount; + + -- Atualiza saldo + UPDATE addon_credits + SET balance = v_balance_after, + total_purchased = total_purchased + p_amount, + low_balance_notified = CASE WHEN v_balance_after > COALESCE(low_balance_threshold, 10) THEN false ELSE low_balance_notified END, + updated_at = now() + WHERE id = v_credit.id; + + -- Registra transa????o + INSERT INTO addon_transactions ( + tenant_id, addon_type, type, amount, + balance_before, balance_after, + product_id, description, + admin_user_id, payment_method, price_cents + ) VALUES ( + p_tenant_id, p_addon_type, 'purchase', p_amount, + v_balance_before, v_balance_after, + p_product_id, p_description, + auth.uid(), p_payment_method, p_price_cents + ) + RETURNING id INTO v_tx_id; + + RETURN jsonb_build_object( + 'success', true, + 'transaction_id', v_tx_id, + 'balance_before', v_balance_before, + 'balance_after', v_balance_after + ); +END; +$$; + + +-- +-- Name: FUNCTION admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.admin_credit_addon(p_tenant_id uuid, p_addon_type text, p_amount integer, p_product_id uuid, p_description text, p_payment_method text, p_price_cents integer) IS 'Admin adiciona cr??ditos de add-on a um tenant. Cria registro se n??o existir.'; + + +-- +-- Name: admin_delete_email_template_global(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_delete_email_template_global(p_id uuid) RETURNS boolean + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + DELETE FROM public.email_templates_global WHERE id = p_id; + IF NOT FOUND THEN + RAISE EXCEPTION 'Template com id % n??o encontrado', p_id; + END IF; + RETURN true; +END; +$$; + + +-- +-- Name: admin_fix_plan_target(text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_fix_plan_target(p_plan_key text, p_new_target text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_plan_id uuid; +begin + -- (opcional) restringe targets v??lidos + if p_new_target not in ('clinic','therapist') then + raise exception 'Target inv??lido: %', p_new_target using errcode='P0001'; + end if; + + -- trava o plano + select id into v_plan_id + from public.plans + where key = p_plan_key + for update; + + if v_plan_id is null then + raise exception 'Plano n??o encontrado: %', p_plan_key using errcode='P0001'; + end if; + + -- seguran??a: n??o mexer se existe subscription + if exists (select 1 from public.subscriptions s where s.plan_id = v_plan_id) then + raise exception 'Plano % possui subscriptions. Migra????o bloqueada.', p_plan_key using errcode='P0001'; + end if; + + -- liga bypass SOMENTE nesta transa????o + perform set_config('app.plan_migration_bypass', '1', true); + + update public.plans + set target = p_new_target + where id = v_plan_id; + +end +$$; + + +-- +-- Name: admin_upsert_email_template_global(uuid, text, text, text, text, text, text, boolean, jsonb); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.admin_upsert_email_template_global(p_id uuid DEFAULT NULL::uuid, p_key text DEFAULT NULL::text, p_domain text DEFAULT NULL::text, p_channel text DEFAULT 'email'::text, p_subject text DEFAULT NULL::text, p_body_html text DEFAULT NULL::text, p_body_text text DEFAULT NULL::text, p_is_active boolean DEFAULT true, p_variables jsonb DEFAULT '{}'::jsonb) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_result jsonb; + v_id uuid; +BEGIN + -- UPDATE existente + IF p_id IS NOT NULL THEN + UPDATE public.email_templates_global + SET + subject = COALESCE(p_subject, subject), + body_html = COALESCE(p_body_html, body_html), + body_text = p_body_text, + is_active = p_is_active, + variables = COALESCE(p_variables, variables), + version = version + 1 + WHERE id = p_id + RETURNING to_jsonb(email_templates_global.*) INTO v_result; + + IF v_result IS NULL THEN + RAISE EXCEPTION 'Template com id % n??o encontrado', p_id; + END IF; + + RETURN v_result; + END IF; + + -- INSERT novo + IF p_key IS NULL OR p_domain IS NULL OR p_subject IS NULL OR p_body_html IS NULL THEN + RAISE EXCEPTION 'key, domain, subject e body_html s??o obrigat??rios para novo template'; + END IF; + + INSERT INTO public.email_templates_global (key, domain, channel, subject, body_html, body_text, is_active, variables) + VALUES (p_key, p_domain, p_channel, p_subject, p_body_html, p_body_text, p_is_active, p_variables) + RETURNING to_jsonb(email_templates_global.*) INTO v_result; + + RETURN v_result; +END; +$$; + + +-- +-- Name: agenda_cfg_sync(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agenda_cfg_sync() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if new.agenda_view_mode = 'custom' then + new.usar_horario_admin_custom := true; + new.admin_inicio_visualizacao := new.agenda_custom_start; + new.admin_fim_visualizacao := new.agenda_custom_end; + else + new.usar_horario_admin_custom := false; + end if; + + return new; +end; +$$; + + +-- +-- Name: agendador_dias_disponiveis(text, integer, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agendador_dias_disponiveis(p_slug text, p_ano integer, p_mes integer) RETURNS TABLE(data date, tem_slots boolean) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_owner_id uuid; + v_antecedencia int; + v_agora timestamptz; + v_data date; + v_data_inicio date; + v_data_fim date; + v_db_dow int; + v_tem_slot boolean; + v_bloqueado boolean; +BEGIN + SELECT c.owner_id, c.antecedencia_minima_horas + INTO v_owner_id, v_antecedencia + FROM public.agendador_configuracoes c + WHERE c.link_slug = p_slug AND c.ativo = true + LIMIT 1; + + IF v_owner_id IS NULL THEN RETURN; END IF; + + v_agora := now(); + v_data_inicio := make_date(p_ano, p_mes, 1); + v_data_fim := (v_data_inicio + interval '1 month' - interval '1 day')::date; + + v_data := v_data_inicio; + WHILE v_data <= v_data_fim LOOP + v_db_dow := extract(dow from v_data::timestamp)::int; + + -- ?????? Dia inteiro bloqueado? (agenda_bloqueios) ??????????????????????????????????????????????????????????????????????????? + SELECT EXISTS ( + SELECT 1 FROM public.agenda_bloqueios b + WHERE b.owner_id = v_owner_id + AND b.data_inicio <= v_data + AND COALESCE(b.data_fim, v_data) >= v_data + AND b.hora_inicio IS NULL -- bloqueio de dia inteiro + AND ( + (NOT b.recorrente) + OR (b.recorrente AND b.dia_semana = v_db_dow) + ) + ) INTO v_bloqueado; + + IF v_bloqueado THEN + v_data := v_data + 1; + CONTINUE; + END IF; + + -- ?????? Tem slots dispon??veis no dia? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????? + SELECT EXISTS ( + SELECT 1 FROM public.agenda_online_slots s + WHERE s.owner_id = v_owner_id + AND s.weekday = v_db_dow + AND s.enabled = true + AND (v_data::text || ' ' || s.time::text)::timestamp + AT TIME ZONE 'America/Sao_Paulo' + >= v_agora + (v_antecedencia || ' hours')::interval + ) INTO v_tem_slot; + + IF v_tem_slot THEN + data := v_data; + tem_slots := true; + RETURN NEXT; + END IF; + + v_data := v_data + 1; + END LOOP; +END; +$$; + + +-- +-- Name: agendador_gerar_slug(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agendador_gerar_slug() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_slug text; + v_exists boolean; +BEGIN + -- s?? gera se ativou e n??o tem slug ainda + IF NEW.ativo = true AND (NEW.link_slug IS NULL OR NEW.link_slug = '') THEN + LOOP + v_slug := lower(substring(replace(gen_random_uuid()::text, '-', ''), 1, 8)); + SELECT EXISTS ( + SELECT 1 FROM public.agendador_configuracoes + WHERE link_slug = v_slug AND owner_id <> NEW.owner_id + ) INTO v_exists; + EXIT WHEN NOT v_exists; + END LOOP; + NEW.link_slug := v_slug; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: agendador_slots_disponiveis(text, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.agendador_slots_disponiveis(p_slug text, p_data date) RETURNS TABLE(hora time without time zone, disponivel boolean) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_owner_id uuid; + v_duracao int; + v_antecedencia int; + v_agora timestamptz; + v_db_dow int; + v_slot time; + v_slot_fim time; + v_slot_ts timestamptz; + v_ocupado boolean; + -- loop de recorr??ncias + v_rule RECORD; + v_rule_start_dow int; + v_first_occ date; + v_day_diff int; + v_ex_type text; +BEGIN + SELECT c.owner_id, c.duracao_sessao_min, c.antecedencia_minima_horas + INTO v_owner_id, v_duracao, v_antecedencia + FROM public.agendador_configuracoes c + WHERE c.link_slug = p_slug AND c.ativo = true + LIMIT 1; + + IF v_owner_id IS NULL THEN RETURN; END IF; + + v_agora := now(); + v_db_dow := extract(dow from p_data::timestamp)::int; + + -- ?????? Dia inteiro bloqueado? (agenda_bloqueios sem hora) ????????????????????????????????????????????????????????? + -- Se sim, n??o h?? nenhum slot dispon??vel ??? retorna vazio. + IF EXISTS ( + SELECT 1 FROM public.agenda_bloqueios b + WHERE b.owner_id = v_owner_id + AND b.data_inicio <= p_data + AND COALESCE(b.data_fim, p_data) >= p_data + AND b.hora_inicio IS NULL -- bloqueio de dia inteiro + AND ( + (NOT b.recorrente) + OR (b.recorrente AND b.dia_semana = v_db_dow) + ) + ) THEN + RETURN; + END IF; + + FOR v_slot IN + SELECT s.time + FROM public.agenda_online_slots s + WHERE s.owner_id = v_owner_id + AND s.weekday = v_db_dow + AND s.enabled = true + ORDER BY s.time + LOOP + v_slot_fim := v_slot + (v_duracao || ' minutes')::interval; + v_ocupado := false; + + -- ?????? Anteced??ncia m??nima ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + v_slot_ts := (p_data::text || ' ' || v_slot::text)::timestamp + AT TIME ZONE 'America/Sao_Paulo'; + IF v_slot_ts < v_agora + (v_antecedencia || ' hours')::interval THEN + v_ocupado := true; + END IF; + + -- ?????? Bloqueio de hor??rio espec??fico (agenda_bloqueios com hora) ????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 FROM public.agenda_bloqueios b + WHERE b.owner_id = v_owner_id + AND b.data_inicio <= p_data + AND COALESCE(b.data_fim, p_data) >= p_data + AND b.hora_inicio IS NOT NULL + AND b.hora_inicio < v_slot_fim + AND b.hora_fim > v_slot + AND ( + (NOT b.recorrente) + OR (b.recorrente AND b.dia_semana = v_db_dow) + ) + ) INTO v_ocupado; + END IF; + + -- ?????? Eventos avulsos internos (agenda_eventos) ???????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 FROM public.agenda_eventos e + WHERE e.owner_id = v_owner_id + AND e.status::text NOT IN ('cancelado', 'faltou') + AND (e.inicio_em AT TIME ZONE 'America/Sao_Paulo')::date = p_data + AND (e.inicio_em AT TIME ZONE 'America/Sao_Paulo')::time < v_slot_fim + AND (e.fim_em AT TIME ZONE 'America/Sao_Paulo')::time > v_slot + ) INTO v_ocupado; + END IF; + + -- ?????? Recorr??ncias ativas (recurrence_rules) ????????????????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + FOR v_rule IN + SELECT + r.id, + r.start_date::date AS start_date, + r.end_date::date AS end_date, + r.start_time::time AS start_time, + r.end_time::time AS end_time, + COALESCE(r.interval, 1)::int AS interval + FROM public.recurrence_rules r + WHERE r.owner_id = v_owner_id + AND r.status = 'ativo' + AND p_data >= r.start_date::date + AND (r.end_date IS NULL OR p_data <= r.end_date::date) + AND v_db_dow = ANY(r.weekdays) + AND r.start_time::time < v_slot_fim + AND r.end_time::time > v_slot + LOOP + v_rule_start_dow := extract(dow from v_rule.start_date)::int; + v_first_occ := v_rule.start_date + + (((v_db_dow - v_rule_start_dow + 7) % 7))::int; + v_day_diff := (p_data - v_first_occ)::int; + + IF v_day_diff >= 0 AND v_day_diff % (7 * v_rule.interval) = 0 THEN + v_ex_type := NULL; + SELECT ex.type INTO v_ex_type + FROM public.recurrence_exceptions ex + WHERE ex.recurrence_id = v_rule.id + AND ex.original_date = p_data + LIMIT 1; + + IF v_ex_type IS NULL OR v_ex_type NOT IN ( + 'cancel_session', 'patient_missed', + 'therapist_canceled', 'holiday_block', + 'reschedule_session' + ) THEN + v_ocupado := true; + EXIT; + END IF; + END IF; + END LOOP; + END IF; + + -- ?????? Recorr??ncias remarcadas para este dia ???????????????????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 + FROM public.recurrence_exceptions ex + JOIN public.recurrence_rules r ON r.id = ex.recurrence_id + WHERE r.owner_id = v_owner_id + AND r.status = 'ativo' + AND ex.type = 'reschedule_session' + AND ex.new_date = p_data + AND COALESCE(ex.new_start_time, r.start_time)::time < v_slot_fim + AND COALESCE(ex.new_end_time, r.end_time)::time > v_slot + ) INTO v_ocupado; + END IF; + + -- ?????? Solicita????es p??blicas pendentes ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + IF NOT v_ocupado THEN + SELECT EXISTS ( + SELECT 1 FROM public.agendador_solicitacoes sol + WHERE sol.owner_id = v_owner_id + AND sol.status = 'pendente' + AND sol.data_solicitada = p_data + AND sol.hora_solicitada = v_slot + AND (sol.reservado_ate IS NULL OR sol.reservado_ate > v_agora) + ) INTO v_ocupado; + END IF; + + hora := v_slot; + disponivel := NOT v_ocupado; + RETURN NEXT; + END LOOP; +END; +$$; + + +-- +-- Name: auto_create_financial_record_from_session(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.auto_create_financial_record_from_session() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_price NUMERIC(10,2); + v_services_total NUMERIC(10,2); + v_already_billed BOOLEAN; +BEGIN + -- ?????? Guards de sa??da r??pida ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + + -- S?? processa quando o status muda PARA 'realizado' + IF NEW.status::TEXT <> 'realizado' THEN + RETURN NEW; + END IF; + + -- S?? processa quando houve mudan??a real de status + IF OLD.status IS NOT DISTINCT FROM NEW.status THEN + RETURN NEW; + END IF; + + -- S?? sess??es (n??o bloqueios, feriados, etc.) + IF NEW.tipo::TEXT <> 'sessao' THEN + RETURN NEW; + END IF; + + -- Paciente obrigat??rio para vincular a cobran??a + IF NEW.patient_id IS NULL THEN + RETURN NEW; + END IF; + + -- Sess??es de pacote t??m cobran??a gerenciada por billing_contract + IF NEW.billing_contract_id IS NOT NULL THEN + RETURN NEW; + END IF; + + -- Idempot??ncia: j?? existe financial_record para este evento? + SELECT billed INTO v_already_billed + FROM public.agenda_eventos + WHERE id = NEW.id; + + IF v_already_billed = TRUE THEN + -- Confirma no financial_records tamb??m (dupla verifica????o) + IF EXISTS ( + SELECT 1 FROM public.financial_records + WHERE agenda_evento_id = NEW.id AND deleted_at IS NULL + ) THEN + RETURN NEW; + END IF; + END IF; + + -- ?????? Busca do pre??o ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + + v_price := NULL; + + -- Prioridade 1: soma dos servi??os da regra de recorr??ncia + IF NEW.recurrence_id IS NOT NULL THEN + SELECT COALESCE(SUM(rrs.final_price), 0) + INTO v_services_total + FROM public.recurrence_rule_services rrs + WHERE rrs.rule_id = NEW.recurrence_id; + + IF v_services_total > 0 THEN + v_price := v_services_total; + END IF; + + -- Prioridade 2: price direto da regra (fallback se sem servi??os) + IF v_price IS NULL OR v_price = 0 THEN + SELECT price INTO v_price + FROM public.recurrence_rules + WHERE id = NEW.recurrence_id; + END IF; + END IF; + + -- Prioridade 3: price do pr??prio evento de agenda + IF v_price IS NULL OR v_price = 0 THEN + v_price := NEW.price; + END IF; + + -- Sem pre??o ??? n??o criar registro (n??o ?? erro, apenas skip silencioso) + IF v_price IS NULL OR v_price <= 0 THEN + RETURN NEW; + END IF; + + -- ?????? Cria????o do financial_record ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + + INSERT INTO public.financial_records ( + owner_id, + tenant_id, + patient_id, + agenda_evento_id, + type, + amount, + discount_amount, + final_amount, + clinic_fee_pct, + clinic_fee_amount, + status, + due_date + -- payment_method: NULL at?? o momento do pagamento (mark_as_paid preenche) + ) VALUES ( + NEW.owner_id, + NEW.tenant_id, + NEW.patient_id, + NEW.id, + 'receita', + v_price, + 0, + v_price, + 0, -- clinic_fee_pct: sem campo de configura????o global no schema atual. + 0, -- clinic_fee_amount: calculado manualmente ou via update posterior. + 'pending', + (NEW.inicio_em::DATE + 7) -- vencimento padr??o: 7 dias ap??s a sess??o + ); + + -- ?????? Marca sess??o como billed ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + -- UPDATE em billed (n??o em status) ??? n??o re-dispara este trigger + UPDATE public.agenda_eventos + SET billed = TRUE + WHERE id = NEW.id; + + RETURN NEW; + +EXCEPTION + WHEN OTHERS THEN + -- Log silencioso: nunca bloquear a agenda por falha financeira + RAISE WARNING '[auto_create_financial_record_from_session] evento=% erro=%', + NEW.id, SQLERRM; + RETURN NEW; +END; +$$; + + +-- +-- Name: FUNCTION auto_create_financial_record_from_session(); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.auto_create_financial_record_from_session() IS 'Trigger que cria automaticamente um financial_record (receita, pending) quando uma sess??o de agenda ?? marcada como realizada. Prioridade de pre??o: recurrence_rule_services > recurrence_rules.price > agenda_eventos.price. Skip silencioso se sem pre??o, pacote ou registro j?? existente.'; + + +-- +-- Name: can_delete_patient(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.can_delete_patient(p_patient_id uuid) RETURNS boolean + LANGUAGE sql STABLE SECURITY DEFINER + AS $$ + SELECT NOT EXISTS ( + SELECT 1 FROM public.agenda_eventos WHERE patient_id = p_patient_id + UNION ALL + SELECT 1 FROM public.recurrence_rules WHERE patient_id = p_patient_id + UNION ALL + SELECT 1 FROM public.billing_contracts WHERE patient_id = p_patient_id + ); +$$; + + +-- +-- Name: cancel_notifications_on_opt_out(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_notifications_on_opt_out() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + -- WhatsApp opt-out + IF OLD.whatsapp_opt_in = true AND NEW.whatsapp_opt_in = false THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, 'whatsapp' + ); + END IF; + -- Email opt-out + IF OLD.email_opt_in = true AND NEW.email_opt_in = false THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, 'email' + ); + END IF; + -- SMS opt-out + IF OLD.sms_opt_in = true AND NEW.sms_opt_in = false THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, 'sms' + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: cancel_notifications_on_session_cancel(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_notifications_on_session_cancel() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF NEW.status IN ('cancelado', 'excluido') + AND OLD.status NOT IN ('cancelado', 'excluido') + THEN + PERFORM public.cancel_patient_pending_notifications( + NEW.patient_id, NULL, NEW.id + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: cancel_patient_pending_notifications(uuid, text, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_patient_pending_notifications(p_patient_id uuid, p_channel text DEFAULT NULL::text, p_evento_id uuid DEFAULT NULL::uuid) RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_canceled integer; +BEGIN + UPDATE public.notification_queue + SET status = 'cancelado', + updated_at = now() + WHERE patient_id = p_patient_id + AND status IN ('pendente', 'processando') + AND (p_channel IS NULL OR channel = p_channel) + AND (p_evento_id IS NULL OR agenda_evento_id = p_evento_id); + + GET DIAGNOSTICS v_canceled = ROW_COUNT; + RETURN v_canceled; +END; +$$; + + +-- +-- Name: cancel_recurrence_from(uuid, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_recurrence_from(p_recurrence_id uuid, p_from_date date) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + UPDATE public.recurrence_rules + SET + end_date = p_from_date - INTERVAL '1 day', + open_ended = false, + status = CASE + WHEN p_from_date <= start_date THEN 'cancelado' + ELSE status + END, + updated_at = now() + WHERE id = p_recurrence_id; +END; +$$; + + +-- +-- Name: cancel_subscription(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancel_subscription(p_subscription_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_owner_type text; + v_owner_ref uuid; +begin + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + if v_sub.status = 'canceled' then + return v_sub; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set status = 'canceled', + cancel_at_period_end = false, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'canceled', + v_sub.plan_id, + v_sub.plan_id, + auth.uid(), + 'Cancelamento manual via admin', + 'admin_panel', + jsonb_build_object('previous_status', 'active') + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation(owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; + +end; +$$; + + +-- +-- Name: cancelar_eventos_serie(uuid, timestamp with time zone); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone DEFAULT now()) RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_count integer; +BEGIN + UPDATE public.agenda_eventos + SET status = 'cancelado', + updated_at = now() + WHERE serie_id = p_serie_id + AND inicio_em >= p_a_partir_de + AND status NOT IN ('realizado', 'cancelado'); + + GET DIAGNOSTICS v_count = ROW_COUNT; + RETURN v_count; +END; +$$; + + +-- +-- Name: FUNCTION cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.cancelar_eventos_serie(p_serie_id uuid, p_a_partir_de timestamp with time zone) IS 'Cancela todos os eventos futuros de uma s??rie a partir de p_a_partir_de (inclusive). + N??o cancela eventos j?? realizados.'; + + +-- +-- Name: change_subscription_plan(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.change_subscription_plan(p_subscription_id uuid, p_new_plan_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_old_plan uuid; + v_new_key text; + + v_owner_type text; + v_owner_ref uuid; + + v_new_target text; + v_sub_target text; +begin + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + v_old_plan := v_sub.plan_id; + + if v_old_plan = p_new_plan_id then + return v_sub; + end if; + + select key, target + into v_new_key, v_new_target + from public.plans + where id = p_new_plan_id; + + if v_new_key is null then + raise exception 'Plano n??o encontrado'; + end if; + + v_new_target := lower(coalesce(v_new_target, '')); + + v_sub_target := case + when v_sub.tenant_id is not null then 'clinic' + else 'therapist' + end; + + if v_new_target <> v_sub_target then + raise exception 'Plano inv??lido para este tipo de assinatura. Assinatura ?? % e o plano ?? %.', + v_sub_target, v_new_target + using errcode = 'P0001'; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set plan_id = p_new_plan_id, + plan_key = v_new_key, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'plan_changed', + v_old_plan, + p_new_plan_id, + auth.uid(), + 'Plan change via DEV menu', + 'dev_menu', + jsonb_build_object( + 'previous_plan', v_old_plan, + 'new_plan', p_new_plan_id, + 'new_plan_key', v_new_key, + 'new_plan_target', v_new_target + ) + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation (owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; +end; +$$; + + +-- +-- Name: cleanup_notification_queue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.cleanup_notification_queue() RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_deleted integer; +BEGIN + DELETE FROM public.notification_queue + WHERE status IN ('enviado', 'cancelado', 'ignorado') + AND created_at < now() - interval '90 days'; + + GET DIAGNOSTICS v_deleted = ROW_COUNT; + RETURN v_deleted; +END; +$$; + + +-- +-- Name: create_clinic_tenant(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_clinic_tenant(p_name text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_uid uuid; + v_tenant uuid; + v_name text; +begin + v_uid := auth.uid(); + if v_uid is null then + raise exception 'Not authenticated'; + end if; + + v_name := nullif(trim(coalesce(p_name, '')), ''); + if v_name is null then + v_name := 'Cl??nica'; + end if; + + insert into public.tenants (name, kind, created_at) + values (v_name, 'clinic', now()) + returning id into v_tenant; + + insert into public.tenant_members (tenant_id, user_id, role, status, created_at) + values (v_tenant, v_uid, 'tenant_admin', 'active', now()); + + return v_tenant; +end; +$$; + + +-- +-- Name: financial_records; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.financial_records ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + type public.financial_record_type DEFAULT 'receita'::public.financial_record_type NOT NULL, + amount numeric(10,2) NOT NULL, + description text, + category text, + payment_method text, + paid_at timestamp with time zone, + due_date date, + installments smallint DEFAULT 1, + installment_number smallint DEFAULT 1, + installment_group uuid, + agenda_evento_id uuid, + patient_id uuid, + clinic_fee_pct numeric(5,2) DEFAULT 0, + clinic_fee_amount numeric(10,2) DEFAULT 0, + net_amount numeric(10,2) GENERATED ALWAYS AS ((amount - clinic_fee_amount)) STORED, + insurance_plan_id uuid, + notes text, + tags text[], + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + discount_amount numeric(10,2) DEFAULT 0 NOT NULL, + final_amount numeric(10,2) DEFAULT 0 NOT NULL, + status text DEFAULT 'pending'::text NOT NULL, + category_id uuid, + CONSTRAINT financial_records_amount_check CHECK ((amount >= (0)::numeric)), + CONSTRAINT financial_records_clinic_fee_amount_check CHECK ((clinic_fee_amount >= (0)::numeric)), + CONSTRAINT financial_records_clinic_fee_pct_check CHECK (((clinic_fee_pct >= (0)::numeric) AND (clinic_fee_pct <= (100)::numeric))), + CONSTRAINT financial_records_discount_amount_check CHECK ((discount_amount >= (0)::numeric)), + CONSTRAINT financial_records_final_amount_check CHECK ((final_amount >= (0)::numeric)), + CONSTRAINT financial_records_installments_check CHECK ((installments >= 1)), + CONSTRAINT financial_records_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'paid'::text, 'partial'::text, 'overdue'::text, 'cancelled'::text, 'refunded'::text]))) +); + + +-- +-- Name: create_financial_record_for_session(uuid, uuid, uuid, uuid, numeric, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_financial_record_for_session(p_tenant_id uuid, p_owner_id uuid, p_patient_id uuid, p_agenda_evento_id uuid, p_amount numeric, p_due_date date) RETURNS SETOF public.financial_records + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_existing public.financial_records%ROWTYPE; + v_new public.financial_records%ROWTYPE; +BEGIN + -- Idempot??ncia: retorna o registro existente se j?? foi criado + SELECT * INTO v_existing + FROM public.financial_records + WHERE agenda_evento_id = p_agenda_evento_id + AND deleted_at IS NULL + LIMIT 1; + + IF FOUND THEN + RETURN NEXT v_existing; + RETURN; + END IF; + + -- Cria o novo registro + INSERT INTO public.financial_records ( + tenant_id, + owner_id, + patient_id, + agenda_evento_id, + amount, + discount_amount, + final_amount, + status, + due_date + ) VALUES ( + p_tenant_id, + p_owner_id, + p_patient_id, + p_agenda_evento_id, + p_amount, + 0, + p_amount, + 'pending', + p_due_date + ) + RETURNING * INTO v_new; + + -- Marca o evento da agenda como billed = true + UPDATE public.agenda_eventos + SET billed = TRUE + WHERE id = p_agenda_evento_id; + + RETURN NEXT v_new; +END; +$$; + + +-- +-- Name: create_patient_intake_request(text, text, text, text, text, boolean); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_patient_intake_request(p_token text, p_name text, p_email text DEFAULT NULL::text, p_phone text DEFAULT NULL::text, p_notes text DEFAULT NULL::text, p_consent boolean DEFAULT false) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_owner uuid; + v_active boolean; + v_expires timestamptz; + v_max_uses int; + v_uses int; + v_id uuid; +begin + select owner_id, active, expires_at, max_uses, uses + into v_owner, v_active, v_expires, v_max_uses, v_uses + from public.patient_invites + where token = p_token + limit 1; + + if v_owner is null then + raise exception 'Token inv??lido'; + end if; + + if v_active is not true then + raise exception 'Link desativado'; + end if; + + if v_expires is not null and now() > v_expires then + raise exception 'Link expirado'; + end if; + + if v_max_uses is not null and v_uses >= v_max_uses then + raise exception 'Limite de uso atingido'; + end if; + + if p_name is null or length(trim(p_name)) = 0 then + raise exception 'Nome ?? obrigat??rio'; + end if; + + insert into public.patient_intake_requests + (owner_id, token, name, email, phone, notes, consent, status) + values + (v_owner, p_token, trim(p_name), + nullif(lower(trim(p_email)), ''), + nullif(trim(p_phone), ''), + nullif(trim(p_notes), ''), + coalesce(p_consent, false), + 'new') + returning id into v_id; + + update public.patient_invites + set uses = uses + 1 + where token = p_token; + + return v_id; +end; +$$; + + +-- +-- Name: create_patient_intake_request_v2(text, jsonb); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_patient_intake_request_v2(p_token text, p_payload jsonb) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $_$ +declare + v_owner_id uuid; + v_intake_id uuid; + v_birth_raw text; + v_birth date; +begin + select owner_id + into v_owner_id + from public.patient_invites + where token = p_token; + + if v_owner_id is null then + raise exception 'Token inv??lido ou expirado'; + end if; + + v_birth_raw := nullif(trim(coalesce( + p_payload->>'data_nascimento', + '' + )), ''); + + v_birth := case + when v_birth_raw is null then null + when v_birth_raw ~ '^\d{4}-\d{2}-\d{2}$' then v_birth_raw::date + when v_birth_raw ~ '^\d{2}-\d{2}-\d{4}$' then to_date(v_birth_raw, 'DD-MM-YYYY') + else null + end; + + insert into public.patient_intake_requests ( + owner_id, + token, + status, + consent, + + nome_completo, + email_principal, + telefone, + + avatar_url, -- ???? AQUI + + data_nascimento, + cpf, + rg, + genero, + estado_civil, + profissao, + escolaridade, + nacionalidade, + naturalidade, + + cep, + pais, + cidade, + estado, + endereco, + numero, + complemento, + bairro, + + observacoes, + notas_internas, + + encaminhado_por, + onde_nos_conheceu + ) + values ( + v_owner_id, + p_token, + 'new', + coalesce((p_payload->>'consent')::boolean, false), + + nullif(trim(p_payload->>'nome_completo'), ''), + nullif(trim(p_payload->>'email_principal'), ''), + nullif(regexp_replace(coalesce(p_payload->>'telefone',''), '\D', '', 'g'), ''), + + nullif(trim(p_payload->>'avatar_url'), ''), -- ???? AQUI + + v_birth, + nullif(regexp_replace(coalesce(p_payload->>'cpf',''), '\D', '', 'g'), ''), + nullif(trim(p_payload->>'rg'), ''), + nullif(trim(p_payload->>'genero'), ''), + nullif(trim(p_payload->>'estado_civil'), ''), + nullif(trim(p_payload->>'profissao'), ''), + nullif(trim(p_payload->>'escolaridade'), ''), + nullif(trim(p_payload->>'nacionalidade'), ''), + nullif(trim(p_payload->>'naturalidade'), ''), + + nullif(regexp_replace(coalesce(p_payload->>'cep',''), '\D', '', 'g'), ''), + nullif(trim(p_payload->>'pais'), ''), + nullif(trim(p_payload->>'cidade'), ''), + nullif(trim(p_payload->>'estado'), ''), + nullif(trim(p_payload->>'endereco'), ''), + nullif(trim(p_payload->>'numero'), ''), + nullif(trim(p_payload->>'complemento'), ''), + nullif(trim(p_payload->>'bairro'), ''), + + nullif(trim(p_payload->>'observacoes'), ''), + nullif(trim(p_payload->>'notas_internas'), ''), + + nullif(trim(p_payload->>'encaminhado_por'), ''), + nullif(trim(p_payload->>'onde_nos_conheceu'), '') + ) + returning id into v_intake_id; + + return v_intake_id; +end; +$_$; + + +-- +-- Name: create_support_session(uuid, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_support_session(p_tenant_id uuid, p_ttl_minutes integer DEFAULT 60) RETURNS json + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_admin_id uuid; + v_role text; + v_token text; + v_expires timestamp with time zone; + v_session support_sessions; +BEGIN + -- Verifica autentica????o + v_admin_id := auth.uid(); + IF v_admin_id IS NULL THEN + RAISE EXCEPTION 'N??o autenticado.' USING ERRCODE = 'P0001'; + END IF; + + -- Verifica role saas_admin + SELECT role INTO v_role + FROM public.profiles + WHERE id = v_admin_id; + + IF v_role <> 'saas_admin' THEN + RAISE EXCEPTION 'Acesso negado. Somente saas_admin pode criar sess??es de suporte.' + USING ERRCODE = 'P0002'; + END IF; + + -- Valida TTL (1 a 120 minutos) + IF p_ttl_minutes < 1 OR p_ttl_minutes > 120 THEN + RAISE EXCEPTION 'TTL inv??lido. Use entre 1 e 120 minutos.' + USING ERRCODE = 'P0003'; + END IF; + + -- Valida tenant + IF NOT EXISTS (SELECT 1 FROM public.tenants WHERE id = p_tenant_id) THEN + RAISE EXCEPTION 'Tenant n??o encontrado.' + USING ERRCODE = 'P0004'; + END IF; + + -- Gera token ??nico (64 chars hex, sem pgcrypto) + v_token := replace(gen_random_uuid()::text, '-', '') || replace(gen_random_uuid()::text, '-', ''); + v_expires := now() + (p_ttl_minutes || ' minutes')::interval; + + -- Insere sess??o + INSERT INTO public.support_sessions (tenant_id, admin_id, token, expires_at) + VALUES (p_tenant_id, v_admin_id, v_token, v_expires) + RETURNING * INTO v_session; + + RETURN json_build_object( + 'token', v_session.token, + 'expires_at', v_session.expires_at, + 'session_id', v_session.id + ); +END; +$$; + + +-- +-- Name: therapist_payouts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.therapist_payouts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + period_start date NOT NULL, + period_end date NOT NULL, + total_sessions integer DEFAULT 0 NOT NULL, + gross_amount numeric(10,2) DEFAULT 0 NOT NULL, + clinic_fee_total numeric(10,2) DEFAULT 0 NOT NULL, + net_amount numeric(10,2) DEFAULT 0 NOT NULL, + status text DEFAULT 'pending'::text NOT NULL, + paid_at timestamp with time zone, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT therapist_payouts_clinic_fee_total_check CHECK ((clinic_fee_total >= (0)::numeric)), + CONSTRAINT therapist_payouts_gross_amount_check CHECK ((gross_amount >= (0)::numeric)), + CONSTRAINT therapist_payouts_net_amount_check CHECK ((net_amount >= (0)::numeric)), + CONSTRAINT therapist_payouts_period_chk CHECK ((period_end >= period_start)), + CONSTRAINT therapist_payouts_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'paid'::text, 'cancelled'::text]))) +); + + +-- +-- Name: create_therapist_payout(uuid, uuid, date, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) RETURNS public.therapist_payouts + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_payout public.therapist_payouts%ROWTYPE; + v_total_sessions INTEGER; + v_gross NUMERIC(10,2); + v_clinic_fee NUMERIC(10,2); + v_net NUMERIC(10,2); +BEGIN + -- ?????? Verifica????o de permiss??o ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + -- Apenas o pr??prio terapeuta ou o tenant_admin pode criar o repasse + IF auth.uid() <> p_therapist_id AND NOT public.is_tenant_admin(p_tenant_id) THEN + RAISE EXCEPTION 'Sem permiss??o para criar repasse para este terapeuta.'; + END IF; + + -- ?????? Verifica se j?? existe repasse para o mesmo per??odo ??????????????????????????????????????????????????? + IF EXISTS ( + SELECT 1 FROM public.therapist_payouts + WHERE owner_id = p_therapist_id + AND tenant_id = p_tenant_id + AND period_start = p_period_start + AND period_end = p_period_end + AND status <> 'cancelled' + ) THEN + RAISE EXCEPTION + 'J?? existe um repasse ativo para o per??odo % a % deste terapeuta.', + p_period_start, p_period_end; + END IF; + + -- ?????? Agrega os financial_records eleg??veis ?????????????????????????????????????????????????????????????????????????????????????????? + -- Eleg??veis: paid, receita, owner=terapeuta, tenant correto, paid_at no per??odo, + -- n??o soft-deleted, ainda n??o vinculados a nenhum payout. + SELECT + COUNT(*) AS total_sessions, + COALESCE(SUM(amount), 0) AS gross_amount, + COALESCE(SUM(clinic_fee_amount), 0) AS clinic_fee_total, + COALESCE(SUM(net_amount), 0) AS net_amount + INTO + v_total_sessions, v_gross, v_clinic_fee, v_net + FROM public.financial_records fr + WHERE fr.owner_id = p_therapist_id + AND fr.tenant_id = p_tenant_id + AND fr.type = 'receita' + AND fr.status = 'paid' + AND fr.deleted_at IS NULL + AND fr.paid_at::DATE BETWEEN p_period_start AND p_period_end + AND NOT EXISTS ( + SELECT 1 FROM public.therapist_payout_records tpr + WHERE tpr.financial_record_id = fr.id + ); + + -- Sem registros eleg??veis ??? n??o criar payout vazio + IF v_total_sessions = 0 THEN + RAISE EXCEPTION + 'Nenhum registro financeiro eleg??vel encontrado para o per??odo % a %.', + p_period_start, p_period_end; + END IF; + + -- ?????? Cria o repasse ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? + INSERT INTO public.therapist_payouts ( + owner_id, + tenant_id, + period_start, + period_end, + total_sessions, + gross_amount, + clinic_fee_total, + net_amount, + status + ) VALUES ( + p_therapist_id, + p_tenant_id, + p_period_start, + p_period_end, + v_total_sessions, + v_gross, + v_clinic_fee, + v_net, + 'pending' + ) + RETURNING * INTO v_payout; + + -- ?????? Vincula os financial_records ao repasse ???????????????????????????????????????????????????????????????????????????????????? + INSERT INTO public.therapist_payout_records (payout_id, financial_record_id) + SELECT v_payout.id, fr.id + FROM public.financial_records fr + WHERE fr.owner_id = p_therapist_id + AND fr.tenant_id = p_tenant_id + AND fr.type = 'receita' + AND fr.status = 'paid' + AND fr.deleted_at IS NULL + AND fr.paid_at::DATE BETWEEN p_period_start AND p_period_end + AND NOT EXISTS ( + SELECT 1 FROM public.therapist_payout_records tpr + WHERE tpr.financial_record_id = fr.id + ); + + RETURN v_payout; +END; +$$; + + +-- +-- Name: FUNCTION create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.create_therapist_payout(p_tenant_id uuid, p_therapist_id uuid, p_period_start date, p_period_end date) IS 'Cria um repasse para o terapeuta com todos os financial_records paid+receita do per??odo que ainda n??o estejam vinculados a outro repasse. Lan??a exce????o se n??o houver registros eleg??veis ou se j?? houver repasse ativo no per??odo.'; + + +-- +-- Name: current_member_id(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.current_member_id(p_tenant_id uuid) RETURNS uuid + LANGUAGE sql STABLE + AS $$ + select tm.id + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + limit 1 +$$; + + +-- +-- Name: current_member_role(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.current_member_role(p_tenant_id uuid) RETURNS text + LANGUAGE sql STABLE + AS $$ + select tm.role + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + limit 1 +$$; + + +-- +-- Name: debit_addon_credit(uuid, text, uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid DEFAULT NULL::uuid, p_description text DEFAULT 'Consumo'::text) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_credit addon_credits%ROWTYPE; + v_balance_before INTEGER; + v_balance_after INTEGER; +BEGIN + -- Lock e leitura + SELECT * INTO v_credit + FROM addon_credits + WHERE tenant_id = p_tenant_id AND addon_type = p_addon_type AND is_active = true + FOR UPDATE; + + IF NOT FOUND THEN + RETURN jsonb_build_object('success', false, 'reason', 'no_credits', 'balance', 0); + END IF; + + -- Verifica saldo + IF v_credit.balance <= 0 THEN + RETURN jsonb_build_object('success', false, 'reason', 'insufficient_balance', 'balance', 0); + END IF; + + -- Verifica rate limit di??rio + IF v_credit.daily_limit IS NOT NULL THEN + -- Reset se passou do dia + IF v_credit.daily_reset_at IS NULL OR v_credit.daily_reset_at < date_trunc('day', now()) THEN + UPDATE addon_credits SET daily_used = 0, daily_reset_at = date_trunc('day', now()) + interval '1 day' WHERE id = v_credit.id; + v_credit.daily_used := 0; + END IF; + + IF v_credit.daily_used >= v_credit.daily_limit THEN + RETURN jsonb_build_object('success', false, 'reason', 'daily_limit_reached', 'balance', v_credit.balance); + END IF; + END IF; + + -- Verifica rate limit hor??rio + IF v_credit.hourly_limit IS NOT NULL THEN + IF v_credit.hourly_reset_at IS NULL OR v_credit.hourly_reset_at < date_trunc('hour', now()) THEN + UPDATE addon_credits SET hourly_used = 0, hourly_reset_at = date_trunc('hour', now()) + interval '1 hour' WHERE id = v_credit.id; + v_credit.hourly_used := 0; + END IF; + + IF v_credit.hourly_used >= v_credit.hourly_limit THEN + RETURN jsonb_build_object('success', false, 'reason', 'hourly_limit_reached', 'balance', v_credit.balance); + END IF; + END IF; + + -- Verifica expira????o + IF v_credit.expires_at IS NOT NULL AND v_credit.expires_at < now() THEN + RETURN jsonb_build_object('success', false, 'reason', 'credits_expired', 'balance', v_credit.balance); + END IF; + + v_balance_before := v_credit.balance; + v_balance_after := v_credit.balance - 1; + + -- Debita + UPDATE addon_credits + SET balance = v_balance_after, + total_consumed = total_consumed + 1, + daily_used = COALESCE(daily_used, 0) + 1, + hourly_used = COALESCE(hourly_used, 0) + 1, + updated_at = now() + WHERE id = v_credit.id; + + -- Registra transa????o + INSERT INTO addon_transactions ( + tenant_id, addon_type, type, amount, + balance_before, balance_after, + queue_id, description + ) VALUES ( + p_tenant_id, p_addon_type, 'consume', -1, + v_balance_before, v_balance_after, + p_queue_id, p_description + ); + + RETURN jsonb_build_object( + 'success', true, + 'balance_before', v_balance_before, + 'balance_after', v_balance_after + ); +END; +$$; + + +-- +-- Name: FUNCTION debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.debit_addon_credit(p_tenant_id uuid, p_addon_type text, p_queue_id uuid, p_description text) IS 'Debita 1 cr??dito de add-on. Verifica saldo, rate limits e expira????o.'; + + +-- +-- Name: delete_commitment_full(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.delete_commitment_full(p_tenant_id uuid, p_commitment_id uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_is_native boolean; + v_fields int := 0; + v_logs int := 0; + v_parent int := 0; +begin + if auth.uid() is null then + raise exception 'Not authenticated'; + end if; + + if not exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.status = 'active' + ) then + raise exception 'Not allowed'; + end if; + + select dc.is_native + into v_is_native + from public.determined_commitments dc + where dc.tenant_id = p_tenant_id + and dc.id = p_commitment_id; + + if v_is_native is null then + raise exception 'Commitment not found'; + end if; + + if v_is_native = true then + raise exception 'Cannot delete native commitment'; + end if; + + delete from public.determined_commitment_fields + where tenant_id = p_tenant_id + and commitment_id = p_commitment_id; + get diagnostics v_fields = row_count; + + delete from public.commitment_time_logs + where tenant_id = p_tenant_id + and commitment_id = p_commitment_id; + get diagnostics v_logs = row_count; + + delete from public.determined_commitments + where tenant_id = p_tenant_id + and id = p_commitment_id; + get diagnostics v_parent = row_count; + + if v_parent <> 1 then + raise exception 'Parent not deleted (RLS/owner issue).'; + end if; + + return jsonb_build_object( + 'ok', true, + 'deleted', jsonb_build_object( + 'fields', v_fields, + 'logs', v_logs, + 'commitment', v_parent + ) + ); +end; +$$; + + +-- +-- Name: delete_determined_commitment(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.delete_determined_commitment(p_tenant_id uuid, p_commitment_id uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_is_native boolean; + v_fields_deleted int := 0; + v_logs_deleted int := 0; + v_commitment_deleted int := 0; +begin + if auth.uid() is null then + raise exception 'Not authenticated'; + end if; + + if not exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.status = 'active' + ) then + raise exception 'Not allowed'; + end if; + + select dc.is_native + into v_is_native + from public.determined_commitments dc + where dc.tenant_id = p_tenant_id + and dc.id = p_commitment_id; + + if v_is_native is null then + raise exception 'Commitment not found for tenant'; + end if; + + if v_is_native = true then + raise exception 'Cannot delete native commitment'; + end if; + + delete from public.determined_commitment_fields f + where f.tenant_id = p_tenant_id + and f.commitment_id = p_commitment_id; + get diagnostics v_fields_deleted = row_count; + + delete from public.commitment_time_logs l + where l.tenant_id = p_tenant_id + and l.commitment_id = p_commitment_id; + get diagnostics v_logs_deleted = row_count; + + delete from public.determined_commitments dc + where dc.tenant_id = p_tenant_id + and dc.id = p_commitment_id; + get diagnostics v_commitment_deleted = row_count; + + if v_commitment_deleted <> 1 then + raise exception 'Delete did not remove the commitment (tenant mismatch?)'; + end if; + + return jsonb_build_object( + 'ok', true, + 'tenant_id', p_tenant_id, + 'commitment_id', p_commitment_id, + 'deleted', jsonb_build_object( + 'fields', v_fields_deleted, + 'logs', v_logs_deleted, + 'commitment', v_commitment_deleted + ) + ); +end; +$$; + + +-- +-- Name: dev_list_auth_users(integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_list_auth_users(p_limit integer DEFAULT 50) RETURNS TABLE(id uuid, email text, created_at timestamp with time zone) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +begin + -- s?? saas_admin pode ver + if not exists ( + select 1 + from public.profiles p + where p.id = auth.uid() + and p.role = 'saas_admin' + ) then + return; + end if; + + return query + select + u.id, + u.email, + u.created_at + from auth.users u + order by u.created_at desc + limit greatest(1, least(coalesce(p_limit, 50), 500)); +end; +$$; + + +-- +-- Name: dev_list_custom_users(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_list_custom_users() RETURNS TABLE(user_id uuid, email text, created_at timestamp with time zone, global_role text, tenant_role text, tenant_id uuid, password_dev text, kind text) + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + with base as ( + select + u.id as user_id, + lower(u.email) as email, + u.created_at + from auth.users u + where lower(u.email) not in ( + 'clinic@agenciapsi.com.br', + 'therapist@agenciapsi.com.br', + 'patient@agenciapsi.com.br', + 'saas@agenciapsi.com.br' + ) + ), + prof as ( + select p.id, p.role as global_role + from public.profiles p + ), + last_membership as ( + select distinct on (tm.user_id) + tm.user_id, + tm.tenant_id, + tm.role as tenant_role, + tm.created_at + from public.tenant_members tm + where tm.status = 'active' + order by tm.user_id, tm.created_at desc + ) + select + b.user_id, + b.email, + b.created_at, + pr.global_role, + lm.tenant_role, + lm.tenant_id, + dc.password_dev, + dc.kind + from base b + left join prof pr on pr.id = b.user_id + left join last_membership lm on lm.user_id = b.user_id + left join public.dev_user_credentials dc on lower(dc.email) = b.email + order by b.created_at desc; +$$; + + +-- +-- Name: dev_list_intent_leads(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_list_intent_leads() RETURNS TABLE(email text, last_intent_at timestamp with time zone, plan_key text, billing_interval text, status text, tenant_id uuid) + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + select + lower(si.email) as email, + max(si.created_at) as last_intent_at, + (array_agg(si.plan_key order by si.created_at desc))[1] as plan_key, + (array_agg(si.interval order by si.created_at desc))[1] as billing_interval, + (array_agg(si.status order by si.created_at desc))[1] as status, + (array_agg(si.tenant_id order by si.created_at desc))[1] as tenant_id + from public.subscription_intents si + where si.email is not null + and not exists ( + select 1 + from auth.users au + where lower(au.email) = lower(si.email) + ) + group by lower(si.email) + order by max(si.created_at) desc; +$$; + + +-- +-- Name: dev_public_debug_snapshot(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.dev_public_debug_snapshot() RETURNS TABLE(users_total integer, tenants_total integer, intents_new_total integer, latest_intents jsonb) + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $_$ +declare + v_latest jsonb; +begin + select jsonb_agg( + jsonb_build_object( + 'created_at', si.created_at, + 'email_masked', + regexp_replace(lower(si.email), '(^.).*(@.*$)', '\1***\2'), + 'plan_key', si.plan_key, + 'status', si.status + ) + order by si.created_at desc + ) + into v_latest + from ( + select si.* + from public.subscription_intents si + where si.email is not null + order by si.created_at desc + limit 5 + ) si; + + return query + select + (select count(*)::int from auth.users) as users_total, + (select count(*)::int from public.tenants) as tenants_total, + (select count(*)::int from public.subscription_intents where status = 'new') as intents_new_total, + coalesce(v_latest, '[]'::jsonb) as latest_intents; +end; +$_$; + + +-- +-- Name: ensure_personal_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.ensure_personal_tenant() RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_uid uuid; + v_existing uuid; +BEGIN + v_uid := auth.uid(); + IF v_uid IS NULL THEN + RAISE EXCEPTION 'Not authenticated'; + END IF; + + SELECT tm.tenant_id INTO v_existing + FROM public.tenant_members tm + JOIN public.tenants t ON t.id = tm.tenant_id + WHERE tm.user_id = v_uid + AND tm.status = 'active' + AND t.kind IN ('therapist', 'saas') + ORDER BY tm.created_at DESC + LIMIT 1; + + IF v_existing IS NOT NULL THEN + RETURN v_existing; + END IF; + + RETURN public.provision_account_tenant(v_uid, 'therapist'); +END; +$$; + + +-- +-- Name: ensure_personal_tenant_for_user(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.ensure_personal_tenant_for_user(p_user_id uuid) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_uid uuid; + v_existing uuid; + v_tenant uuid; + v_email text; + v_name text; +begin + v_uid := p_user_id; + if v_uid is null then + raise exception 'Missing user id'; + end if; + + -- s?? considera tenant pessoal (kind='saas') + select tm.tenant_id + into v_existing + from public.tenant_members tm + join public.tenants t on t.id = tm.tenant_id + where tm.user_id = v_uid + and tm.status = 'active' + and t.kind = 'saas' + order by tm.created_at desc + limit 1; + + if v_existing is not null then + return v_existing; + end if; + + select email into v_email + from auth.users + where id = v_uid; + + v_name := coalesce(split_part(v_email, '@', 1), 'Conta'); + + insert into public.tenants (name, kind, created_at) + values (v_name || ' (Pessoal)', 'saas', now()) + returning id into v_tenant; + + insert into public.tenant_members (tenant_id, user_id, role, status, created_at) + values (v_tenant, v_uid, 'tenant_admin', 'active', now()); + + return v_tenant; +end; +$$; + + +-- +-- Name: faq_votar(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.faq_votar(faq_id uuid) RETURNS void + LANGUAGE sql SECURITY DEFINER + AS $$ + update public.saas_faq + set votos = votos + 1, + updated_at = now() + where id = faq_id + and ativo = true; +$$; + + +-- +-- Name: fix_all_subscription_mismatches(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.fix_all_subscription_mismatches() RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + r record; +begin + for r in + select distinct s.user_id as owner_id + from public.subscriptions s + where s.status = 'active' + and s.user_id is not null + loop + perform public.rebuild_owner_entitlements(r.owner_id); + end loop; +end; +$$; + + +-- +-- Name: fn_agenda_regras_semanais_no_overlap(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.fn_agenda_regras_semanais_no_overlap() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_count int; +begin + if new.ativo is false then + return new; + end if; + + select count(*) into v_count + from public.agenda_regras_semanais r + where r.owner_id = new.owner_id + and r.dia_semana = new.dia_semana + and r.ativo is true + and (tg_op = 'INSERT' or r.id <> new.id) + and (new.hora_inicio < r.hora_fim and new.hora_fim > r.hora_inicio); + + if v_count > 0 then + raise exception 'Janela sobreposta: j?? existe uma regra ativa nesse intervalo.'; + end if; + + return new; +end; +$$; + + +-- +-- Name: get_financial_report(uuid, date, date, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text DEFAULT 'month'::text) RETURNS TABLE(group_key text, group_label text, total_receitas numeric, total_despesas numeric, saldo numeric, total_pendente numeric, total_overdue numeric, count_records bigint) + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + AS $$ + + -- ?????? Valida p_group_by antes de executar ?????????????????????????????????????????????????????????????????????????????????????????????????????? + -- (lan??a erro se valor inv??lido; plpgsql seria necess??rio para isso em SQL puro, + -- ent??o usamos um CTE de valida????o com CASE WHEN para retornar vazio em vez de erro) + + WITH base AS ( + SELECT + fr.type, + fr.amount, + fr.final_amount, + fr.status, + fr.deleted_at, + -- Chave de agrupamento calculada conforme p_group_by + CASE p_group_by + WHEN 'month' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'YYYY-MM' + ) + WHEN 'week' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'IYYY-"W"IW' + ) + WHEN 'category' THEN COALESCE(fr.category_id::TEXT, fr.category, 'sem_categoria') + WHEN 'patient' THEN COALESCE(fr.patient_id::TEXT, 'sem_paciente') + ELSE NULL -- group_by inv??lido ??? group_key NULL ??? retorno vazio + END AS gkey, + -- Label leg??vel (enriquecido via JOIN abaixo quando poss??vel) + CASE p_group_by + WHEN 'month' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'YYYY-MM' + ) + WHEN 'week' THEN TO_CHAR( + COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE), + 'IYYY-"W"IW' + ) + WHEN 'category' THEN COALESCE(fc.name, fr.category, 'Sem categoria') + WHEN 'patient' THEN COALESCE(p.nome_completo, fr.patient_id::TEXT, 'Sem paciente') + ELSE NULL + END AS glabel + FROM public.financial_records fr + LEFT JOIN public.financial_categories fc + ON fc.id = fr.category_id + LEFT JOIN public.patients p + ON p.id = fr.patient_id + WHERE fr.owner_id = p_owner_id + AND fr.deleted_at IS NULL + AND COALESCE(fr.paid_at::DATE, fr.due_date, fr.created_at::DATE) + BETWEEN p_start_date AND p_end_date + ) + + SELECT + gkey AS group_key, + glabel AS group_label, + + COALESCE(SUM(final_amount) FILTER (WHERE type = 'receita' AND status = 'paid'), 0) + AS total_receitas, + + COALESCE(SUM(final_amount) FILTER (WHERE type = 'despesa' AND status = 'paid'), 0) + AS total_despesas, + + COALESCE(SUM(final_amount) FILTER (WHERE type = 'receita' AND status = 'paid'), 0) + - COALESCE(SUM(final_amount) FILTER (WHERE type = 'despesa' AND status = 'paid'), 0) + AS saldo, + + COALESCE(SUM(final_amount) FILTER (WHERE status = 'pending'), 0) AS total_pendente, + + COALESCE(SUM(final_amount) FILTER (WHERE status = 'overdue'), 0) AS total_overdue, + + COUNT(*) AS count_records + + FROM base + WHERE gkey IS NOT NULL -- descarta p_group_by inv??lido + GROUP BY gkey, glabel + ORDER BY gkey ASC; + +$$; + + +-- +-- Name: FUNCTION get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.get_financial_report(p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text) IS 'Relat??rio financeiro agrupado por m??s, semana ISO, categoria ou paciente. p_group_by aceita: ''month'' | ''week'' | ''category'' | ''patient''. Totais de receita/despesa consideram apenas registros com status=paid. total_pendente e total_overdue incluem todos os tipos (receita + despesa).'; + + +-- +-- Name: get_financial_summary(uuid, integer, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.get_financial_summary(p_owner_id uuid, p_year integer, p_month integer) RETURNS TABLE(total_receitas numeric, total_despesas numeric, total_pendente numeric, saldo_liquido numeric, total_repasse numeric, count_receitas bigint, count_despesas bigint) + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + AS $$ + SELECT + -- Receitas pagas no per??odo + COALESCE(SUM(amount) FILTER ( + WHERE type = 'receita' AND status = 'paid' + ), 0) AS total_receitas, + + -- Despesas pagas no per??odo + COALESCE(SUM(amount) FILTER ( + WHERE type = 'despesa' AND status = 'paid' + ), 0) AS total_despesas, + + -- Tudo pendente ou vencido (receitas + despesas) + COALESCE(SUM(amount) FILTER ( + WHERE status IN ('pending', 'overdue') + ), 0) AS total_pendente, + + -- Saldo l??quido (receitas pagas ??? despesas pagas) + COALESCE(SUM(amount) FILTER ( + WHERE type = 'receita' AND status = 'paid' + ), 0) + - COALESCE(SUM(amount) FILTER ( + WHERE type = 'despesa' AND status = 'paid' + ), 0) AS saldo_liquido, + + -- Total repassado ?? cl??nica (apenas receitas pagas) + COALESCE(SUM(clinic_fee_amount) FILTER ( + WHERE type = 'receita' AND status = 'paid' + ), 0) AS total_repasse, + + -- Contadores (excluindo soft-deleted) + COUNT(*) FILTER (WHERE type = 'receita' AND deleted_at IS NULL) AS count_receitas, + COUNT(*) FILTER (WHERE type = 'despesa' AND deleted_at IS NULL) AS count_despesas + + FROM public.financial_records + WHERE owner_id = p_owner_id + AND deleted_at IS NULL + AND EXTRACT(YEAR FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_year + AND EXTRACT(MONTH FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_month; +$$; + + +-- +-- Name: get_my_email(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.get_my_email() RETURNS text + LANGUAGE sql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ + select lower(email) + from auth.users + where id = auth.uid(); +$$; + + +-- +-- Name: guard_account_type_immutable(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_account_type_immutable() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF OLD.account_type <> 'free' AND NEW.account_type IS DISTINCT FROM OLD.account_type THEN + RAISE EXCEPTION 'account_type ?? imut??vel ap??s escolha (atual: "%" para tentativa: "%"). Para mudar de perfil, crie uma nova conta.', OLD.account_type, NEW.account_type + USING ERRCODE = 'P0001'; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: guard_locked_commitment(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_locked_commitment() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if (old.is_locked = true) then + if (tg_op = 'DELETE') then + raise exception 'Compromisso bloqueado n??o pode ser exclu??do.'; + end if; + + if (tg_op = 'UPDATE') then + if (new.active = false) then + raise exception 'Compromisso bloqueado n??o pode ser desativado.'; + end if; + + -- trava renomear (mant??m o "Sess??o" sempre igual) + if (new.name is distinct from old.name) then + raise exception 'Compromisso bloqueado n??o pode ser renomeado.'; + end if; + + -- se quiser travar descri????o tamb??m, descomente: + -- if (new.description is distinct from old.description) then + -- raise exception 'Compromisso bloqueado n??o pode alterar descri????o.'; + -- end if; + end if; + end if; + + return new; +end; +$$; + + +-- +-- Name: guard_no_change_core_plan_key(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_no_change_core_plan_key() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') + and new.key is distinct from old.key then + raise exception 'N??o ?? permitido alterar a key do plano padr??o (%).', old.key + using errcode = 'P0001'; + end if; + + return new; +end $$; + + +-- +-- Name: guard_no_change_plan_target(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_no_change_plan_target() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_bypass text; +begin + -- bypass controlado por sess??o/transa????o: + -- s?? passa se app.plan_migration_bypass = '1' + v_bypass := current_setting('app.plan_migration_bypass', true); + + if v_bypass = '1' then + return new; + end if; + + -- comportamento original (bloqueia qualquer mudan??a) + if new.target is distinct from old.target then + raise exception 'N??o ?? permitido alterar target do plano (%) de % para %.', + old.key, old.target, new.target + using errcode = 'P0001'; + end if; + + return new; +end +$$; + + +-- +-- Name: guard_no_delete_core_plans(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_no_delete_core_plans() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') then + raise exception 'Plano padr??o (%) n??o pode ser removido.', old.key + using errcode = 'P0001'; + end if; + + return old; +end $$; + + +-- +-- Name: guard_patient_cannot_own_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_patient_cannot_own_tenant() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_account_type text; +BEGIN + SELECT account_type INTO v_account_type + FROM public.profiles + WHERE id = NEW.user_id; + + IF v_account_type = 'patient' AND NEW.role IN ('tenant_admin', 'therapist') THEN + RAISE EXCEPTION 'Usu??rio com perfil "patient" n??o pode ser propriet??rio ou terapeuta de um tenant. Se tornou profissional? Crie uma nova conta.' + USING ERRCODE = 'P0001'; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: guard_tenant_kind_immutable(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.guard_tenant_kind_immutable() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF NEW.kind IS DISTINCT FROM OLD.kind THEN + RAISE EXCEPTION 'tenants.kind ?? imut??vel ap??s cria????o. Tentativa de alterar "%" para "%".', OLD.kind, NEW.kind + USING ERRCODE = 'P0001'; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: handle_new_user(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.handle_new_user() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + INSERT INTO public.profiles (id, role, account_type) + VALUES (NEW.id, 'portal_user', 'free') + ON CONFLICT (id) DO NOTHING; + RETURN NEW; +END; +$$; + + +-- +-- Name: handle_new_user_create_personal_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.handle_new_user_create_personal_tenant() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + -- Desabilitado. Tenant criado no onboarding via provision_account_tenant(). + RETURN NEW; +END; +$$; + + +-- +-- Name: has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.has_feature(p_owner_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.owner_feature_entitlements e + where e.owner_id = p_owner_id + and e.feature_key = p_feature_key + ); +$$; + + +-- +-- Name: is_clinic_tenant(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_clinic_tenant(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + SELECT EXISTS ( + SELECT 1 FROM public.tenants t + WHERE t.id = _tenant_id + AND t.kind IN ('clinic', 'clinic_coworking', 'clinic_reception', 'clinic_full') + ); +$$; + + +-- +-- Name: is_saas_admin(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_saas_admin() RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 from public.saas_admins sa + where sa.user_id = auth.uid() + ); +$$; + + +-- +-- Name: is_tenant_admin(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_tenant_admin(p_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ + select exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.role = 'tenant_admin' + and tm.status = 'active' + ); +$$; + + +-- +-- Name: is_tenant_member(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_tenant_member(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.tenant_members m + where m.tenant_id = _tenant_id + and m.user_id = auth.uid() + and m.status = 'active' + ); +$$; + + +-- +-- Name: is_therapist_tenant(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.is_therapist_tenant(_tenant_id uuid) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + SELECT EXISTS ( + SELECT 1 FROM public.tenants t + WHERE t.id = _tenant_id AND t.kind = 'therapist' + ); +$$; + + +-- +-- Name: jwt_email(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.jwt_email() RETURNS text + LANGUAGE sql STABLE + AS $$ + select nullif(lower(current_setting('request.jwt.claim.email', true)), ''); +$$; + + +-- +-- Name: list_financial_records(uuid, integer, integer, text, text, uuid, integer, integer); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.list_financial_records(p_owner_id uuid, p_year integer DEFAULT NULL::integer, p_month integer DEFAULT NULL::integer, p_type text DEFAULT NULL::text, p_status text DEFAULT NULL::text, p_patient_id uuid DEFAULT NULL::uuid, p_limit integer DEFAULT 50, p_offset integer DEFAULT 0) RETURNS SETOF public.financial_records + LANGUAGE sql STABLE SECURITY DEFINER + SET search_path TO 'public' + AS $$ + SELECT * + FROM public.financial_records + WHERE owner_id = p_owner_id + AND deleted_at IS NULL + AND (p_type IS NULL OR type::TEXT = p_type) + AND (p_status IS NULL OR status = p_status) + AND (p_patient_id IS NULL OR patient_id = p_patient_id) + AND (p_year IS NULL OR EXTRACT(YEAR FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_year) + AND (p_month IS NULL OR EXTRACT(MONTH FROM COALESCE(paid_at::DATE, due_date, created_at::DATE)) = p_month) + ORDER BY COALESCE(paid_at, due_date::TIMESTAMPTZ, created_at) DESC + LIMIT p_limit + OFFSET p_offset; +$$; + + +-- +-- Name: mark_as_paid(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.mark_as_paid(p_financial_record_id uuid, p_payment_method text) RETURNS SETOF public.financial_records + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_record public.financial_records%ROWTYPE; +BEGIN + -- Garante que o registro pertence ao usu??rio autenticado (RLS n??o aplica em SECURITY DEFINER) + SELECT * INTO v_record + FROM public.financial_records + WHERE id = p_financial_record_id + AND owner_id = auth.uid() + AND deleted_at IS NULL; + + IF NOT FOUND THEN + RAISE EXCEPTION 'Registro financeiro n??o encontrado ou sem permiss??o.'; + END IF; + + IF v_record.status NOT IN ('pending', 'overdue') THEN + RAISE EXCEPTION 'Apenas cobran??as pendentes ou vencidas podem ser marcadas como pagas.'; + END IF; + + UPDATE public.financial_records + SET status = 'paid', + paid_at = NOW(), + payment_method = p_payment_method, + updated_at = NOW() + WHERE id = p_financial_record_id + RETURNING * INTO v_record; + + RETURN NEXT v_record; +END; +$$; + + +-- +-- Name: mark_payout_as_paid(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.mark_payout_as_paid(p_payout_id uuid) RETURNS public.therapist_payouts + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_payout public.therapist_payouts%ROWTYPE; +BEGIN + -- Busca o payout + SELECT * INTO v_payout + FROM public.therapist_payouts + WHERE id = p_payout_id; + + IF NOT FOUND THEN + RAISE EXCEPTION 'Repasse n??o encontrado: %', p_payout_id; + END IF; + + -- Verifica permiss??o: apenas tenant_admin do tenant do repasse + IF NOT public.is_tenant_admin(v_payout.tenant_id) THEN + RAISE EXCEPTION 'Apenas o administrador da cl??nica pode marcar repasses como pagos.'; + END IF; + + -- Verifica status + IF v_payout.status <> 'pending' THEN + RAISE EXCEPTION + 'Repasse j?? est?? com status ''%''. Apenas repasses pendentes podem ser pagos.', + v_payout.status; + END IF; + + -- Atualiza + UPDATE public.therapist_payouts + SET + status = 'paid', + paid_at = NOW(), + updated_at = NOW() + WHERE id = p_payout_id + RETURNING * INTO v_payout; + + RETURN v_payout; +END; +$$; + + +-- +-- Name: FUNCTION mark_payout_as_paid(p_payout_id uuid); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.mark_payout_as_paid(p_payout_id uuid) IS 'Marca um repasse de terapeuta como pago. Apenas o tenant_admin pode chamar. Apenas repasses com status=pending podem ser finalizados.'; + + +-- +-- Name: my_tenants(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.my_tenants() RETURNS TABLE(tenant_id uuid, role text, status text, kind text) + LANGUAGE sql STABLE + AS $$ + select + tm.tenant_id, + tm.role, + tm.status, + t.kind + from public.tenant_members tm + join public.tenants t on t.id = tm.tenant_id + where tm.user_id = auth.uid(); +$$; + + +-- +-- Name: notice_track_click(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notice_track_click(p_notice_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +begin + update public.global_notices + set clicks_count = clicks_count + 1 + where id = p_notice_id; +end; +$$; + + +-- +-- Name: notice_track_view(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notice_track_view(p_notice_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +begin + update public.global_notices + set views_count = views_count + 1 + where id = p_notice_id; +end; +$$; + + +-- +-- Name: notify_on_intake(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notify_on_intake() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF NEW.status = 'new' THEN + INSERT INTO public.notifications ( + owner_id, + tenant_id, + type, + ref_id, + ref_table, + payload + ) + VALUES ( + NEW.owner_id, + NEW.tenant_id, + 'new_patient', + NEW.id, + 'patient_intake_requests', + jsonb_build_object( + 'title', 'Novo cadastro externo', + 'detail', COALESCE(NEW.nome_completo, 'Paciente'), + 'deeplink', '/therapist/patients/cadastro/recebidos', + 'avatar_initials', upper(left(COALESCE(NEW.nome_completo, '?'), 2)) + ) + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: notify_on_scheduling(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notify_on_scheduling() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ BEGIN IF NEW.status = 'pendente' THEN + INSERT INTO public.notifications ( owner_id, tenant_id, type, ref_id, ref_table, payload ) VALUES ( + NEW.owner_id, NEW.tenant_id, + 'new_scheduling', NEW.id, 'agendador_solicitacoes', jsonb_build_object( 'title', 'Nova solicita????o de agendamento', 'detail', COALESCE(NEW.paciente_nome, 'Paciente') || ' ' || COALESCE(NEW.paciente_sobrenome, '') || ' ??? ' || COALESCE(NEW.tipo, ''), 'deeplink', '/therapist/agendamentos-recebidos', 'avatar_initials', upper(left(COALESCE(NEW.paciente_nome, '?'), 1) || left(COALESCE(NEW.paciente_sobrenome, ''), 1)) ) ); END IF; RETURN NEW; END; $$; + + +-- +-- Name: notify_on_session_status(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.notify_on_session_status() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_nome text; +BEGIN + IF NEW.status IN ('faltou', 'cancelado') AND OLD.status IS DISTINCT FROM NEW.status THEN + + SELECT nome_completo + INTO v_nome + FROM public.patients + WHERE id = NEW.patient_id + LIMIT 1; + + INSERT INTO public.notifications ( + owner_id, + tenant_id, + type, + ref_id, + ref_table, + payload + ) + VALUES ( + NEW.owner_id, + NEW.tenant_id, + 'session_status', + NEW.id, + 'agenda_eventos', + jsonb_build_object( + 'title', CASE WHEN NEW.status = 'faltou' THEN 'Paciente faltou' ELSE 'Sess??o cancelada' END, + 'detail', COALESCE(v_nome, 'Paciente') || ' ??? ' || to_char(NEW.inicio_em, 'DD/MM HH24:MI'), + 'deeplink', '/therapist/agenda', + 'avatar_initials', upper(left(COALESCE(v_nome, '?'), 2)) + ) + ); + + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: on_new_user_seed_patient_groups(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.on_new_user_seed_patient_groups() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ + BEGIN + PERFORM public.seed_default_patient_groups(NEW.id); + RETURN NEW; + END; + $$; + + +-- +-- Name: patients_validate_member_consistency(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.patients_validate_member_consistency() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_tenant_responsible uuid; + v_tenant_therapist uuid; +BEGIN + -- responsible_member sempre deve existir e ser do tenant + SELECT tenant_id INTO v_tenant_responsible + FROM public.tenant_members + WHERE id = NEW.responsible_member_id; + + IF v_tenant_responsible IS NULL THEN + RAISE EXCEPTION 'Responsible member not found'; + END IF; + + IF NEW.tenant_id IS NULL THEN + RAISE EXCEPTION 'tenant_id is required'; + END IF; + + IF v_tenant_responsible <> NEW.tenant_id THEN + RAISE EXCEPTION 'Responsible member must belong to the same tenant'; + END IF; + + -- therapist scope: therapist_member_id deve existir e ser do mesmo tenant + IF NEW.patient_scope = 'therapist' THEN + IF NEW.therapist_member_id IS NULL THEN + RAISE EXCEPTION 'therapist_member_id is required when patient_scope=therapist'; + END IF; + + SELECT tenant_id INTO v_tenant_therapist + FROM public.tenant_members + WHERE id = NEW.therapist_member_id; + + IF v_tenant_therapist IS NULL THEN + RAISE EXCEPTION 'Therapist member not found'; + END IF; + + IF v_tenant_therapist <> NEW.tenant_id THEN + RAISE EXCEPTION 'Therapist member must belong to the same tenant'; + END IF; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: patients_validate_responsible_member_tenant(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.patients_validate_responsible_member_tenant() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + m_tenant uuid; +begin + select tenant_id into m_tenant + from public.tenant_members + where id = new.responsible_member_id; + + if m_tenant is null then + raise exception 'Responsible member not found'; + end if; + + if new.tenant_id is null then + raise exception 'tenant_id is required'; + end if; + + if m_tenant <> new.tenant_id then + raise exception 'Responsible member must belong to the same tenant'; + end if; + + return new; +end; +$$; + + +-- +-- Name: populate_notification_queue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.populate_notification_queue() RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + INSERT INTO public.notification_queue ( + tenant_id, owner_id, agenda_evento_id, patient_id, + channel, template_key, schedule_key, + resolved_vars, recipient_address, + scheduled_at, idempotency_key + ) + SELECT + ae.tenant_id, + ae.owner_id, + ae.id AS agenda_evento_id, + ae.patient_id, + ch.channel, + 'session.' || REPLACE(ns.event_type, '_sessao', '') || '.' || ch.channel, + ns.schedule_key, + jsonb_build_object( + 'nome_paciente', COALESCE(p.nome_completo, 'Paciente'), + 'data_sessao', TO_CHAR(ae.inicio_em AT TIME ZONE 'America/Sao_Paulo', 'DD/MM/YYYY'), + 'hora_sessao', TO_CHAR(ae.inicio_em AT TIME ZONE 'America/Sao_Paulo', 'HH24:MI'), + 'nome_terapeuta', COALESCE(prof.full_name, 'Terapeuta'), + 'modalidade', COALESCE(ae.modalidade, 'Presencial'), + 'titulo', COALESCE(ae.titulo, 'Sess??o') + ), + CASE ch.channel + WHEN 'whatsapp' THEN COALESCE(p.telefone, '') + WHEN 'sms' THEN COALESCE(p.telefone, '') + WHEN 'email' THEN COALESCE(p.email_principal, '') + END, + CASE + WHEN (ae.inicio_em - (ns.offset_minutes || ' minutes')::interval)::time + < ns.allowed_time_start + THEN DATE_TRUNC('day', ae.inicio_em - (ns.offset_minutes || ' minutes')::interval) + + ns.allowed_time_start + WHEN (ae.inicio_em - (ns.offset_minutes || ' minutes')::interval)::time + > ns.allowed_time_end + THEN DATE_TRUNC('day', ae.inicio_em - (ns.offset_minutes || ' minutes')::interval) + + ns.allowed_time_start + ELSE ae.inicio_em - (ns.offset_minutes || ' minutes')::interval + END, + ae.id::text || ':' || ns.schedule_key || ':' || ch.channel || ':' + || ae.inicio_em::date::text + FROM public.agenda_eventos ae + JOIN public.patients p ON p.id = ae.patient_id + LEFT JOIN public.profiles prof ON prof.id = ae.owner_id + JOIN public.notification_schedules ns + ON ns.owner_id = ae.owner_id + AND ns.is_active = true + AND ns.deleted_at IS NULL + AND ns.trigger_type = 'before_event' + AND ns.event_type = 'lembrete_sessao' + JOIN public.notification_channels nc + ON nc.owner_id = ae.owner_id + AND nc.is_active = true + AND nc.deleted_at IS NULL + CROSS JOIN LATERAL ( + SELECT 'whatsapp' AS channel WHERE ns.whatsapp_enabled AND nc.channel = 'whatsapp' + UNION ALL + SELECT 'email' AS channel WHERE ns.email_enabled AND nc.channel = 'email' + UNION ALL + SELECT 'sms' AS channel WHERE ns.sms_enabled AND nc.channel = 'sms' + ) ch + LEFT JOIN public.notification_preferences np + ON np.patient_id = ae.patient_id + AND np.owner_id = ae.owner_id + AND np.deleted_at IS NULL + WHERE + ae.inicio_em > now() + AND ae.inicio_em <= now() + interval '48 hours' + AND ae.status NOT IN ('cancelado', 'faltou') + AND CASE ch.channel + WHEN 'whatsapp' THEN COALESCE(p.telefone, '') != '' + WHEN 'sms' THEN COALESCE(p.telefone, '') != '' + WHEN 'email' THEN COALESCE(p.email_principal, '') != '' + END + AND CASE ch.channel + WHEN 'whatsapp' THEN COALESCE(np.whatsapp_opt_in, true) + WHEN 'email' THEN COALESCE(np.email_opt_in, true) + WHEN 'sms' THEN COALESCE(np.sms_opt_in, false) + END + AND EXISTS ( + SELECT 1 FROM public.profiles tp + WHERE tp.id = ae.owner_id + AND COALESCE(tp.notify_reminders, true) = true + ) + ON CONFLICT (idempotency_key) DO NOTHING; +END; +$$; + + +-- +-- Name: prevent_promoting_to_system(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.prevent_promoting_to_system() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if new.is_system = true and old.is_system is distinct from true then + raise exception 'N??o ?? permitido transformar um grupo comum em grupo do sistema.'; + end if; + return new; +end; +$$; + + +-- +-- Name: prevent_saas_membership(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.prevent_saas_membership() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM public.profiles + WHERE id = NEW.user_id + AND role = 'saas_admin' + ) THEN + RAISE EXCEPTION 'SaaS admin cannot belong to tenant'; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: prevent_system_group_changes(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.prevent_system_group_changes() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + -- Se for grupo do sistema, regras r??gidas: + if old.is_system = true then + + -- nunca pode deletar + if tg_op = 'DELETE' then + raise exception 'Grupos padr??o do sistema n??o podem ser alterados ou exclu??dos.'; + end if; + + if tg_op = 'UPDATE' then + -- permite SOMENTE mudar tenant_id e/ou updated_at + -- qualquer mudan??a de conte??do permanece proibida + if + new.nome is distinct from old.nome or + new.descricao is distinct from old.descricao or + new.cor is distinct from old.cor or + new.is_active is distinct from old.is_active or + new.is_system is distinct from old.is_system or + new.owner_id is distinct from old.owner_id or + new.therapist_id is distinct from old.therapist_id or + new.created_at is distinct from old.created_at + then + raise exception 'Grupos padr??o do sistema n??o podem ser alterados ou exclu??dos.'; + end if; + + -- chegou aqui: s?? tenant_id/updated_at mudaram -> ok + return new; + end if; + + end if; + + -- n??o-system: deixa passar + if tg_op = 'DELETE' then + return old; + end if; + + return new; +end; +$$; + + +-- +-- Name: provision_account_tenant(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text DEFAULT NULL::text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_tenant_id uuid; + v_account_type text; + v_name text; +BEGIN + IF p_kind NOT IN ('therapist', 'clinic_coworking', 'clinic_reception', 'clinic_full') THEN + RAISE EXCEPTION 'kind inv??lido: "%". Use: therapist, clinic_coworking, clinic_reception, clinic_full.', p_kind + USING ERRCODE = 'P0001'; + END IF; + + v_account_type := CASE WHEN p_kind = 'therapist' THEN 'therapist' ELSE 'clinic' END; + + IF EXISTS ( + SELECT 1 + FROM public.tenant_members tm + JOIN public.tenants t ON t.id = tm.tenant_id + WHERE tm.user_id = p_user_id + AND tm.role = 'tenant_admin' + AND tm.status = 'active' + AND t.kind = p_kind + ) THEN + RAISE EXCEPTION 'Usu??rio j?? possui um tenant do tipo "%".', p_kind + USING ERRCODE = 'P0001'; + END IF; + + v_name := COALESCE( + NULLIF(TRIM(p_name), ''), + ( + SELECT COALESCE(NULLIF(TRIM(pr.full_name), ''), SPLIT_PART(au.email, '@', 1)) + FROM public.profiles pr + JOIN auth.users au ON au.id = pr.id + WHERE pr.id = p_user_id + ), + 'Conta' + ); + + INSERT INTO public.tenants (name, kind, created_at) + VALUES (v_name, p_kind, now()) + RETURNING id INTO v_tenant_id; + + INSERT INTO public.tenant_members (tenant_id, user_id, role, status, created_at) + VALUES (v_tenant_id, p_user_id, 'tenant_admin', 'active', now()); + + UPDATE public.profiles + SET account_type = v_account_type + WHERE id = p_user_id; + + PERFORM public.seed_determined_commitments(v_tenant_id); + + RETURN v_tenant_id; +END; +$$; + + +-- +-- Name: FUNCTION provision_account_tenant(p_user_id uuid, p_kind text, p_name text); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.provision_account_tenant(p_user_id uuid, p_kind text, p_name text) IS 'Cria o tenant do tipo correto e atualiza account_type no profile. Chamar no onboarding ap??s escolha/pagamento de plano therapist ou clinic. p_kind: therapist | clinic_coworking | clinic_reception | clinic_full'; + + +-- +-- Name: reactivate_subscription(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.reactivate_subscription(p_subscription_id uuid) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_owner_type text; + v_owner_ref uuid; +begin + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id + for update; + + if not found then + raise exception 'Subscription n??o encontrada'; + end if; + + if v_sub.status = 'active' then + return v_sub; + end if; + + if v_sub.tenant_id is not null then + v_owner_type := 'clinic'; + v_owner_ref := v_sub.tenant_id; + elsif v_sub.user_id is not null then + v_owner_type := 'therapist'; + v_owner_ref := v_sub.user_id; + else + v_owner_type := null; + v_owner_ref := null; + end if; + + update public.subscriptions + set status = 'active', + cancel_at_period_end = false, + updated_at = now() + where id = p_subscription_id + returning * into v_sub; + + insert into public.subscription_events( + subscription_id, + owner_id, + owner_type, + owner_ref, + event_type, + old_plan_id, + new_plan_id, + created_by, + reason, + source, + metadata + ) + values ( + v_sub.id, + v_owner_ref, + v_owner_type, + v_owner_ref, + 'reactivated', + v_sub.plan_id, + v_sub.plan_id, + auth.uid(), + 'Reativa????o manual via admin', + 'admin_panel', + jsonb_build_object('previous_status', 'canceled') + ); + + if v_owner_ref is not null then + insert into public.entitlements_invalidation(owner_id, changed_at) + values (v_owner_ref, now()) + on conflict (owner_id) + do update set changed_at = excluded.changed_at; + end if; + + return v_sub; + +end; +$$; + + +-- +-- Name: rebuild_owner_entitlements(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.rebuild_owner_entitlements(p_owner_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_plan_id uuid; +begin + -- Plano ativo do owner (owner = subscriptions.user_id) + select s.plan_id + into v_plan_id + from public.subscriptions s + where s.user_id = p_owner_id + and s.status = 'active' + order by s.created_at desc + limit 1; + + -- Sempre zera entitlements do owner (rebuild) + delete from public.owner_feature_entitlements e + where e.owner_id = p_owner_id; + + -- Se n??o tem assinatura ativa, acabou + if v_plan_id is null then + return; + end if; + + -- Recria entitlements esperados pelo plano + insert into public.owner_feature_entitlements (owner_id, feature_key, sources, limits_list) + select + p_owner_id as owner_id, + f.key as feature_key, + array['plan'::text] as sources, + '{}'::jsonb as limits_list + from public.plan_features pf + join public.features f on f.id = pf.feature_id + where pf.plan_id = v_plan_id; + +end; +$$; + + +-- +-- Name: revoke_support_session(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.revoke_support_session(p_token text) RETURNS boolean + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_admin_id uuid; + v_role text; +BEGIN + v_admin_id := auth.uid(); + IF v_admin_id IS NULL THEN + RAISE EXCEPTION 'N??o autenticado.' USING ERRCODE = 'P0001'; + END IF; + + SELECT role INTO v_role FROM public.profiles WHERE id = v_admin_id; + IF v_role <> 'saas_admin' THEN + RAISE EXCEPTION 'Acesso negado.' USING ERRCODE = 'P0002'; + END IF; + + DELETE FROM public.support_sessions + WHERE token = p_token + AND admin_id = v_admin_id; + + RETURN FOUND; +END; +$$; + + +-- +-- Name: rotate_patient_invite_token(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.rotate_patient_invite_token(p_new_token text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + v_uid uuid; + v_id uuid; +begin + -- pega o usu??rio logado + v_uid := auth.uid(); + if v_uid is null then + raise exception 'Usu??rio n??o autenticado'; + end if; + + -- desativa tokens antigos ativos do usu??rio + update public.patient_invites + set active = false + where owner_id = v_uid + and active = true; + + -- cria novo token + insert into public.patient_invites (owner_id, token, active) + values (v_uid, p_new_token, true) + returning id into v_id; + + return v_id; +end; +$$; + + +-- +-- Name: saas_votar_doc(uuid, boolean); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.saas_votar_doc(p_doc_id uuid, p_util boolean) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_uid uuid := auth.uid(); + v_voto_antigo boolean; +begin + if v_uid is null then + raise exception 'N??o autenticado'; + end if; + + -- Verifica se j?? votou + select util into v_voto_antigo + from public.saas_doc_votos + where doc_id = p_doc_id and user_id = v_uid; + + if found then + -- J?? votou igual ??? cancela o voto (toggle) + if v_voto_antigo = p_util then + delete from public.saas_doc_votos + where doc_id = p_doc_id and user_id = v_uid; + + update public.saas_docs set + votos_util = greatest(0, votos_util - (case when p_util then 1 else 0 end)), + votos_nao_util = greatest(0, votos_nao_util - (case when not p_util then 1 else 0 end)), + updated_at = now() + where id = p_doc_id; + + return jsonb_build_object('acao', 'removido', 'util', null); + else + -- Mudou de voto + update public.saas_doc_votos set util = p_util, updated_at = now() + where doc_id = p_doc_id and user_id = v_uid; + + update public.saas_docs set + votos_util = greatest(0, votos_util + (case when p_util then 1 else -1 end)), + votos_nao_util = greatest(0, votos_nao_util + (case when not p_util then 1 else -1 end)), + updated_at = now() + where id = p_doc_id; + + return jsonb_build_object('acao', 'atualizado', 'util', p_util); + end if; + else + -- Primeiro voto + insert into public.saas_doc_votos (doc_id, user_id, util) + values (p_doc_id, v_uid, p_util); + + update public.saas_docs set + votos_util = votos_util + (case when p_util then 1 else 0 end), + votos_nao_util = votos_nao_util + (case when not p_util then 1 else 0 end), + updated_at = now() + where id = p_doc_id; + + return jsonb_build_object('acao', 'registrado', 'util', p_util); + end if; +end; +$$; + + +-- +-- Name: safe_delete_patient(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.safe_delete_patient(p_patient_id uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + -- Bloqueia se houver hist??rico + IF NOT public.can_delete_patient(p_patient_id) THEN + RETURN jsonb_build_object( + 'ok', false, + 'error', 'has_history', + 'message', 'Este paciente possui hist??rico cl??nico ou financeiro e n??o pode ser removido. Voc?? pode desativar ou arquivar o paciente.' + ); + END IF; + + -- Verifica ownership via RLS (owner_id ou responsible_member_id) + IF NOT EXISTS ( + SELECT 1 FROM public.patients + WHERE id = p_patient_id + AND ( + owner_id = auth.uid() + OR responsible_member_id IN ( + SELECT id FROM public.tenant_members WHERE user_id = auth.uid() + ) + ) + ) THEN + RETURN jsonb_build_object( + 'ok', false, + 'error', 'forbidden', + 'message', 'Sem permiss??o para excluir este paciente.' + ); + END IF; + + DELETE FROM public.patients WHERE id = p_patient_id; + + RETURN jsonb_build_object('ok', true); +END; +$$; + + +-- +-- Name: sanitize_phone_br(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.sanitize_phone_br(raw_phone text) RETURNS text + LANGUAGE plpgsql IMMUTABLE + AS $$ DECLARE digits text; + BEGIN + digits := regexp_replace(COALESCE(raw_phone, ''), '[^0-9]', '', 'g'); + IF digits = '' THEN RETURN ''; END IF; + IF length(digits) = 10 OR length(digits) = 11 THEN + digits := '55' || digits; + END IF; + RETURN digits; + END; $$; + + +-- +-- Name: seed_default_financial_categories(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.seed_default_financial_categories(p_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + INSERT INTO public.financial_categories (user_id, name, type, color, icon, sort_order) + VALUES + (p_user_id, 'Sess??o', 'receita', '#22c55e', 'pi pi-heart', 1), + (p_user_id, 'Supervis??o', 'receita', '#6366f1', 'pi pi-users', 2), + (p_user_id, 'Conv??nio', 'receita', '#3b82f6', 'pi pi-building', 3), + (p_user_id, 'Grupo terap??utico', 'receita', '#f59e0b', 'pi pi-sitemap', 4), + (p_user_id, 'Outro (receita)', 'receita', '#8b5cf6', 'pi pi-plus-circle', 5), + (p_user_id, 'Aluguel sala', 'despesa', '#ef4444', 'pi pi-home', 1), + (p_user_id, 'Plataforma/SaaS', 'despesa', '#f97316', 'pi pi-desktop', 2), + (p_user_id, 'Repasse cl??nica', 'despesa', '#64748b', 'pi pi-arrow-right-arrow-left', 3), + (p_user_id, 'Supervis??o (custo)', 'despesa', '#6366f1', 'pi pi-users', 4), + (p_user_id, 'Outro (despesa)', 'despesa', '#94a3b8', 'pi pi-minus-circle', 5) + ON CONFLICT DO NOTHING; +END; +$$; + + +-- +-- Name: seed_default_patient_groups(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.seed_default_patient_groups(p_tenant_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_owner_id uuid; +BEGIN + -- busca o owner (tenant_admin) do tenant + SELECT user_id INTO v_owner_id + FROM public.tenant_members + WHERE tenant_id = p_tenant_id + AND role = 'tenant_admin' + AND status = 'active' + LIMIT 1; + + IF v_owner_id IS NULL THEN + RETURN; + END IF; + + INSERT INTO public.patient_groups (owner_id, nome, cor, is_system, tenant_id) + VALUES + (v_owner_id, 'Crian??as', '#60a5fa', true, p_tenant_id), + (v_owner_id, 'Adolescentes', '#a78bfa', true, p_tenant_id), + (v_owner_id, 'Idosos', '#34d399', true, p_tenant_id) + ON CONFLICT (owner_id, nome) DO NOTHING; +END; +$$; + + +-- +-- Name: seed_determined_commitments(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.seed_determined_commitments(p_tenant_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_id uuid; +begin + -- Sess??o (locked + sempre ativa) + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'session' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'session', true, true, 'Sess??o', 'Sess??o com paciente'); + end if; + + -- Leitura + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'reading' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'reading', false, true, 'Leitura', 'Praticar leitura'); + end if; + + -- Supervis??o + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'supervision' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'supervision', false, true, 'Supervis??o', 'Supervis??o'); + end if; + + -- Aula ??? (corrigido) + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'class' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'class', false, false, 'Aula', 'Dar aula'); + end if; + + -- An??lise pessoal + if not exists ( + select 1 from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'analysis' + ) then + insert into public.determined_commitments + (tenant_id, is_native, native_key, is_locked, active, name, description) + values + (p_tenant_id, true, 'analysis', false, true, 'An??lise Pessoal', 'Minha an??lise pessoal'); + end if; + + -- ------------------------------------------------------- + -- Campos padr??o (idempotentes por (commitment_id, key)) + -- ------------------------------------------------------- + + -- Leitura + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'reading' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'book') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'book', 'Livro', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'author') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'author', 'Autor', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; + + -- Supervis??o + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'supervision' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'supervisor') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'supervisor', 'Supervisor', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'topic') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'topic', 'Assunto', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; + + -- Aula + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'class' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'theme') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'theme', 'Tema', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'group') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'group', 'Turma', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; + + -- An??lise + select id into v_id + from public.determined_commitments + where tenant_id = p_tenant_id and is_native = true and native_key = 'analysis' + limit 1; + + if v_id is not null then + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'analyst') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'analyst', 'Analista', 'text', false, 10); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'focus') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'focus', 'Foco', 'text', false, 20); + end if; + + if not exists (select 1 from public.determined_commitment_fields where commitment_id = v_id and key = 'notes') then + insert into public.determined_commitment_fields (tenant_id, commitment_id, key, label, field_type, required, sort_order) + values (p_tenant_id, v_id, 'notes', 'Observa????o', 'textarea', false, 30); + end if; + end if; +end; +$$; + + +-- +-- Name: set_insurance_plans_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_insurance_plans_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN NEW.updated_at = now(); RETURN NEW; END; $$; + + +-- +-- Name: set_medicos_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_medicos_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: set_owner_id(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_owner_id() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if new.owner_id is null then + new.owner_id := auth.uid(); + end if; + return new; +end; +$$; + + +-- +-- Name: set_services_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_services_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: set_tenant_feature_exception(uuid, text, boolean, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_tenant_feature_exception(p_tenant_id uuid, p_feature_key text, p_enabled boolean, p_reason text DEFAULT NULL::text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +begin + -- ??? S?? owner ou admin do tenant podem alterar features + if not exists ( + select 1 from public.tenant_members + where tenant_id = p_tenant_id + and user_id = auth.uid() + and role in ('owner', 'admin') + and status = 'active' + ) then + raise exception 'Acesso negado: apenas owner/admin pode alterar features do tenant.'; + end if; + + insert into public.tenant_features (tenant_id, feature_key, enabled) + values (p_tenant_id, p_feature_key, p_enabled) + on conflict (tenant_id, feature_key) + do update set enabled = excluded.enabled; + + insert into public.tenant_feature_exceptions_log ( + tenant_id, feature_key, enabled, reason, created_by + ) values ( + p_tenant_id, p_feature_key, p_enabled, p_reason, auth.uid() + ); +end; +$$; + + +-- +-- Name: set_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: set_updated_at_recurrence(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.set_updated_at_recurrence() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN NEW.updated_at = now(); RETURN NEW; END; +$$; + + +-- +-- Name: split_recurrence_at(uuid, date); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.split_recurrence_at(p_recurrence_id uuid, p_from_date date) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_old public.recurrence_rules; + v_new_id uuid; +BEGIN + -- busca a regra original + SELECT * INTO v_old + FROM public.recurrence_rules + WHERE id = p_recurrence_id; + + IF NOT FOUND THEN + RAISE EXCEPTION 'recurrence_rule % n??o encontrada', p_recurrence_id; + END IF; + + -- encerra a regra antiga na data anterior + UPDATE public.recurrence_rules + SET + end_date = p_from_date - INTERVAL '1 day', + open_ended = false, + updated_at = now() + WHERE id = p_recurrence_id; + + -- cria nova regra a partir de p_from_date + INSERT INTO public.recurrence_rules ( + tenant_id, owner_id, therapist_id, patient_id, + determined_commitment_id, type, interval, weekdays, + start_time, end_time, timezone, duration_min, + start_date, end_date, max_occurrences, open_ended, + modalidade, titulo_custom, observacoes, extra_fields, status + ) + SELECT + tenant_id, owner_id, therapist_id, patient_id, + determined_commitment_id, type, interval, weekdays, + start_time, end_time, timezone, duration_min, + p_from_date, v_old.end_date, v_old.max_occurrences, v_old.open_ended, + modalidade, titulo_custom, observacoes, extra_fields, status + FROM public.recurrence_rules + WHERE id = p_recurrence_id + RETURNING id INTO v_new_id; + + RETURN v_new_id; +END; +$$; + + +-- +-- Name: subscription_intents_view_insert(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.subscription_intents_view_insert() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_target text; + v_plan_id uuid; +begin + select p.id, p.target into v_plan_id, v_target + from public.plans p + where p.key = new.plan_key; + + if v_plan_id is null then + raise exception 'Plano inv??lido: plan_key=%', new.plan_key; + end if; + + if lower(v_target) = 'clinic' then + if new.tenant_id is null then + raise exception 'Inten????o clinic exige tenant_id.'; + end if; + + insert into public.subscription_intents_tenant ( + id, tenant_id, created_by_user_id, email, + plan_id, plan_key, interval, amount_cents, currency, + status, source, notes, created_at, paid_at + ) values ( + coalesce(new.id, gen_random_uuid()), + new.tenant_id, new.created_by_user_id, new.email, + v_plan_id, new.plan_key, coalesce(new.interval,'month'), + new.amount_cents, coalesce(new.currency,'BRL'), + coalesce(new.status,'pending'), coalesce(new.source,'manual'), + new.notes, coalesce(new.created_at, now()), new.paid_at + ); + + new.plan_target := 'clinic'; + return new; + end if; + + -- therapist ou supervisor ??? tabela personal + if lower(v_target) in ('therapist', 'supervisor') then + insert into public.subscription_intents_personal ( + id, user_id, created_by_user_id, email, + plan_id, plan_key, interval, amount_cents, currency, + status, source, notes, created_at, paid_at + ) values ( + coalesce(new.id, gen_random_uuid()), + new.user_id, new.created_by_user_id, new.email, + v_plan_id, new.plan_key, coalesce(new.interval,'month'), + new.amount_cents, coalesce(new.currency,'BRL'), + coalesce(new.status,'pending'), coalesce(new.source,'manual'), + new.notes, coalesce(new.created_at, now()), new.paid_at + ); + + new.plan_target := lower(v_target); -- 'therapist' ou 'supervisor' + return new; + end if; + + raise exception 'Target de plano n??o suportado: %', v_target; +end; +$$; + + +-- +-- Name: subscriptions_validate_scope(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.subscriptions_validate_scope() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE + v_target text; +BEGIN + SELECT lower(p.target) INTO v_target + FROM public.plans p + WHERE p.id = NEW.plan_id; + + IF v_target IS NULL THEN + RAISE EXCEPTION 'Plano inv??lido (target nulo).'; + END IF; + + IF v_target = 'clinic' THEN + IF NEW.tenant_id IS NULL THEN + RAISE EXCEPTION 'Assinatura clinic exige tenant_id.'; + END IF; + IF NEW.user_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura clinic n??o pode ter user_id (XOR).'; + END IF; + + ELSIF v_target IN ('therapist', 'supervisor') THEN + -- supervisor ?? pessoal como therapist + IF NEW.tenant_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura % n??o deve ter tenant_id.', v_target; + END IF; + IF NEW.user_id IS NULL THEN + RAISE EXCEPTION 'Assinatura % exige user_id.', v_target; + END IF; + + ELSIF v_target = 'patient' THEN + IF NEW.tenant_id IS NOT NULL THEN + RAISE EXCEPTION 'Assinatura patient n??o deve ter tenant_id.'; + END IF; + IF NEW.user_id IS NULL THEN + RAISE EXCEPTION 'Assinatura patient exige user_id.'; + END IF; + + ELSE + RAISE EXCEPTION 'Target de plano inv??lido: %', v_target; + END IF; + + RETURN NEW; +END; +$$; + + +-- +-- Name: sync_busy_mirror_agenda_eventos(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.sync_busy_mirror_agenda_eventos() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +declare + clinic_tenant uuid; + is_personal boolean; + should_mirror boolean; +begin + -- Anti-recurs??o: espelho n??o espelha + if (tg_op <> 'DELETE') then + if new.mirror_of_event_id is not null then + return new; + end if; + else + if old.mirror_of_event_id is not null then + return old; + end if; + end if; + + -- Define se ?? pessoal e se deve espelhar + if (tg_op = 'DELETE') then + is_personal := (old.tenant_id = old.owner_id); + should_mirror := (old.visibility_scope in ('busy_only','private')); + else + is_personal := (new.tenant_id = new.owner_id); + should_mirror := (new.visibility_scope in ('busy_only','private')); + end if; + + -- Se n??o ?? pessoal, n??o faz nada + if not is_personal then + if (tg_op = 'DELETE') then + return old; + end if; + return new; + end if; + + -- DELETE: remove espelhos existentes + if (tg_op = 'DELETE') then + delete from public.agenda_eventos e + where e.mirror_of_event_id = old.id + and e.mirror_source = 'personal_busy_mirror'; + + return old; + end if; + + -- INSERT/UPDATE: + -- Se n??o deve espelhar, remove espelhos e sai + if not should_mirror then + delete from public.agenda_eventos e + where e.mirror_of_event_id = new.id + and e.mirror_source = 'personal_busy_mirror'; + + return new; + end if; + + -- Para cada cl??nica onde o usu??rio ?? therapist active, cria/atualiza o "Ocupado" + for clinic_tenant in + select tm.tenant_id + from public.tenant_members tm + where tm.user_id = new.owner_id + and tm.role = 'therapist' + and tm.status = 'active' + and tm.tenant_id <> new.owner_id + loop + insert into public.agenda_eventos ( + tenant_id, + owner_id, + terapeuta_id, + paciente_id, + tipo, + status, + titulo, + observacoes, + inicio_em, + fim_em, + mirror_of_event_id, + mirror_source, + visibility_scope, + created_at, + updated_at + ) values ( + clinic_tenant, + new.owner_id, + new.owner_id, + null, + 'bloqueio'::public.tipo_evento_agenda, + 'agendado'::public.status_evento_agenda, + 'Ocupado', + null, + new.inicio_em, + new.fim_em, + new.id, + 'personal_busy_mirror', + 'public', + now(), + now() + ) + on conflict (tenant_id, mirror_of_event_id) where mirror_of_event_id is not null + do update set + owner_id = excluded.owner_id, + terapeuta_id = excluded.terapeuta_id, + tipo = excluded.tipo, + status = excluded.status, + titulo = excluded.titulo, + observacoes = excluded.observacoes, + inicio_em = excluded.inicio_em, + fim_em = excluded.fim_em, + updated_at = now(); + end loop; + + -- Limpa espelhos de cl??nicas onde o v??nculo therapist active n??o existe mais + delete from public.agenda_eventos e + where e.mirror_of_event_id = new.id + and e.mirror_source = 'personal_busy_mirror' + and not exists ( + select 1 + from public.tenant_members tm + where tm.user_id = new.owner_id + and tm.role = 'therapist' + and tm.status = 'active' + and tm.tenant_id = e.tenant_id + ); + + return new; +end; +$$; + + +-- +-- Name: sync_overdue_financial_records(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.sync_overdue_financial_records() RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_count integer; +BEGIN + UPDATE public.financial_records + SET + status = 'overdue', + updated_at = NOW() + WHERE status = 'pending' + AND due_date IS NOT NULL + AND due_date < CURRENT_DATE + AND deleted_at IS NULL; + + GET DIAGNOSTICS v_count = ROW_COUNT; + RETURN v_count; +END; +$$; + + +-- +-- Name: FUNCTION sync_overdue_financial_records(); Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON FUNCTION public.sync_overdue_financial_records() IS 'Marca como overdue todos os financial_records pendentes com due_date vencido. Pode ser chamada manualmente, via pg_cron ou via Supabase Edge Function agendada.'; + + +-- +-- Name: tenant_accept_invite(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_accept_invite(p_token uuid) RETURNS jsonb + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_uid uuid; + v_email text; + v_invite public.tenant_invites%rowtype; +begin + -- 1) precisa estar autenticado + v_uid := auth.uid(); + if v_uid is null then + raise exception 'not_authenticated' using errcode = 'P0001'; + end if; + + -- 2) pega email real do usu??rio logado sem depender do JWT claim + select u.email + into v_email + from auth.users u + where u.id = v_uid; + + if v_email is null or length(trim(v_email)) = 0 then + raise exception 'missing_user_email' using errcode = 'P0001'; + end if; + + -- 3) carrega o invite e trava linha (evita 2 aceites concorrentes) + select * + into v_invite + from public.tenant_invites i + where i.token = p_token + for update; + + if not found then + raise exception 'invite_not_found' using errcode = 'P0001'; + end if; + + -- 4) valida????es de estado + if v_invite.revoked_at is not null then + raise exception 'invite_revoked' using errcode = 'P0001'; + end if; + + if v_invite.accepted_at is not null then + raise exception 'invite_already_accepted' using errcode = 'P0001'; + end if; + + if v_invite.expires_at is not null and v_invite.expires_at <= now() then + raise exception 'invite_expired' using errcode = 'P0001'; + end if; + + -- 5) valida email (case-insensitive) + if lower(trim(v_invite.email)) <> lower(trim(v_email)) then + raise exception 'email_mismatch' using errcode = 'P0001'; + end if; + + -- 6) consome o invite + update public.tenant_invites + set accepted_at = now(), + accepted_by = v_uid + where id = v_invite.id; + + -- 7) cria ou reativa o membership + insert into public.tenant_members (tenant_id, user_id, role, status, created_at) + values (v_invite.tenant_id, v_uid, v_invite.role, 'active', now()) + on conflict (tenant_id, user_id) + do update set + role = excluded.role, + status = 'active'; + + -- 8) retorno ??til pro front (voc?? j?? tenta ler tenant_id no AcceptInvitePage) + return jsonb_build_object( + 'ok', true, + 'tenant_id', v_invite.tenant_id, + 'role', v_invite.role + ); +end; +$$; + + +-- +-- Name: tenant_members; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_members ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + user_id uuid NOT NULL, + role text NOT NULL, + status text DEFAULT 'active'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_add_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_add_member_by_email(p_tenant_id uuid, p_email text, p_role text DEFAULT 'therapist'::text) RETURNS public.tenant_members + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_target_uid uuid; + v_member public.tenant_members%rowtype; + v_is_admin boolean; + v_email text; +begin + if p_tenant_id is null then + raise exception 'tenant_id ?? obrigat??rio'; + end if; + + v_email := lower(trim(coalesce(p_email, ''))); + if v_email = '' then + raise exception 'email ?? obrigat??rio'; + end if; + + -- valida role permitida + if p_role not in ('tenant_admin','therapist','secretary','patient') then + raise exception 'role inv??lida: %', p_role; + end if; + + -- apenas admin do tenant (role real no banco) + select exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = p_tenant_id + and tm.user_id = auth.uid() + and tm.role = 'tenant_admin' + and coalesce(tm.status,'active') = 'active' + ) into v_is_admin; + + if not v_is_admin then + raise exception 'sem permiss??o: apenas admin da cl??nica pode adicionar membros'; + end if; + + -- acha usu??rio pelo e-mail no Supabase Auth + select u.id + into v_target_uid + from auth.users u + where lower(u.email) = v_email + limit 1; + + if v_target_uid is null then + raise exception 'nenhum usu??rio encontrado com este e-mail'; + end if; + + -- cria ou reativa membro + insert into public.tenant_members (tenant_id, user_id, role, status) + values (p_tenant_id, v_target_uid, p_role, 'active') + on conflict (tenant_id, user_id) + do update set + role = excluded.role, + status = 'active' + returning * into v_member; + + return v_member; +end; +$$; + + +-- +-- Name: tenant_feature_allowed(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_feature_allowed(p_tenant_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.v_tenant_entitlements v + where v.tenant_id = p_tenant_id + and v.feature_key = p_feature_key + and coalesce(v.allowed, false) = true + ); +$$; + + +-- +-- Name: tenant_feature_enabled(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_feature_enabled(p_tenant_id uuid, p_feature_key text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select coalesce( + (select tf.enabled + from public.tenant_features tf + where tf.tenant_id = p_tenant_id and tf.feature_key = p_feature_key), + false + ); +$$; + + +-- +-- Name: tenant_features_guard_with_plan(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_features_guard_with_plan() RETURNS trigger + LANGUAGE plpgsql + AS $$ +declare + v_allowed boolean; +begin + -- s?? valida quando est?? habilitando + if new.enabled is distinct from true then + return new; + end if; + + -- permitido pelo plano do tenant? + select exists ( + select 1 + from public.v_tenant_entitlements_full v + where v.tenant_id = new.tenant_id + and v.feature_key = new.feature_key + and v.allowed = true + ) + into v_allowed; + + if not v_allowed then + raise exception 'Feature % n??o permitida pelo plano atual do tenant %.', + new.feature_key, new.tenant_id + using errcode = 'P0001'; + end if; + + return new; +end; +$$; + + +-- +-- Name: tenant_has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_has_feature(_tenant_id uuid, _feature text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select + exists ( + select 1 + from public.v_tenant_entitlements e + where e.tenant_id = _tenant_id + and e.feature_key = _feature + and e.allowed = true + ) + or exists ( + select 1 + from public.tenant_features tf + where tf.tenant_id = _tenant_id + and tf.feature_key = _feature + and tf.enabled = true + ); +$$; + + +-- +-- Name: tenant_invite_member_by_email(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_invite_member_by_email(p_tenant_id uuid, p_email text, p_role text) RETURNS uuid + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public', 'auth' + AS $$ +declare + v_email text; + v_my_email text; + v_token uuid; + v_updated int; +begin + -- valida????es b??sicas + if p_tenant_id is null then + raise exception 'tenant_id inv??lido' using errcode = 'P0001'; + end if; + + v_email := lower(trim(coalesce(p_email, ''))); + if v_email = '' then + raise exception 'Informe um email' using errcode = 'P0001'; + end if; + + -- role permitido (ajuste se quiser) + if p_role is null or p_role not in ('therapist', 'secretary') then + raise exception 'Role inv??lido (use therapist/secretary)' using errcode = 'P0001'; + end if; + + -- ??? bloqueio: auto-convite + v_my_email := public.get_my_email(); + if v_my_email is not null and v_email = v_my_email then + raise exception 'Voc?? n??o pode convidar o seu pr??prio email.' using errcode = 'P0001'; + end if; + + -- ??? bloqueio: j?? ?? membro ativo do tenant + if exists ( + select 1 + from tenant_members tm + join auth.users au on au.id = tm.user_id + where tm.tenant_id = p_tenant_id + and tm.status = 'active' + and lower(au.email) = v_email + ) then + raise exception 'Este email j?? est?? vinculado a esta cl??nica.' using errcode = 'P0001'; + end if; + + -- ??? permiss??o: s?? admin do tenant pode convidar + if not exists ( + select 1 + from tenant_members me + where me.tenant_id = p_tenant_id + and me.user_id = auth.uid() + and me.status = 'active' + and me.role in ('tenant_admin','clinic_admin') + ) then + raise exception 'Sem permiss??o para convidar membros.' using errcode = 'P0001'; + end if; + + -- Gera token (reenvio simples / regenera????o) + v_token := gen_random_uuid(); + + -- 1) tenta "regerar" um convite pendente existente (mesmo email) + update tenant_invites + set token = v_token, + role = p_role, + created_at = now(), + expires_at = now() + interval '7 days', + accepted_at = null, + revoked_at = null + where tenant_id = p_tenant_id + and lower(email) = v_email + and accepted_at is null + and revoked_at is null; + + get diagnostics v_updated = row_count; + + -- 2) se n??o atualizou nada, cria convite novo + if v_updated = 0 then + insert into tenant_invites (tenant_id, email, role, token, created_at, expires_at) + values (p_tenant_id, v_email, p_role, v_token, now(), now() + interval '7 days'); + end if; + + return v_token; +end; +$$; + + +-- +-- Name: tenant_reactivate_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_reactivate_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + update public.tenant_members + set status = 'active' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_remove_member(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_remove_member(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +declare + v_role text; +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + if p_member_user_id = auth.uid() then + raise exception 'cannot_remove_self'; + end if; + + -- pega role atual do membro (se n??o existir, erro) + select role into v_role + from public.tenant_members + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if v_role is null then + raise exception 'membership_not_found'; + end if; + + -- trava: se for therapist, n??o pode remover com eventos futuros + if v_role = 'therapist' then + if exists ( + select 1 + from public.agenda_eventos e + where e.owner_id = p_tenant_id + and e.terapeuta_id = p_member_user_id + and e.inicio_em >= now() + and e.status::text not in ('cancelado','cancelled','canceled') + limit 1 + ) then + raise exception 'cannot_remove_therapist_with_future_events'; + end if; + end if; + + update public.tenant_members + set status = 'inactive' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_remove_member_soft(uuid, uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_remove_member_soft(p_tenant_id uuid, p_member_user_id uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + if p_member_user_id = auth.uid() then + raise exception 'cannot_remove_self'; + end if; + + update public.tenant_members + set status = 'inactive' + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_revoke_invite(uuid, text, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_revoke_invite(p_tenant_id uuid, p_email text, p_role text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +declare + v_email text; +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + v_email := lower(trim(p_email)); + + update public.tenant_invites + set revoked_at = now(), + revoked_by = auth.uid() + where tenant_id = p_tenant_id + and lower(email) = v_email + and role = p_role + and accepted_at is null + and revoked_at is null; + + if not found then + raise exception 'invite_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_set_member_status(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_set_member_status(p_tenant_id uuid, p_member_user_id uuid, p_new_status text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + -- valida status (adapte aos seus valores reais) + if p_new_status not in ('active','inactive','suspended','invited') then + raise exception 'invalid_status: %', p_new_status; + end if; + + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + -- evita desativar a si mesmo (opcional) + if p_member_user_id = auth.uid() and p_new_status <> 'active' then + raise exception 'cannot_disable_self'; + end if; + + update public.tenant_members + set status = p_new_status + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: tenant_update_member_role(uuid, uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.tenant_update_member_role(p_tenant_id uuid, p_member_user_id uuid, p_new_role text) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + SET row_security TO 'off' + AS $$ +begin + -- exige auth + if auth.uid() is null then + raise exception 'not_authenticated'; + end if; + + -- valida role + if p_new_role not in ('tenant_admin','therapist','secretary','patient') then + raise exception 'invalid_role: %', p_new_role; + end if; + + -- somente tenant_admin ativo pode alterar role + if not public.is_tenant_admin(p_tenant_id) then + raise exception 'not_allowed'; + end if; + + -- evita o admin remover o pr??prio admin sem querer (opcional mas recomendado) + if p_member_user_id = auth.uid() and p_new_role <> 'tenant_admin' then + raise exception 'cannot_demote_self'; + end if; + + update public.tenant_members + set role = p_new_role + where tenant_id = p_tenant_id + and user_id = p_member_user_id; + + if not found then + raise exception 'membership_not_found'; + end if; +end; +$$; + + +-- +-- Name: toggle_plan(uuid); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.toggle_plan(owner uuid) RETURNS void + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + current_key text; + new_key text; +begin + select p.key into current_key + from subscriptions s + join plans p on p.id = s.plan_id + where s.owner_id = owner + and s.status = 'active'; + + new_key := case + when current_key = 'pro' then 'free' + else 'pro' + end; + + update subscriptions s + set plan_id = p.id + from plans p + where p.key = new_key + and s.owner_id = owner + and s.status = 'active'; +end; +$$; + + +-- +-- Name: transition_subscription(uuid, text, text, jsonb); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.transition_subscription(p_subscription_id uuid, p_to_status text, p_reason text DEFAULT NULL::text, p_metadata jsonb DEFAULT NULL::jsonb) RETURNS public.subscriptions + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +declare + v_sub public.subscriptions; + v_uid uuid; + v_is_allowed boolean := false; +begin + v_uid := auth.uid(); + + select * + into v_sub + from public.subscriptions + where id = p_subscription_id; + + if not found then + raise exception 'Assinatura n??o encontrada'; + end if; + + -- ===================================================== + -- ???? BLOCO DE AUTORIZA????O + -- ===================================================== + + -- 1) SaaS admin pode tudo + if is_saas_admin() then + v_is_allowed := true; + end if; + + -- 2) Assinatura pessoal (therapist) + if not v_is_allowed + and v_sub.tenant_id is null + and v_sub.user_id = v_uid then + v_is_allowed := true; + end if; + + -- 3) Assinatura de clinic (tenant) + if not v_is_allowed + and v_sub.tenant_id is not null then + + if exists ( + select 1 + from public.tenant_members tm + where tm.tenant_id = v_sub.tenant_id + and tm.user_id = v_uid + and tm.status = 'active' + and tm.role = 'tenant_admin' + ) then + v_is_allowed := true; + end if; + + end if; + + if not v_is_allowed then + raise exception 'Sem permiss??o para transicionar esta assinatura'; + end if; + + -- ===================================================== + -- ???? TRANSI????O + -- ===================================================== + + update public.subscriptions + set status = p_to_status, + updated_at = now(), + cancelled_at = case when p_to_status = 'cancelled' then now() else cancelled_at end, + suspended_at = case when p_to_status = 'suspended' then now() else suspended_at end, + past_due_since = case when p_to_status = 'past_due' then now() else past_due_since end, + expired_at = case when p_to_status = 'expired' then now() else expired_at end, + activated_at = case when p_to_status = 'active' then now() else activated_at end + where id = p_subscription_id + returning * into v_sub; + + -- ===================================================== + -- ???? EVENT LOG + -- ===================================================== + + insert into public.subscription_events ( + subscription_id, + owner_id, + event_type, + created_at, + created_by, + source, + reason, + metadata, + owner_type, + owner_ref + ) + values ( + v_sub.id, + coalesce(v_sub.tenant_id, v_sub.user_id), + 'status_changed', + now(), + v_uid, + 'manual_transition', + p_reason, + p_metadata, + case when v_sub.tenant_id is not null then 'tenant' else 'personal' end, + coalesce(v_sub.tenant_id, v_sub.user_id) + ); + + return v_sub; +end; +$$; + + +-- +-- Name: trg_fn_financial_records_auto_overdue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.trg_fn_financial_records_auto_overdue() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +BEGIN + IF NEW.status = 'pending' + AND NEW.due_date IS NOT NULL + AND NEW.due_date < CURRENT_DATE + THEN + NEW.status := 'overdue'; + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: trg_fn_patient_risco_timeline(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.trg_fn_patient_risco_timeline() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF OLD.risco_elevado IS DISTINCT FROM NEW.risco_elevado THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, + evento_tipo, titulo, descricao, icone_cor, + gerado_por, ocorrido_em + ) VALUES ( + NEW.id, NEW.tenant_id, + CASE WHEN NEW.risco_elevado THEN 'risco_sinalizado' ELSE 'risco_removido' END, + CASE WHEN NEW.risco_elevado THEN 'Risco elevado sinalizado' ELSE 'Sinalização de risco removida' END, + NEW.risco_nota, + CASE WHEN NEW.risco_elevado THEN 'red' ELSE 'green' END, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: trg_fn_patient_status_history(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.trg_fn_patient_status_history() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF (TG_OP = 'INSERT') OR (OLD.status IS DISTINCT FROM NEW.status) THEN + INSERT INTO public.patient_status_history ( + patient_id, tenant_id, + status_anterior, status_novo, + motivo, encaminhado_para, data_saida, + alterado_por, alterado_em + ) VALUES ( + NEW.id, NEW.tenant_id, + CASE WHEN TG_OP = 'INSERT' THEN NULL ELSE OLD.status END, + NEW.status, + NEW.motivo_saida, + NEW.encaminhado_para, + NEW.data_saida, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: trg_fn_patient_status_timeline(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.trg_fn_patient_status_timeline() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +BEGIN + IF (TG_OP = 'INSERT') OR (OLD.status IS DISTINCT FROM NEW.status) THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, + evento_tipo, titulo, descricao, icone_cor, + gerado_por, ocorrido_em + ) VALUES ( + NEW.id, NEW.tenant_id, + 'status_alterado', + 'Status alterado para ' || NEW.status, + CASE + WHEN TG_OP = 'INSERT' THEN 'Paciente cadastrado' + ELSE 'De ' || OLD.status || ' → ' || NEW.status || + CASE WHEN NEW.motivo_saida IS NOT NULL THEN ' · ' || NEW.motivo_saida ELSE '' END + END, + CASE NEW.status + WHEN 'Ativo' THEN 'green' + WHEN 'Alta' THEN 'blue' + WHEN 'Inativo' THEN 'gray' + WHEN 'Encaminhado' THEN 'amber' + WHEN 'Arquivado' THEN 'gray' + ELSE 'gray' + END, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + + +-- +-- Name: unstick_notification_queue(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.unstick_notification_queue() RETURNS integer + LANGUAGE plpgsql SECURITY DEFINER + AS $$ +DECLARE + v_unstuck integer; +BEGIN + UPDATE public.notification_queue + SET status = 'pendente', + attempts = attempts + 1, + last_error = 'Timeout: preso em processando por >10min', + next_retry_at = now() + interval '2 minutes' + WHERE status = 'processando' + AND updated_at < now() - interval '10 minutes'; + + GET DIAGNOSTICS v_unstuck = ROW_COUNT; + RETURN v_unstuck; +END; +$$; + + +-- +-- Name: update_payment_settings_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.update_payment_settings_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: update_professional_pricing_updated_at(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.update_professional_pricing_updated_at() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: user_has_feature(uuid, text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.user_has_feature(_user_id uuid, _feature text) RETURNS boolean + LANGUAGE sql STABLE + AS $$ + select exists ( + select 1 + from public.v_user_entitlements e + where e.user_id = _user_id + and e.feature_key = _feature + and e.allowed = true + ); +$$; + + +-- +-- Name: validate_support_session(text); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.validate_support_session(p_token text) RETURNS json + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'public' + AS $$ +DECLARE + v_session support_sessions; +BEGIN + IF p_token IS NULL OR length(trim(p_token)) < 32 THEN + RETURN json_build_object('valid', false, 'tenant_id', null); + END IF; + + SELECT * INTO v_session + FROM public.support_sessions + WHERE token = p_token + AND expires_at > now() + LIMIT 1; + + IF NOT FOUND THEN + RETURN json_build_object('valid', false, 'tenant_id', null); + END IF; + + RETURN json_build_object( + 'valid', true, + 'tenant_id', v_session.tenant_id + ); +END; +$$; + + +-- +-- Name: whoami(); Type: FUNCTION; Schema: public; Owner: - +-- + +CREATE FUNCTION public.whoami() RETURNS TABLE(uid uuid, role text) + LANGUAGE sql STABLE + AS $$ + select auth.uid() as uid, auth.role() as role; +$$; + + +-- +-- Name: apply_rls(jsonb, integer); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.apply_rls(wal jsonb, max_record_bytes integer DEFAULT (1024 * 1024)) RETURNS SETOF realtime.wal_rls + LANGUAGE plpgsql + AS $$ +declare +-- Regclass of the table e.g. public.notes +entity_ regclass = (quote_ident(wal ->> 'schema') || '.' || quote_ident(wal ->> 'table'))::regclass; + +-- I, U, D, T: insert, update ... +action realtime.action = ( + case wal ->> 'action' + when 'I' then 'INSERT' + when 'U' then 'UPDATE' + when 'D' then 'DELETE' + else 'ERROR' + end +); + +-- Is row level security enabled for the table +is_rls_enabled bool = relrowsecurity from pg_class where oid = entity_; + +subscriptions realtime.subscription[] = array_agg(subs) + from + realtime.subscription subs + where + subs.entity = entity_; + +-- Subscription vars +roles regrole[] = array_agg(distinct us.claims_role::text) + from + unnest(subscriptions) us; + +working_role regrole; +claimed_role regrole; +claims jsonb; + +subscription_id uuid; +subscription_has_access bool; +visible_to_subscription_ids uuid[] = '{}'; + +-- structured info for wal's columns +columns realtime.wal_column[]; +-- previous identity values for update/delete +old_columns realtime.wal_column[]; + +error_record_exceeds_max_size boolean = octet_length(wal::text) > max_record_bytes; + +-- Primary jsonb output for record +output jsonb; + +begin +perform set_config('role', null, true); + +columns = + array_agg( + ( + x->>'name', + x->>'type', + x->>'typeoid', + realtime.cast( + (x->'value') #>> '{}', + coalesce( + (x->>'typeoid')::regtype, -- null when wal2json version <= 2.4 + (x->>'type')::regtype + ) + ), + (pks ->> 'name') is not null, + true + )::realtime.wal_column + ) + from + jsonb_array_elements(wal -> 'columns') x + left join jsonb_array_elements(wal -> 'pk') pks + on (x ->> 'name') = (pks ->> 'name'); + +old_columns = + array_agg( + ( + x->>'name', + x->>'type', + x->>'typeoid', + realtime.cast( + (x->'value') #>> '{}', + coalesce( + (x->>'typeoid')::regtype, -- null when wal2json version <= 2.4 + (x->>'type')::regtype + ) + ), + (pks ->> 'name') is not null, + true + )::realtime.wal_column + ) + from + jsonb_array_elements(wal -> 'identity') x + left join jsonb_array_elements(wal -> 'pk') pks + on (x ->> 'name') = (pks ->> 'name'); + +for working_role in select * from unnest(roles) loop + + -- Update `is_selectable` for columns and old_columns + columns = + array_agg( + ( + c.name, + c.type_name, + c.type_oid, + c.value, + c.is_pkey, + pg_catalog.has_column_privilege(working_role, entity_, c.name, 'SELECT') + )::realtime.wal_column + ) + from + unnest(columns) c; + + old_columns = + array_agg( + ( + c.name, + c.type_name, + c.type_oid, + c.value, + c.is_pkey, + pg_catalog.has_column_privilege(working_role, entity_, c.name, 'SELECT') + )::realtime.wal_column + ) + from + unnest(old_columns) c; + + if action <> 'DELETE' and count(1) = 0 from unnest(columns) c where c.is_pkey then + return next ( + jsonb_build_object( + 'schema', wal ->> 'schema', + 'table', wal ->> 'table', + 'type', action + ), + is_rls_enabled, + -- subscriptions is already filtered by entity + (select array_agg(s.subscription_id) from unnest(subscriptions) as s where claims_role = working_role), + array['Error 400: Bad Request, no primary key'] + )::realtime.wal_rls; + + -- The claims role does not have SELECT permission to the primary key of entity + elsif action <> 'DELETE' and sum(c.is_selectable::int) <> count(1) from unnest(columns) c where c.is_pkey then + return next ( + jsonb_build_object( + 'schema', wal ->> 'schema', + 'table', wal ->> 'table', + 'type', action + ), + is_rls_enabled, + (select array_agg(s.subscription_id) from unnest(subscriptions) as s where claims_role = working_role), + array['Error 401: Unauthorized'] + )::realtime.wal_rls; + + else + output = jsonb_build_object( + 'schema', wal ->> 'schema', + 'table', wal ->> 'table', + 'type', action, + 'commit_timestamp', to_char( + ((wal ->> 'timestamp')::timestamptz at time zone 'utc'), + 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"' + ), + 'columns', ( + select + jsonb_agg( + jsonb_build_object( + 'name', pa.attname, + 'type', pt.typname + ) + order by pa.attnum asc + ) + from + pg_attribute pa + join pg_type pt + on pa.atttypid = pt.oid + where + attrelid = entity_ + and attnum > 0 + and pg_catalog.has_column_privilege(working_role, entity_, pa.attname, 'SELECT') + ) + ) + -- Add "record" key for insert and update + || case + when action in ('INSERT', 'UPDATE') then + jsonb_build_object( + 'record', + ( + select + jsonb_object_agg( + -- if unchanged toast, get column name and value from old record + coalesce((c).name, (oc).name), + case + when (c).name is null then (oc).value + else (c).value + end + ) + from + unnest(columns) c + full outer join unnest(old_columns) oc + on (c).name = (oc).name + where + coalesce((c).is_selectable, (oc).is_selectable) + and ( not error_record_exceeds_max_size or (octet_length((c).value::text) <= 64)) + ) + ) + else '{}'::jsonb + end + -- Add "old_record" key for update and delete + || case + when action = 'UPDATE' then + jsonb_build_object( + 'old_record', + ( + select jsonb_object_agg((c).name, (c).value) + from unnest(old_columns) c + where + (c).is_selectable + and ( not error_record_exceeds_max_size or (octet_length((c).value::text) <= 64)) + ) + ) + when action = 'DELETE' then + jsonb_build_object( + 'old_record', + ( + select jsonb_object_agg((c).name, (c).value) + from unnest(old_columns) c + where + (c).is_selectable + and ( not error_record_exceeds_max_size or (octet_length((c).value::text) <= 64)) + and ( not is_rls_enabled or (c).is_pkey ) -- if RLS enabled, we can't secure deletes so filter to pkey + ) + ) + else '{}'::jsonb + end; + + -- Create the prepared statement + if is_rls_enabled and action <> 'DELETE' then + if (select 1 from pg_prepared_statements where name = 'walrus_rls_stmt' limit 1) > 0 then + deallocate walrus_rls_stmt; + end if; + execute realtime.build_prepared_statement_sql('walrus_rls_stmt', entity_, columns); + end if; + + visible_to_subscription_ids = '{}'; + + for subscription_id, claims in ( + select + subs.subscription_id, + subs.claims + from + unnest(subscriptions) subs + where + subs.entity = entity_ + and subs.claims_role = working_role + and ( + realtime.is_visible_through_filters(columns, subs.filters) + or ( + action = 'DELETE' + and realtime.is_visible_through_filters(old_columns, subs.filters) + ) + ) + ) loop + + if not is_rls_enabled or action = 'DELETE' then + visible_to_subscription_ids = visible_to_subscription_ids || subscription_id; + else + -- Check if RLS allows the role to see the record + perform + -- Trim leading and trailing quotes from working_role because set_config + -- doesn't recognize the role as valid if they are included + set_config('role', trim(both '"' from working_role::text), true), + set_config('request.jwt.claims', claims::text, true); + + execute 'execute walrus_rls_stmt' into subscription_has_access; + + if subscription_has_access then + visible_to_subscription_ids = visible_to_subscription_ids || subscription_id; + end if; + end if; + end loop; + + perform set_config('role', null, true); + + return next ( + output, + is_rls_enabled, + visible_to_subscription_ids, + case + when error_record_exceeds_max_size then array['Error 413: Payload Too Large'] + else '{}' + end + )::realtime.wal_rls; + + end if; +end loop; + +perform set_config('role', null, true); +end; +$$; + + +-- +-- Name: broadcast_changes(text, text, text, text, text, record, record, text); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.broadcast_changes(topic_name text, event_name text, operation text, table_name text, table_schema text, new record, old record, level text DEFAULT 'ROW'::text) RETURNS void + LANGUAGE plpgsql + AS $$ +DECLARE + -- Declare a variable to hold the JSONB representation of the row + row_data jsonb := '{}'::jsonb; +BEGIN + IF level = 'STATEMENT' THEN + RAISE EXCEPTION 'function can only be triggered for each row, not for each statement'; + END IF; + -- Check the operation type and handle accordingly + IF operation = 'INSERT' OR operation = 'UPDATE' OR operation = 'DELETE' THEN + row_data := jsonb_build_object('old_record', OLD, 'record', NEW, 'operation', operation, 'table', table_name, 'schema', table_schema); + PERFORM realtime.send (row_data, event_name, topic_name); + ELSE + RAISE EXCEPTION 'Unexpected operation type: %', operation; + END IF; +EXCEPTION + WHEN OTHERS THEN + RAISE EXCEPTION 'Failed to process the row: %', SQLERRM; +END; + +$$; + + +-- +-- Name: build_prepared_statement_sql(text, regclass, realtime.wal_column[]); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.build_prepared_statement_sql(prepared_statement_name text, entity regclass, columns realtime.wal_column[]) RETURNS text + LANGUAGE sql + AS $$ + /* + Builds a sql string that, if executed, creates a prepared statement to + tests retrive a row from *entity* by its primary key columns. + Example + select realtime.build_prepared_statement_sql('public.notes', '{"id"}'::text[], '{"bigint"}'::text[]) + */ + select + 'prepare ' || prepared_statement_name || ' as + select + exists( + select + 1 + from + ' || entity || ' + where + ' || string_agg(quote_ident(pkc.name) || '=' || quote_nullable(pkc.value #>> '{}') , ' and ') || ' + )' + from + unnest(columns) pkc + where + pkc.is_pkey + group by + entity + $$; + + +-- +-- Name: cast(text, regtype); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime."cast"(val text, type_ regtype) RETURNS jsonb + LANGUAGE plpgsql IMMUTABLE + AS $$ + declare + res jsonb; + begin + execute format('select to_jsonb(%L::'|| type_::text || ')', val) into res; + return res; + end + $$; + + +-- +-- Name: check_equality_op(realtime.equality_op, regtype, text, text); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.check_equality_op(op realtime.equality_op, type_ regtype, val_1 text, val_2 text) RETURNS boolean + LANGUAGE plpgsql IMMUTABLE + AS $$ + /* + Casts *val_1* and *val_2* as type *type_* and check the *op* condition for truthiness + */ + declare + op_symbol text = ( + case + when op = 'eq' then '=' + when op = 'neq' then '!=' + when op = 'lt' then '<' + when op = 'lte' then '<=' + when op = 'gt' then '>' + when op = 'gte' then '>=' + when op = 'in' then '= any' + else 'UNKNOWN OP' + end + ); + res boolean; + begin + execute format( + 'select %L::'|| type_::text || ' ' || op_symbol + || ' ( %L::' + || ( + case + when op = 'in' then type_::text || '[]' + else type_::text end + ) + || ')', val_1, val_2) into res; + return res; + end; + $$; + + +-- +-- Name: is_visible_through_filters(realtime.wal_column[], realtime.user_defined_filter[]); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.is_visible_through_filters(columns realtime.wal_column[], filters realtime.user_defined_filter[]) RETURNS boolean + LANGUAGE sql IMMUTABLE + AS $_$ + /* + Should the record be visible (true) or filtered out (false) after *filters* are applied + */ + select + -- Default to allowed when no filters present + $2 is null -- no filters. this should not happen because subscriptions has a default + or array_length($2, 1) is null -- array length of an empty array is null + or bool_and( + coalesce( + realtime.check_equality_op( + op:=f.op, + type_:=coalesce( + col.type_oid::regtype, -- null when wal2json version <= 2.4 + col.type_name::regtype + ), + -- cast jsonb to text + val_1:=col.value #>> '{}', + val_2:=f.value + ), + false -- if null, filter does not match + ) + ) + from + unnest(filters) f + join unnest(columns) col + on f.column_name = col.name; + $_$; + + +-- +-- Name: list_changes(name, name, integer, integer); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.list_changes(publication name, slot_name name, max_changes integer, max_record_bytes integer) RETURNS SETOF realtime.wal_rls + LANGUAGE sql + SET log_min_messages TO 'fatal' + AS $$ + with pub as ( + select + concat_ws( + ',', + case when bool_or(pubinsert) then 'insert' else null end, + case when bool_or(pubupdate) then 'update' else null end, + case when bool_or(pubdelete) then 'delete' else null end + ) as w2j_actions, + coalesce( + string_agg( + realtime.quote_wal2json(format('%I.%I', schemaname, tablename)::regclass), + ',' + ) filter (where ppt.tablename is not null and ppt.tablename not like '% %'), + '' + ) w2j_add_tables + from + pg_publication pp + left join pg_publication_tables ppt + on pp.pubname = ppt.pubname + where + pp.pubname = publication + group by + pp.pubname + limit 1 + ), + w2j as ( + select + x.*, pub.w2j_add_tables + from + pub, + pg_logical_slot_get_changes( + slot_name, null, max_changes, + 'include-pk', 'true', + 'include-transaction', 'false', + 'include-timestamp', 'true', + 'include-type-oids', 'true', + 'format-version', '2', + 'actions', pub.w2j_actions, + 'add-tables', pub.w2j_add_tables + ) x + ) + select + xyz.wal, + xyz.is_rls_enabled, + xyz.subscription_ids, + xyz.errors + from + w2j, + realtime.apply_rls( + wal := w2j.data::jsonb, + max_record_bytes := max_record_bytes + ) xyz(wal, is_rls_enabled, subscription_ids, errors) + where + w2j.w2j_add_tables <> '' + and xyz.subscription_ids[1] is not null + $$; + + +-- +-- Name: quote_wal2json(regclass); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.quote_wal2json(entity regclass) RETURNS text + LANGUAGE sql IMMUTABLE STRICT + AS $$ + select + ( + select string_agg('' || ch,'') + from unnest(string_to_array(nsp.nspname::text, null)) with ordinality x(ch, idx) + where + not (x.idx = 1 and x.ch = '"') + and not ( + x.idx = array_length(string_to_array(nsp.nspname::text, null), 1) + and x.ch = '"' + ) + ) + || '.' + || ( + select string_agg('' || ch,'') + from unnest(string_to_array(pc.relname::text, null)) with ordinality x(ch, idx) + where + not (x.idx = 1 and x.ch = '"') + and not ( + x.idx = array_length(string_to_array(nsp.nspname::text, null), 1) + and x.ch = '"' + ) + ) + from + pg_class pc + join pg_namespace nsp + on pc.relnamespace = nsp.oid + where + pc.oid = entity + $$; + + +-- +-- Name: send(jsonb, text, text, boolean); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.send(payload jsonb, event text, topic text, private boolean DEFAULT true) RETURNS void + LANGUAGE plpgsql + AS $$ +DECLARE + generated_id uuid; + final_payload jsonb; +BEGIN + BEGIN + -- Generate a new UUID for the id + generated_id := gen_random_uuid(); + + -- Check if payload has an 'id' key, if not, add the generated UUID + IF payload ? 'id' THEN + final_payload := payload; + ELSE + final_payload := jsonb_set(payload, '{id}', to_jsonb(generated_id)); + END IF; + + -- Set the topic configuration + EXECUTE format('SET LOCAL realtime.topic TO %L', topic); + + -- Attempt to insert the message + INSERT INTO realtime.messages (id, payload, event, topic, private, extension) + VALUES (generated_id, final_payload, event, topic, private, 'broadcast'); + EXCEPTION + WHEN OTHERS THEN + -- Capture and notify the error + RAISE WARNING 'ErrorSendingBroadcastMessage: %', SQLERRM; + END; +END; +$$; + + +-- +-- Name: subscription_check_filters(); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.subscription_check_filters() RETURNS trigger + LANGUAGE plpgsql + AS $$ + /* + Validates that the user defined filters for a subscription: + - refer to valid columns that the claimed role may access + - values are coercable to the correct column type + */ + declare + col_names text[] = coalesce( + array_agg(c.column_name order by c.ordinal_position), + '{}'::text[] + ) + from + information_schema.columns c + where + format('%I.%I', c.table_schema, c.table_name)::regclass = new.entity + and pg_catalog.has_column_privilege( + (new.claims ->> 'role'), + format('%I.%I', c.table_schema, c.table_name)::regclass, + c.column_name, + 'SELECT' + ); + filter realtime.user_defined_filter; + col_type regtype; + + in_val jsonb; + begin + for filter in select * from unnest(new.filters) loop + -- Filtered column is valid + if not filter.column_name = any(col_names) then + raise exception 'invalid column for filter %', filter.column_name; + end if; + + -- Type is sanitized and safe for string interpolation + col_type = ( + select atttypid::regtype + from pg_catalog.pg_attribute + where attrelid = new.entity + and attname = filter.column_name + ); + if col_type is null then + raise exception 'failed to lookup type for column %', filter.column_name; + end if; + + -- Set maximum number of entries for in filter + if filter.op = 'in'::realtime.equality_op then + in_val = realtime.cast(filter.value, (col_type::text || '[]')::regtype); + if coalesce(jsonb_array_length(in_val), 0) > 100 then + raise exception 'too many values for `in` filter. Maximum 100'; + end if; + else + -- raises an exception if value is not coercable to type + perform realtime.cast(filter.value, col_type); + end if; + + end loop; + + -- Apply consistent order to filters so the unique constraint on + -- (subscription_id, entity, filters) can't be tricked by a different filter order + new.filters = coalesce( + array_agg(f order by f.column_name, f.op, f.value), + '{}' + ) from unnest(new.filters) f; + + return new; + end; + $$; + + +-- +-- Name: to_regrole(text); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.to_regrole(role_name text) RETURNS regrole + LANGUAGE sql IMMUTABLE + AS $$ select role_name::regrole $$; + + +-- +-- Name: topic(); Type: FUNCTION; Schema: realtime; Owner: - +-- + +CREATE FUNCTION realtime.topic() RETURNS text + LANGUAGE sql STABLE + AS $$ +select nullif(current_setting('realtime.topic', true), '')::text; +$$; + + +-- +-- Name: can_insert_object(text, text, uuid, jsonb); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.can_insert_object(bucketid text, name text, owner uuid, metadata jsonb) RETURNS void + LANGUAGE plpgsql + AS $$ +BEGIN + INSERT INTO "storage"."objects" ("bucket_id", "name", "owner", "metadata") VALUES (bucketid, name, owner, metadata); + -- hack to rollback the successful insert + RAISE sqlstate 'PT200' using + message = 'ROLLBACK', + detail = 'rollback successful insert'; +END +$$; + + +-- +-- Name: enforce_bucket_name_length(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.enforce_bucket_name_length() RETURNS trigger + LANGUAGE plpgsql + AS $$ +begin + if length(new.name) > 100 then + raise exception 'bucket name "%" is too long (% characters). Max is 100.', new.name, length(new.name); + end if; + return new; +end; +$$; + + +-- +-- Name: extension(text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.extension(name text) RETURNS text + LANGUAGE plpgsql + AS $$ +DECLARE +_parts text[]; +_filename text; +BEGIN + select string_to_array(name, '/') into _parts; + select _parts[array_length(_parts,1)] into _filename; + -- @todo return the last part instead of 2 + return reverse(split_part(reverse(_filename), '.', 1)); +END +$$; + + +-- +-- Name: filename(text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.filename(name text) RETURNS text + LANGUAGE plpgsql + AS $$ +DECLARE +_parts text[]; +BEGIN + select string_to_array(name, '/') into _parts; + return _parts[array_length(_parts,1)]; +END +$$; + + +-- +-- Name: foldername(text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.foldername(name text) RETURNS text[] + LANGUAGE plpgsql + AS $$ +DECLARE +_parts text[]; +BEGIN + select string_to_array(name, '/') into _parts; + return _parts[1:array_length(_parts,1)-1]; +END +$$; + + +-- +-- Name: get_common_prefix(text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.get_common_prefix(p_key text, p_prefix text, p_delimiter text) RETURNS text + LANGUAGE sql IMMUTABLE + AS $$ +SELECT CASE + WHEN position(p_delimiter IN substring(p_key FROM length(p_prefix) + 1)) > 0 + THEN left(p_key, length(p_prefix) + position(p_delimiter IN substring(p_key FROM length(p_prefix) + 1))) + ELSE NULL +END; +$$; + + +-- +-- Name: get_size_by_bucket(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.get_size_by_bucket() RETURNS TABLE(size bigint, bucket_id text) + LANGUAGE plpgsql + AS $$ +BEGIN + return query + select sum((metadata->>'size')::int) as size, obj.bucket_id + from "storage".objects as obj + group by obj.bucket_id; +END +$$; + + +-- +-- Name: list_multipart_uploads_with_delimiter(text, text, text, integer, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.list_multipart_uploads_with_delimiter(bucket_id text, prefix_param text, delimiter_param text, max_keys integer DEFAULT 100, next_key_token text DEFAULT ''::text, next_upload_token text DEFAULT ''::text) RETURNS TABLE(key text, id text, created_at timestamp with time zone) + LANGUAGE plpgsql + AS $_$ +BEGIN + RETURN QUERY EXECUTE + 'SELECT DISTINCT ON(key COLLATE "C") * from ( + SELECT + CASE + WHEN position($2 IN substring(key from length($1) + 1)) > 0 THEN + substring(key from 1 for length($1) + position($2 IN substring(key from length($1) + 1))) + ELSE + key + END AS key, id, created_at + FROM + storage.s3_multipart_uploads + WHERE + bucket_id = $5 AND + key ILIKE $1 || ''%'' AND + CASE + WHEN $4 != '''' AND $6 = '''' THEN + CASE + WHEN position($2 IN substring(key from length($1) + 1)) > 0 THEN + substring(key from 1 for length($1) + position($2 IN substring(key from length($1) + 1))) COLLATE "C" > $4 + ELSE + key COLLATE "C" > $4 + END + ELSE + true + END AND + CASE + WHEN $6 != '''' THEN + id COLLATE "C" > $6 + ELSE + true + END + ORDER BY + key COLLATE "C" ASC, created_at ASC) as e order by key COLLATE "C" LIMIT $3' + USING prefix_param, delimiter_param, max_keys, next_key_token, bucket_id, next_upload_token; +END; +$_$; + + +-- +-- Name: list_objects_with_delimiter(text, text, text, integer, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.list_objects_with_delimiter(_bucket_id text, prefix_param text, delimiter_param text, max_keys integer DEFAULT 100, start_after text DEFAULT ''::text, next_token text DEFAULT ''::text, sort_order text DEFAULT 'asc'::text) RETURNS TABLE(name text, id uuid, metadata jsonb, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone) + LANGUAGE plpgsql STABLE + AS $_$ +DECLARE + v_peek_name TEXT; + v_current RECORD; + v_common_prefix TEXT; + + -- Configuration + v_is_asc BOOLEAN; + v_prefix TEXT; + v_start TEXT; + v_upper_bound TEXT; + v_file_batch_size INT; + + -- Seek state + v_next_seek TEXT; + v_count INT := 0; + + -- Dynamic SQL for batch query only + v_batch_query TEXT; + +BEGIN + -- ======================================================================== + -- INITIALIZATION + -- ======================================================================== + v_is_asc := lower(coalesce(sort_order, 'asc')) = 'asc'; + v_prefix := coalesce(prefix_param, ''); + v_start := CASE WHEN coalesce(next_token, '') <> '' THEN next_token ELSE coalesce(start_after, '') END; + v_file_batch_size := LEAST(GREATEST(max_keys * 2, 100), 1000); + + -- Calculate upper bound for prefix filtering (bytewise, using COLLATE "C") + IF v_prefix = '' THEN + v_upper_bound := NULL; + ELSIF right(v_prefix, 1) = delimiter_param THEN + v_upper_bound := left(v_prefix, -1) || chr(ascii(delimiter_param) + 1); + ELSE + v_upper_bound := left(v_prefix, -1) || chr(ascii(right(v_prefix, 1)) + 1); + END IF; + + -- Build batch query (dynamic SQL - called infrequently, amortized over many rows) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" >= $2 ' || + 'AND o.name COLLATE "C" < $3 ORDER BY o.name COLLATE "C" ASC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" >= $2 ' || + 'ORDER BY o.name COLLATE "C" ASC LIMIT $4'; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" < $2 ' || + 'AND o.name COLLATE "C" >= $3 ORDER BY o.name COLLATE "C" DESC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND o.name COLLATE "C" < $2 ' || + 'ORDER BY o.name COLLATE "C" DESC LIMIT $4'; + END IF; + END IF; + + -- ======================================================================== + -- SEEK INITIALIZATION: Determine starting position + -- ======================================================================== + IF v_start = '' THEN + IF v_is_asc THEN + v_next_seek := v_prefix; + ELSE + -- DESC without cursor: find the last item in range + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_next_seek FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_prefix AND o.name COLLATE "C" < v_upper_bound + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix <> '' THEN + SELECT o.name INTO v_next_seek FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_prefix + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_next_seek FROM storage.objects o + WHERE o.bucket_id = _bucket_id + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + END IF; + + IF v_next_seek IS NOT NULL THEN + v_next_seek := v_next_seek || delimiter_param; + ELSE + RETURN; + END IF; + END IF; + ELSE + -- Cursor provided: determine if it refers to a folder or leaf + IF EXISTS ( + SELECT 1 FROM storage.objects o + WHERE o.bucket_id = _bucket_id + AND o.name COLLATE "C" LIKE v_start || delimiter_param || '%' + LIMIT 1 + ) THEN + -- Cursor refers to a folder + IF v_is_asc THEN + v_next_seek := v_start || chr(ascii(delimiter_param) + 1); + ELSE + v_next_seek := v_start || delimiter_param; + END IF; + ELSE + -- Cursor refers to a leaf object + IF v_is_asc THEN + v_next_seek := v_start || delimiter_param; + ELSE + v_next_seek := v_start; + END IF; + END IF; + END IF; + + -- ======================================================================== + -- MAIN LOOP: Hybrid peek-then-batch algorithm + -- Uses STATIC SQL for peek (hot path) and DYNAMIC SQL for batch + -- ======================================================================== + LOOP + EXIT WHEN v_count >= max_keys; + + -- STEP 1: PEEK using STATIC SQL (plan cached, very fast) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_next_seek AND o.name COLLATE "C" < v_upper_bound + ORDER BY o.name COLLATE "C" ASC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" >= v_next_seek + ORDER BY o.name COLLATE "C" ASC LIMIT 1; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" < v_next_seek AND o.name COLLATE "C" >= v_prefix + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix <> '' THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" < v_next_seek AND o.name COLLATE "C" >= v_prefix + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = _bucket_id AND o.name COLLATE "C" < v_next_seek + ORDER BY o.name COLLATE "C" DESC LIMIT 1; + END IF; + END IF; + + EXIT WHEN v_peek_name IS NULL; + + -- STEP 2: Check if this is a FOLDER or FILE + v_common_prefix := storage.get_common_prefix(v_peek_name, v_prefix, delimiter_param); + + IF v_common_prefix IS NOT NULL THEN + -- FOLDER: Emit and skip to next folder (no heap access needed) + name := rtrim(v_common_prefix, delimiter_param); + id := NULL; + updated_at := NULL; + created_at := NULL; + last_accessed_at := NULL; + metadata := NULL; + RETURN NEXT; + v_count := v_count + 1; + + -- Advance seek past the folder range + IF v_is_asc THEN + v_next_seek := left(v_common_prefix, -1) || chr(ascii(delimiter_param) + 1); + ELSE + v_next_seek := v_common_prefix; + END IF; + ELSE + -- FILE: Batch fetch using DYNAMIC SQL (overhead amortized over many rows) + -- For ASC: upper_bound is the exclusive upper limit (< condition) + -- For DESC: prefix is the inclusive lower limit (>= condition) + FOR v_current IN EXECUTE v_batch_query USING _bucket_id, v_next_seek, + CASE WHEN v_is_asc THEN COALESCE(v_upper_bound, v_prefix) ELSE v_prefix END, v_file_batch_size + LOOP + v_common_prefix := storage.get_common_prefix(v_current.name, v_prefix, delimiter_param); + + IF v_common_prefix IS NOT NULL THEN + -- Hit a folder: exit batch, let peek handle it + v_next_seek := v_current.name; + EXIT; + END IF; + + -- Emit file + name := v_current.name; + id := v_current.id; + updated_at := v_current.updated_at; + created_at := v_current.created_at; + last_accessed_at := v_current.last_accessed_at; + metadata := v_current.metadata; + RETURN NEXT; + v_count := v_count + 1; + + -- Advance seek past this file + IF v_is_asc THEN + v_next_seek := v_current.name || delimiter_param; + ELSE + v_next_seek := v_current.name; + END IF; + + EXIT WHEN v_count >= max_keys; + END LOOP; + END IF; + END LOOP; +END; +$_$; + + +-- +-- Name: operation(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.operation() RETURNS text + LANGUAGE plpgsql STABLE + AS $$ +BEGIN + RETURN current_setting('storage.operation', true); +END; +$$; + + +-- +-- Name: protect_delete(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.protect_delete() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + -- Check if storage.allow_delete_query is set to 'true' + IF COALESCE(current_setting('storage.allow_delete_query', true), 'false') != 'true' THEN + RAISE EXCEPTION 'Direct deletion from storage tables is not allowed. Use the Storage API instead.' + USING HINT = 'This prevents accidental data loss from orphaned objects.', + ERRCODE = '42501'; + END IF; + RETURN NULL; +END; +$$; + + +-- +-- Name: search(text, text, integer, integer, integer, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.search(prefix text, bucketname text, limits integer DEFAULT 100, levels integer DEFAULT 1, offsets integer DEFAULT 0, search text DEFAULT ''::text, sortcolumn text DEFAULT 'name'::text, sortorder text DEFAULT 'asc'::text) RETURNS TABLE(name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb) + LANGUAGE plpgsql STABLE + AS $_$ +DECLARE + v_peek_name TEXT; + v_current RECORD; + v_common_prefix TEXT; + v_delimiter CONSTANT TEXT := '/'; + + -- Configuration + v_limit INT; + v_prefix TEXT; + v_prefix_lower TEXT; + v_is_asc BOOLEAN; + v_order_by TEXT; + v_sort_order TEXT; + v_upper_bound TEXT; + v_file_batch_size INT; + + -- Dynamic SQL for batch query only + v_batch_query TEXT; + + -- Seek state + v_next_seek TEXT; + v_count INT := 0; + v_skipped INT := 0; +BEGIN + -- ======================================================================== + -- INITIALIZATION + -- ======================================================================== + v_limit := LEAST(coalesce(limits, 100), 1500); + v_prefix := coalesce(prefix, '') || coalesce(search, ''); + v_prefix_lower := lower(v_prefix); + v_is_asc := lower(coalesce(sortorder, 'asc')) = 'asc'; + v_file_batch_size := LEAST(GREATEST(v_limit * 2, 100), 1000); + + -- Validate sort column + CASE lower(coalesce(sortcolumn, 'name')) + WHEN 'name' THEN v_order_by := 'name'; + WHEN 'updated_at' THEN v_order_by := 'updated_at'; + WHEN 'created_at' THEN v_order_by := 'created_at'; + WHEN 'last_accessed_at' THEN v_order_by := 'last_accessed_at'; + ELSE v_order_by := 'name'; + END CASE; + + v_sort_order := CASE WHEN v_is_asc THEN 'asc' ELSE 'desc' END; + + -- ======================================================================== + -- NON-NAME SORTING: Use path_tokens approach (unchanged) + -- ======================================================================== + IF v_order_by != 'name' THEN + RETURN QUERY EXECUTE format( + $sql$ + WITH folders AS ( + SELECT path_tokens[$1] AS folder + FROM storage.objects + WHERE objects.name ILIKE $2 || '%%' + AND bucket_id = $3 + AND array_length(objects.path_tokens, 1) <> $1 + GROUP BY folder + ORDER BY folder %s + ) + (SELECT folder AS "name", + NULL::uuid AS id, + NULL::timestamptz AS updated_at, + NULL::timestamptz AS created_at, + NULL::timestamptz AS last_accessed_at, + NULL::jsonb AS metadata FROM folders) + UNION ALL + (SELECT path_tokens[$1] AS "name", + id, updated_at, created_at, last_accessed_at, metadata + FROM storage.objects + WHERE objects.name ILIKE $2 || '%%' + AND bucket_id = $3 + AND array_length(objects.path_tokens, 1) = $1 + ORDER BY %I %s) + LIMIT $4 OFFSET $5 + $sql$, v_sort_order, v_order_by, v_sort_order + ) USING levels, v_prefix, bucketname, v_limit, offsets; + RETURN; + END IF; + + -- ======================================================================== + -- NAME SORTING: Hybrid skip-scan with batch optimization + -- ======================================================================== + + -- Calculate upper bound for prefix filtering + IF v_prefix_lower = '' THEN + v_upper_bound := NULL; + ELSIF right(v_prefix_lower, 1) = v_delimiter THEN + v_upper_bound := left(v_prefix_lower, -1) || chr(ascii(v_delimiter) + 1); + ELSE + v_upper_bound := left(v_prefix_lower, -1) || chr(ascii(right(v_prefix_lower, 1)) + 1); + END IF; + + -- Build batch query (dynamic SQL - called infrequently, amortized over many rows) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" >= $2 ' || + 'AND lower(o.name) COLLATE "C" < $3 ORDER BY lower(o.name) COLLATE "C" ASC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" >= $2 ' || + 'ORDER BY lower(o.name) COLLATE "C" ASC LIMIT $4'; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" < $2 ' || + 'AND lower(o.name) COLLATE "C" >= $3 ORDER BY lower(o.name) COLLATE "C" DESC LIMIT $4'; + ELSE + v_batch_query := 'SELECT o.name, o.id, o.updated_at, o.created_at, o.last_accessed_at, o.metadata ' || + 'FROM storage.objects o WHERE o.bucket_id = $1 AND lower(o.name) COLLATE "C" < $2 ' || + 'ORDER BY lower(o.name) COLLATE "C" DESC LIMIT $4'; + END IF; + END IF; + + -- Initialize seek position + IF v_is_asc THEN + v_next_seek := v_prefix_lower; + ELSE + -- DESC: find the last item in range first (static SQL) + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_prefix_lower AND lower(o.name) COLLATE "C" < v_upper_bound + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix_lower <> '' THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_prefix_lower + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + END IF; + + IF v_peek_name IS NOT NULL THEN + v_next_seek := lower(v_peek_name) || v_delimiter; + ELSE + RETURN; + END IF; + END IF; + + -- ======================================================================== + -- MAIN LOOP: Hybrid peek-then-batch algorithm + -- Uses STATIC SQL for peek (hot path) and DYNAMIC SQL for batch + -- ======================================================================== + LOOP + EXIT WHEN v_count >= v_limit; + + -- STEP 1: PEEK using STATIC SQL (plan cached, very fast) + IF v_is_asc THEN + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_next_seek AND lower(o.name) COLLATE "C" < v_upper_bound + ORDER BY lower(o.name) COLLATE "C" ASC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" >= v_next_seek + ORDER BY lower(o.name) COLLATE "C" ASC LIMIT 1; + END IF; + ELSE + IF v_upper_bound IS NOT NULL THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" < v_next_seek AND lower(o.name) COLLATE "C" >= v_prefix_lower + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSIF v_prefix_lower <> '' THEN + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" < v_next_seek AND lower(o.name) COLLATE "C" >= v_prefix_lower + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + ELSE + SELECT o.name INTO v_peek_name FROM storage.objects o + WHERE o.bucket_id = bucketname AND lower(o.name) COLLATE "C" < v_next_seek + ORDER BY lower(o.name) COLLATE "C" DESC LIMIT 1; + END IF; + END IF; + + EXIT WHEN v_peek_name IS NULL; + + -- STEP 2: Check if this is a FOLDER or FILE + v_common_prefix := storage.get_common_prefix(lower(v_peek_name), v_prefix_lower, v_delimiter); + + IF v_common_prefix IS NOT NULL THEN + -- FOLDER: Handle offset, emit if needed, skip to next folder + IF v_skipped < offsets THEN + v_skipped := v_skipped + 1; + ELSE + name := split_part(rtrim(v_common_prefix, v_delimiter), v_delimiter, levels); + id := NULL; + updated_at := NULL; + created_at := NULL; + last_accessed_at := NULL; + metadata := NULL; + RETURN NEXT; + v_count := v_count + 1; + END IF; + + -- Advance seek past the folder range + IF v_is_asc THEN + v_next_seek := lower(left(v_common_prefix, -1)) || chr(ascii(v_delimiter) + 1); + ELSE + v_next_seek := lower(v_common_prefix); + END IF; + ELSE + -- FILE: Batch fetch using DYNAMIC SQL (overhead amortized over many rows) + -- For ASC: upper_bound is the exclusive upper limit (< condition) + -- For DESC: prefix_lower is the inclusive lower limit (>= condition) + FOR v_current IN EXECUTE v_batch_query + USING bucketname, v_next_seek, + CASE WHEN v_is_asc THEN COALESCE(v_upper_bound, v_prefix_lower) ELSE v_prefix_lower END, v_file_batch_size + LOOP + v_common_prefix := storage.get_common_prefix(lower(v_current.name), v_prefix_lower, v_delimiter); + + IF v_common_prefix IS NOT NULL THEN + -- Hit a folder: exit batch, let peek handle it + v_next_seek := lower(v_current.name); + EXIT; + END IF; + + -- Handle offset skipping + IF v_skipped < offsets THEN + v_skipped := v_skipped + 1; + ELSE + -- Emit file + name := split_part(v_current.name, v_delimiter, levels); + id := v_current.id; + updated_at := v_current.updated_at; + created_at := v_current.created_at; + last_accessed_at := v_current.last_accessed_at; + metadata := v_current.metadata; + RETURN NEXT; + v_count := v_count + 1; + END IF; + + -- Advance seek past this file + IF v_is_asc THEN + v_next_seek := lower(v_current.name) || v_delimiter; + ELSE + v_next_seek := lower(v_current.name); + END IF; + + EXIT WHEN v_count >= v_limit; + END LOOP; + END IF; + END LOOP; +END; +$_$; + + +-- +-- Name: search_by_timestamp(text, text, integer, integer, text, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.search_by_timestamp(p_prefix text, p_bucket_id text, p_limit integer, p_level integer, p_start_after text, p_sort_order text, p_sort_column text, p_sort_column_after text) RETURNS TABLE(key text, name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb) + LANGUAGE plpgsql STABLE + AS $_$ +DECLARE + v_cursor_op text; + v_query text; + v_prefix text; +BEGIN + v_prefix := coalesce(p_prefix, ''); + + IF p_sort_order = 'asc' THEN + v_cursor_op := '>'; + ELSE + v_cursor_op := '<'; + END IF; + + v_query := format($sql$ + WITH raw_objects AS ( + SELECT + o.name AS obj_name, + o.id AS obj_id, + o.updated_at AS obj_updated_at, + o.created_at AS obj_created_at, + o.last_accessed_at AS obj_last_accessed_at, + o.metadata AS obj_metadata, + storage.get_common_prefix(o.name, $1, '/') AS common_prefix + FROM storage.objects o + WHERE o.bucket_id = $2 + AND o.name COLLATE "C" LIKE $1 || '%%' + ), + -- Aggregate common prefixes (folders) + -- Both created_at and updated_at use MIN(obj_created_at) to match the old prefixes table behavior + aggregated_prefixes AS ( + SELECT + rtrim(common_prefix, '/') AS name, + NULL::uuid AS id, + MIN(obj_created_at) AS updated_at, + MIN(obj_created_at) AS created_at, + NULL::timestamptz AS last_accessed_at, + NULL::jsonb AS metadata, + TRUE AS is_prefix + FROM raw_objects + WHERE common_prefix IS NOT NULL + GROUP BY common_prefix + ), + leaf_objects AS ( + SELECT + obj_name AS name, + obj_id AS id, + obj_updated_at AS updated_at, + obj_created_at AS created_at, + obj_last_accessed_at AS last_accessed_at, + obj_metadata AS metadata, + FALSE AS is_prefix + FROM raw_objects + WHERE common_prefix IS NULL + ), + combined AS ( + SELECT * FROM aggregated_prefixes + UNION ALL + SELECT * FROM leaf_objects + ), + filtered AS ( + SELECT * + FROM combined + WHERE ( + $5 = '' + OR ROW( + date_trunc('milliseconds', %I), + name COLLATE "C" + ) %s ROW( + COALESCE(NULLIF($6, '')::timestamptz, 'epoch'::timestamptz), + $5 + ) + ) + ) + SELECT + split_part(name, '/', $3) AS key, + name, + id, + updated_at, + created_at, + last_accessed_at, + metadata + FROM filtered + ORDER BY + COALESCE(date_trunc('milliseconds', %I), 'epoch'::timestamptz) %s, + name COLLATE "C" %s + LIMIT $4 + $sql$, + p_sort_column, + v_cursor_op, + p_sort_column, + p_sort_order, + p_sort_order + ); + + RETURN QUERY EXECUTE v_query + USING v_prefix, p_bucket_id, p_level, p_limit, p_start_after, p_sort_column_after; +END; +$_$; + + +-- +-- Name: search_v2(text, text, integer, integer, text, text, text, text); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.search_v2(prefix text, bucket_name text, limits integer DEFAULT 100, levels integer DEFAULT 1, start_after text DEFAULT ''::text, sort_order text DEFAULT 'asc'::text, sort_column text DEFAULT 'name'::text, sort_column_after text DEFAULT ''::text) RETURNS TABLE(key text, name text, id uuid, updated_at timestamp with time zone, created_at timestamp with time zone, last_accessed_at timestamp with time zone, metadata jsonb) + LANGUAGE plpgsql STABLE + AS $$ +DECLARE + v_sort_col text; + v_sort_ord text; + v_limit int; +BEGIN + -- Cap limit to maximum of 1500 records + v_limit := LEAST(coalesce(limits, 100), 1500); + + -- Validate and normalize sort_order + v_sort_ord := lower(coalesce(sort_order, 'asc')); + IF v_sort_ord NOT IN ('asc', 'desc') THEN + v_sort_ord := 'asc'; + END IF; + + -- Validate and normalize sort_column + v_sort_col := lower(coalesce(sort_column, 'name')); + IF v_sort_col NOT IN ('name', 'updated_at', 'created_at') THEN + v_sort_col := 'name'; + END IF; + + -- Route to appropriate implementation + IF v_sort_col = 'name' THEN + -- Use list_objects_with_delimiter for name sorting (most efficient: O(k * log n)) + RETURN QUERY + SELECT + split_part(l.name, '/', levels) AS key, + l.name AS name, + l.id, + l.updated_at, + l.created_at, + l.last_accessed_at, + l.metadata + FROM storage.list_objects_with_delimiter( + bucket_name, + coalesce(prefix, ''), + '/', + v_limit, + start_after, + '', + v_sort_ord + ) l; + ELSE + -- Use aggregation approach for timestamp sorting + -- Not efficient for large datasets but supports correct pagination + RETURN QUERY SELECT * FROM storage.search_by_timestamp( + prefix, bucket_name, v_limit, levels, start_after, + v_sort_ord, v_sort_col, sort_column_after + ); + END IF; +END; +$$; + + +-- +-- Name: update_updated_at_column(); Type: FUNCTION; Schema: storage; Owner: - +-- + +CREATE FUNCTION storage.update_updated_at_column() RETURNS trigger + LANGUAGE plpgsql + AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + + +-- +-- Name: http_request(); Type: FUNCTION; Schema: supabase_functions; Owner: - +-- + +CREATE FUNCTION supabase_functions.http_request() RETURNS trigger + LANGUAGE plpgsql SECURITY DEFINER + SET search_path TO 'supabase_functions' + AS $$ + DECLARE + request_id bigint; + payload jsonb; + url text := TG_ARGV[0]::text; + method text := TG_ARGV[1]::text; + headers jsonb DEFAULT '{}'::jsonb; + params jsonb DEFAULT '{}'::jsonb; + timeout_ms integer DEFAULT 1000; + BEGIN + IF url IS NULL OR url = 'null' THEN + RAISE EXCEPTION 'url argument is missing'; + END IF; + + IF method IS NULL OR method = 'null' THEN + RAISE EXCEPTION 'method argument is missing'; + END IF; + + IF TG_ARGV[2] IS NULL OR TG_ARGV[2] = 'null' THEN + headers = '{"Content-Type": "application/json"}'::jsonb; + ELSE + headers = TG_ARGV[2]::jsonb; + END IF; + + IF TG_ARGV[3] IS NULL OR TG_ARGV[3] = 'null' THEN + params = '{}'::jsonb; + ELSE + params = TG_ARGV[3]::jsonb; + END IF; + + IF TG_ARGV[4] IS NULL OR TG_ARGV[4] = 'null' THEN + timeout_ms = 1000; + ELSE + timeout_ms = TG_ARGV[4]::integer; + END IF; + + CASE + WHEN method = 'GET' THEN + SELECT http_get INTO request_id FROM net.http_get( + url, + params, + headers, + timeout_ms + ); + WHEN method = 'POST' THEN + payload = jsonb_build_object( + 'old_record', OLD, + 'record', NEW, + 'type', TG_OP, + 'table', TG_TABLE_NAME, + 'schema', TG_TABLE_SCHEMA + ); + + SELECT http_post INTO request_id FROM net.http_post( + url, + payload, + params, + headers, + timeout_ms + ); + ELSE + RAISE EXCEPTION 'method argument % is invalid', method; + END CASE; + + INSERT INTO supabase_functions.hooks + (hook_table_id, hook_name, request_id) + VALUES + (TG_RELID, TG_NAME, request_id); + + RETURN NEW; + END +$$; + + +-- +-- Name: extensions; Type: TABLE; Schema: _realtime; Owner: - +-- + +CREATE TABLE _realtime.extensions ( + id uuid NOT NULL, + type text, + settings jsonb, + tenant_external_id text, + inserted_at timestamp(0) without time zone NOT NULL, + updated_at timestamp(0) without time zone NOT NULL +); + + +-- +-- Name: schema_migrations; Type: TABLE; Schema: _realtime; Owner: - +-- + +CREATE TABLE _realtime.schema_migrations ( + version bigint NOT NULL, + inserted_at timestamp(0) without time zone +); + + +-- +-- Name: tenants; Type: TABLE; Schema: _realtime; Owner: - +-- + +CREATE TABLE _realtime.tenants ( + id uuid NOT NULL, + name text, + external_id text, + jwt_secret text, + max_concurrent_users integer DEFAULT 200 NOT NULL, + inserted_at timestamp(0) without time zone NOT NULL, + updated_at timestamp(0) without time zone NOT NULL, + max_events_per_second integer DEFAULT 100 NOT NULL, + postgres_cdc_default text DEFAULT 'postgres_cdc_rls'::text, + max_bytes_per_second integer DEFAULT 100000 NOT NULL, + max_channels_per_client integer DEFAULT 100 NOT NULL, + max_joins_per_second integer DEFAULT 500 NOT NULL, + suspend boolean DEFAULT false, + jwt_jwks jsonb, + notify_private_alpha boolean DEFAULT false, + private_only boolean DEFAULT false NOT NULL, + migrations_ran integer DEFAULT 0, + broadcast_adapter character varying(255) DEFAULT 'gen_rpc'::character varying, + max_presence_events_per_second integer DEFAULT 1000, + max_payload_size_in_kb integer DEFAULT 3000, + CONSTRAINT jwt_secret_or_jwt_jwks_required CHECK (((jwt_secret IS NOT NULL) OR (jwt_jwks IS NOT NULL))) +); + + +-- +-- Name: audit_log_entries; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.audit_log_entries ( + instance_id uuid, + id uuid NOT NULL, + payload json, + created_at timestamp with time zone, + ip_address character varying(64) DEFAULT ''::character varying NOT NULL +); + + +-- +-- Name: TABLE audit_log_entries; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.audit_log_entries IS 'Auth: Audit trail for user actions.'; + + +-- +-- Name: flow_state; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.flow_state ( + id uuid NOT NULL, + user_id uuid, + auth_code text, + code_challenge_method auth.code_challenge_method, + code_challenge text, + provider_type text NOT NULL, + provider_access_token text, + provider_refresh_token text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + authentication_method text NOT NULL, + auth_code_issued_at timestamp with time zone, + invite_token text, + referrer text, + oauth_client_state_id uuid, + linking_target_id uuid, + email_optional boolean DEFAULT false NOT NULL +); + + +-- +-- Name: TABLE flow_state; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.flow_state IS 'Stores metadata for all OAuth/SSO login flows'; + + +-- +-- Name: identities; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.identities ( + provider_id text NOT NULL, + user_id uuid NOT NULL, + identity_data jsonb NOT NULL, + provider text NOT NULL, + last_sign_in_at timestamp with time zone, + created_at timestamp with time zone, + updated_at timestamp with time zone, + email text GENERATED ALWAYS AS (lower((identity_data ->> 'email'::text))) STORED, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: TABLE identities; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.identities IS 'Auth: Stores identities associated to a user.'; + + +-- +-- Name: COLUMN identities.email; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.identities.email IS 'Auth: Email is a generated column that references the optional email property in the identity_data'; + + +-- +-- Name: instances; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.instances ( + id uuid NOT NULL, + uuid uuid, + raw_base_config text, + created_at timestamp with time zone, + updated_at timestamp with time zone +); + + +-- +-- Name: TABLE instances; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.instances IS 'Auth: Manages users across multiple sites.'; + + +-- +-- Name: mfa_amr_claims; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.mfa_amr_claims ( + session_id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + authentication_method text NOT NULL, + id uuid NOT NULL +); + + +-- +-- Name: TABLE mfa_amr_claims; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.mfa_amr_claims IS 'auth: stores authenticator method reference claims for multi factor authentication'; + + +-- +-- Name: mfa_challenges; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.mfa_challenges ( + id uuid NOT NULL, + factor_id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + verified_at timestamp with time zone, + ip_address inet NOT NULL, + otp_code text, + web_authn_session_data jsonb +); + + +-- +-- Name: TABLE mfa_challenges; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.mfa_challenges IS 'auth: stores metadata about challenge requests made'; + + +-- +-- Name: mfa_factors; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.mfa_factors ( + id uuid NOT NULL, + user_id uuid NOT NULL, + friendly_name text, + factor_type auth.factor_type NOT NULL, + status auth.factor_status NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + secret text, + phone text, + last_challenged_at timestamp with time zone, + web_authn_credential jsonb, + web_authn_aaguid uuid, + last_webauthn_challenge_data jsonb +); + + +-- +-- Name: TABLE mfa_factors; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.mfa_factors IS 'auth: stores metadata about factors'; + + +-- +-- Name: COLUMN mfa_factors.last_webauthn_challenge_data; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.mfa_factors.last_webauthn_challenge_data IS 'Stores the latest WebAuthn challenge data including attestation/assertion for customer verification'; + + +-- +-- Name: oauth_authorizations; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_authorizations ( + id uuid NOT NULL, + authorization_id text NOT NULL, + client_id uuid NOT NULL, + user_id uuid, + redirect_uri text NOT NULL, + scope text NOT NULL, + state text, + resource text, + code_challenge text, + code_challenge_method auth.code_challenge_method, + response_type auth.oauth_response_type DEFAULT 'code'::auth.oauth_response_type NOT NULL, + status auth.oauth_authorization_status DEFAULT 'pending'::auth.oauth_authorization_status NOT NULL, + authorization_code text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + expires_at timestamp with time zone DEFAULT (now() + '00:03:00'::interval) NOT NULL, + approved_at timestamp with time zone, + nonce text, + CONSTRAINT oauth_authorizations_authorization_code_length CHECK ((char_length(authorization_code) <= 255)), + CONSTRAINT oauth_authorizations_code_challenge_length CHECK ((char_length(code_challenge) <= 128)), + CONSTRAINT oauth_authorizations_expires_at_future CHECK ((expires_at > created_at)), + CONSTRAINT oauth_authorizations_nonce_length CHECK ((char_length(nonce) <= 255)), + CONSTRAINT oauth_authorizations_redirect_uri_length CHECK ((char_length(redirect_uri) <= 2048)), + CONSTRAINT oauth_authorizations_resource_length CHECK ((char_length(resource) <= 2048)), + CONSTRAINT oauth_authorizations_scope_length CHECK ((char_length(scope) <= 4096)), + CONSTRAINT oauth_authorizations_state_length CHECK ((char_length(state) <= 4096)) +); + + +-- +-- Name: oauth_client_states; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_client_states ( + id uuid NOT NULL, + provider_type text NOT NULL, + code_verifier text, + created_at timestamp with time zone NOT NULL +); + + +-- +-- Name: TABLE oauth_client_states; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.oauth_client_states IS 'Stores OAuth states for third-party provider authentication flows where Supabase acts as the OAuth client.'; + + +-- +-- Name: oauth_clients; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_clients ( + id uuid NOT NULL, + client_secret_hash text, + registration_type auth.oauth_registration_type NOT NULL, + redirect_uris text NOT NULL, + grant_types text NOT NULL, + client_name text, + client_uri text, + logo_uri text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + client_type auth.oauth_client_type DEFAULT 'confidential'::auth.oauth_client_type NOT NULL, + token_endpoint_auth_method text NOT NULL, + CONSTRAINT oauth_clients_client_name_length CHECK ((char_length(client_name) <= 1024)), + CONSTRAINT oauth_clients_client_uri_length CHECK ((char_length(client_uri) <= 2048)), + CONSTRAINT oauth_clients_logo_uri_length CHECK ((char_length(logo_uri) <= 2048)), + CONSTRAINT oauth_clients_token_endpoint_auth_method_check CHECK ((token_endpoint_auth_method = ANY (ARRAY['client_secret_basic'::text, 'client_secret_post'::text, 'none'::text]))) +); + + +-- +-- Name: oauth_consents; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.oauth_consents ( + id uuid NOT NULL, + user_id uuid NOT NULL, + client_id uuid NOT NULL, + scopes text NOT NULL, + granted_at timestamp with time zone DEFAULT now() NOT NULL, + revoked_at timestamp with time zone, + CONSTRAINT oauth_consents_revoked_after_granted CHECK (((revoked_at IS NULL) OR (revoked_at >= granted_at))), + CONSTRAINT oauth_consents_scopes_length CHECK ((char_length(scopes) <= 2048)), + CONSTRAINT oauth_consents_scopes_not_empty CHECK ((char_length(TRIM(BOTH FROM scopes)) > 0)) +); + + +-- +-- Name: one_time_tokens; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.one_time_tokens ( + id uuid NOT NULL, + user_id uuid NOT NULL, + token_type auth.one_time_token_type NOT NULL, + token_hash text NOT NULL, + relates_to text NOT NULL, + created_at timestamp without time zone DEFAULT now() NOT NULL, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + CONSTRAINT one_time_tokens_token_hash_check CHECK ((char_length(token_hash) > 0)) +); + + +-- +-- Name: refresh_tokens; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.refresh_tokens ( + instance_id uuid, + id bigint NOT NULL, + token character varying(255), + user_id character varying(255), + revoked boolean, + created_at timestamp with time zone, + updated_at timestamp with time zone, + parent character varying(255), + session_id uuid +); + + +-- +-- Name: TABLE refresh_tokens; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.refresh_tokens IS 'Auth: Store of tokens used to refresh JWT tokens once they expire.'; + + +-- +-- Name: refresh_tokens_id_seq; Type: SEQUENCE; Schema: auth; Owner: - +-- + +CREATE SEQUENCE auth.refresh_tokens_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: refresh_tokens_id_seq; Type: SEQUENCE OWNED BY; Schema: auth; Owner: - +-- + +ALTER SEQUENCE auth.refresh_tokens_id_seq OWNED BY auth.refresh_tokens.id; + + +-- +-- Name: saml_providers; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.saml_providers ( + id uuid NOT NULL, + sso_provider_id uuid NOT NULL, + entity_id text NOT NULL, + metadata_xml text NOT NULL, + metadata_url text, + attribute_mapping jsonb, + created_at timestamp with time zone, + updated_at timestamp with time zone, + name_id_format text, + CONSTRAINT "entity_id not empty" CHECK ((char_length(entity_id) > 0)), + CONSTRAINT "metadata_url not empty" CHECK (((metadata_url = NULL::text) OR (char_length(metadata_url) > 0))), + CONSTRAINT "metadata_xml not empty" CHECK ((char_length(metadata_xml) > 0)) +); + + +-- +-- Name: TABLE saml_providers; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.saml_providers IS 'Auth: Manages SAML Identity Provider connections.'; + + +-- +-- Name: saml_relay_states; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.saml_relay_states ( + id uuid NOT NULL, + sso_provider_id uuid NOT NULL, + request_id text NOT NULL, + for_email text, + redirect_to text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + flow_state_id uuid, + CONSTRAINT "request_id not empty" CHECK ((char_length(request_id) > 0)) +); + + +-- +-- Name: TABLE saml_relay_states; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.saml_relay_states IS 'Auth: Contains SAML Relay State information for each Service Provider initiated login.'; + + +-- +-- Name: schema_migrations; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.schema_migrations ( + version character varying(255) NOT NULL +); + + +-- +-- Name: TABLE schema_migrations; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.schema_migrations IS 'Auth: Manages updates to the auth system.'; + + +-- +-- Name: sessions; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.sessions ( + id uuid NOT NULL, + user_id uuid NOT NULL, + created_at timestamp with time zone, + updated_at timestamp with time zone, + factor_id uuid, + aal auth.aal_level, + not_after timestamp with time zone, + refreshed_at timestamp without time zone, + user_agent text, + ip inet, + tag text, + oauth_client_id uuid, + refresh_token_hmac_key text, + refresh_token_counter bigint, + scopes text, + CONSTRAINT sessions_scopes_length CHECK ((char_length(scopes) <= 4096)) +); + + +-- +-- Name: TABLE sessions; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.sessions IS 'Auth: Stores session data associated to a user.'; + + +-- +-- Name: COLUMN sessions.not_after; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sessions.not_after IS 'Auth: Not after is a nullable column that contains a timestamp after which the session should be regarded as expired.'; + + +-- +-- Name: COLUMN sessions.refresh_token_hmac_key; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sessions.refresh_token_hmac_key IS 'Holds a HMAC-SHA256 key used to sign refresh tokens for this session.'; + + +-- +-- Name: COLUMN sessions.refresh_token_counter; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sessions.refresh_token_counter IS 'Holds the ID (counter) of the last issued refresh token.'; + + +-- +-- Name: sso_domains; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.sso_domains ( + id uuid NOT NULL, + sso_provider_id uuid NOT NULL, + domain text NOT NULL, + created_at timestamp with time zone, + updated_at timestamp with time zone, + CONSTRAINT "domain not empty" CHECK ((char_length(domain) > 0)) +); + + +-- +-- Name: TABLE sso_domains; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.sso_domains IS 'Auth: Manages SSO email address domain mapping to an SSO Identity Provider.'; + + +-- +-- Name: sso_providers; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.sso_providers ( + id uuid NOT NULL, + resource_id text, + created_at timestamp with time zone, + updated_at timestamp with time zone, + disabled boolean, + CONSTRAINT "resource_id not empty" CHECK (((resource_id = NULL::text) OR (char_length(resource_id) > 0))) +); + + +-- +-- Name: TABLE sso_providers; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.sso_providers IS 'Auth: Manages SSO identity provider information; see saml_providers for SAML.'; + + +-- +-- Name: COLUMN sso_providers.resource_id; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.sso_providers.resource_id IS 'Auth: Uniquely identifies a SSO provider according to a user-chosen resource ID (case insensitive), useful in infrastructure as code.'; + + +-- +-- Name: users; Type: TABLE; Schema: auth; Owner: - +-- + +CREATE TABLE auth.users ( + instance_id uuid, + id uuid NOT NULL, + aud character varying(255), + role character varying(255), + email character varying(255), + encrypted_password character varying(255), + email_confirmed_at timestamp with time zone, + invited_at timestamp with time zone, + confirmation_token character varying(255), + confirmation_sent_at timestamp with time zone, + recovery_token character varying(255), + recovery_sent_at timestamp with time zone, + email_change_token_new character varying(255), + email_change character varying(255), + email_change_sent_at timestamp with time zone, + last_sign_in_at timestamp with time zone, + raw_app_meta_data jsonb, + raw_user_meta_data jsonb, + is_super_admin boolean, + created_at timestamp with time zone, + updated_at timestamp with time zone, + phone text DEFAULT NULL::character varying, + phone_confirmed_at timestamp with time zone, + phone_change text DEFAULT ''::character varying, + phone_change_token character varying(255) DEFAULT ''::character varying, + phone_change_sent_at timestamp with time zone, + confirmed_at timestamp with time zone GENERATED ALWAYS AS (LEAST(email_confirmed_at, phone_confirmed_at)) STORED, + email_change_token_current character varying(255) DEFAULT ''::character varying, + email_change_confirm_status smallint DEFAULT 0, + banned_until timestamp with time zone, + reauthentication_token character varying(255) DEFAULT ''::character varying, + reauthentication_sent_at timestamp with time zone, + is_sso_user boolean DEFAULT false NOT NULL, + deleted_at timestamp with time zone, + is_anonymous boolean DEFAULT false NOT NULL, + CONSTRAINT users_email_change_confirm_status_check CHECK (((email_change_confirm_status >= 0) AND (email_change_confirm_status <= 2))) +); + + +-- +-- Name: TABLE users; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON TABLE auth.users IS 'Auth: Stores user login data within a secure schema.'; + + +-- +-- Name: COLUMN users.is_sso_user; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON COLUMN auth.users.is_sso_user IS 'Auth: Set this column to true when the account comes from SSO. These accounts can have duplicate emails.'; + + +-- +-- Name: _db_migrations; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public._db_migrations ( + id integer NOT NULL, + filename text NOT NULL, + hash text NOT NULL, + category text DEFAULT 'migration'::text NOT NULL, + applied_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: _db_migrations_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public._db_migrations_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: _db_migrations_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public._db_migrations_id_seq OWNED BY public._db_migrations.id; + + +-- +-- Name: addon_credits; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.addon_credits ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid, + addon_type text NOT NULL, + balance integer DEFAULT 0 NOT NULL, + total_purchased integer DEFAULT 0 NOT NULL, + total_consumed integer DEFAULT 0 NOT NULL, + low_balance_threshold integer DEFAULT 10, + low_balance_notified boolean DEFAULT false, + daily_limit integer, + hourly_limit integer, + daily_used integer DEFAULT 0, + hourly_used integer DEFAULT 0, + daily_reset_at timestamp with time zone, + hourly_reset_at timestamp with time zone, + from_number_override text, + expires_at timestamp with time zone, + is_active boolean DEFAULT true, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: TABLE addon_credits; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.addon_credits IS 'Saldo de cr??ditos de add-ons por tenant. Um registro por (tenant_id, addon_type).'; + + +-- +-- Name: addon_products; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.addon_products ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + slug text NOT NULL, + name text NOT NULL, + description text, + addon_type text NOT NULL, + icon text DEFAULT 'pi pi-box'::text, + credits_amount integer DEFAULT 0, + price_cents integer DEFAULT 0 NOT NULL, + currency text DEFAULT 'BRL'::text, + is_active boolean DEFAULT true, + is_visible boolean DEFAULT true, + sort_order integer DEFAULT 0, + metadata jsonb DEFAULT '{}'::jsonb, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + deleted_at timestamp with time zone +); + + +-- +-- Name: TABLE addon_products; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.addon_products IS 'Cat??logo de recursos extras vendidos pela plataforma (SMS, servidor, dom??nio, etc.)'; + + +-- +-- Name: addon_transactions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.addon_transactions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid, + addon_type text NOT NULL, + type text NOT NULL, + amount integer NOT NULL, + balance_before integer DEFAULT 0 NOT NULL, + balance_after integer DEFAULT 0 NOT NULL, + product_id uuid, + queue_id uuid, + description text, + admin_user_id uuid, + payment_method text, + payment_reference text, + price_cents integer, + currency text DEFAULT 'BRL'::text, + created_at timestamp with time zone DEFAULT now(), + metadata jsonb DEFAULT '{}'::jsonb +); + + +-- +-- Name: TABLE addon_transactions; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.addon_transactions IS 'Hist??rico de todas as transa????es de cr??ditos: compras, consumo, ajustes, reembolsos.'; + + +-- +-- Name: agenda_bloqueios; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_bloqueios ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + tipo text NOT NULL, + titulo text NOT NULL, + data_inicio date NOT NULL, + data_fim date, + hora_inicio time without time zone, + hora_fim time without time zone, + recorrente boolean DEFAULT false NOT NULL, + dia_semana smallint, + observacao text, + origem text DEFAULT 'manual'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT agenda_bloqueios_tipo_check CHECK ((tipo = ANY (ARRAY['feriado_nacional'::text, 'feriado_municipal'::text, 'bloqueio'::text]))) +); + + +-- +-- Name: agenda_configuracoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_configuracoes ( + owner_id uuid NOT NULL, + duracao_padrao_minutos integer DEFAULT 50 NOT NULL, + intervalo_padrao_minutos integer DEFAULT 0 NOT NULL, + timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL, + usar_horario_admin_custom boolean DEFAULT false NOT NULL, + admin_inicio_visualizacao time without time zone, + admin_fim_visualizacao time without time zone, + admin_slot_visual_minutos integer DEFAULT 30 NOT NULL, + online_ativo boolean DEFAULT false NOT NULL, + online_min_antecedencia_horas integer DEFAULT 24 NOT NULL, + online_max_dias_futuro integer DEFAULT 60 NOT NULL, + online_cancelar_ate_horas integer DEFAULT 12 NOT NULL, + online_reagendar_ate_horas integer DEFAULT 12 NOT NULL, + online_limite_agendamentos_futuros integer DEFAULT 1 NOT NULL, + online_modo text DEFAULT 'automatico'::text NOT NULL, + online_buffer_antes_min integer DEFAULT 0 NOT NULL, + online_buffer_depois_min integer DEFAULT 0 NOT NULL, + online_modalidade text DEFAULT 'ambos'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + usar_granularidade_custom boolean DEFAULT false NOT NULL, + granularidade_min integer, + setup_concluido boolean DEFAULT false NOT NULL, + setup_concluido_em timestamp with time zone, + agenda_view_mode text DEFAULT 'full_24h'::text NOT NULL, + agenda_custom_start time without time zone, + agenda_custom_end time without time zone, + session_duration_min integer DEFAULT 50 NOT NULL, + session_break_min integer DEFAULT 10 NOT NULL, + pausas_semanais jsonb DEFAULT '[]'::jsonb NOT NULL, + setup_clinica_concluido boolean DEFAULT false NOT NULL, + setup_clinica_concluido_em timestamp with time zone, + tenant_id uuid, + jornada_igual_todos boolean DEFAULT true, + slot_mode text DEFAULT 'fixed'::text NOT NULL, + atendimento_mode text DEFAULT 'particular'::text, + CONSTRAINT agenda_configuracoes_admin_slot_visual_minutos_check CHECK ((admin_slot_visual_minutos = ANY (ARRAY[5, 10, 15, 20, 30, 60]))), + CONSTRAINT agenda_configuracoes_atendimento_mode_check CHECK (((atendimento_mode IS NULL) OR (atendimento_mode = ANY (ARRAY['particular'::text, 'convenio'::text, 'ambos'::text])))), + CONSTRAINT agenda_configuracoes_check CHECK (((usar_horario_admin_custom = false) OR ((admin_inicio_visualizacao IS NOT NULL) AND (admin_fim_visualizacao IS NOT NULL) AND (admin_fim_visualizacao > admin_inicio_visualizacao)))), + CONSTRAINT agenda_configuracoes_duracao_padrao_minutos_check CHECK (((duracao_padrao_minutos >= 10) AND (duracao_padrao_minutos <= 240))), + CONSTRAINT agenda_configuracoes_granularidade_min_check CHECK (((granularidade_min IS NULL) OR (granularidade_min = ANY (ARRAY[5, 10, 15, 20, 30, 45, 50, 60])))), + CONSTRAINT agenda_configuracoes_intervalo_padrao_minutos_check CHECK (((intervalo_padrao_minutos >= 0) AND (intervalo_padrao_minutos <= 120))), + CONSTRAINT agenda_configuracoes_online_buffer_antes_min_check CHECK (((online_buffer_antes_min >= 0) AND (online_buffer_antes_min <= 120))), + CONSTRAINT agenda_configuracoes_online_buffer_depois_min_check CHECK (((online_buffer_depois_min >= 0) AND (online_buffer_depois_min <= 120))), + CONSTRAINT agenda_configuracoes_online_cancelar_ate_horas_check CHECK (((online_cancelar_ate_horas >= 0) AND (online_cancelar_ate_horas <= 720))), + CONSTRAINT agenda_configuracoes_online_limite_agendamentos_futuros_check CHECK (((online_limite_agendamentos_futuros >= 1) AND (online_limite_agendamentos_futuros <= 10))), + CONSTRAINT agenda_configuracoes_online_max_dias_futuro_check CHECK (((online_max_dias_futuro >= 1) AND (online_max_dias_futuro <= 365))), + CONSTRAINT agenda_configuracoes_online_min_antecedencia_horas_check CHECK (((online_min_antecedencia_horas >= 0) AND (online_min_antecedencia_horas <= 720))), + CONSTRAINT agenda_configuracoes_online_modalidade_check CHECK ((online_modalidade = ANY (ARRAY['online'::text, 'presencial'::text, 'ambos'::text]))), + CONSTRAINT agenda_configuracoes_online_modo_check CHECK ((online_modo = ANY (ARRAY['automatico'::text, 'aprovacao'::text]))), + CONSTRAINT agenda_configuracoes_online_reagendar_ate_horas_check CHECK (((online_reagendar_ate_horas >= 0) AND (online_reagendar_ate_horas <= 720))), + CONSTRAINT agenda_configuracoes_slot_mode_chk CHECK ((slot_mode = ANY (ARRAY['fixed'::text, 'dynamic'::text]))), + CONSTRAINT session_break_min_chk CHECK (((session_break_min >= 0) AND (session_break_min <= 60))), + CONSTRAINT session_duration_min_chk CHECK (((session_duration_min >= 10) AND (session_duration_min <= 240))) +); + + +-- +-- Name: COLUMN agenda_configuracoes.atendimento_mode; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agenda_configuracoes.atendimento_mode IS 'Modo de atendimento: particular | convenio | ambos'; + + +-- +-- Name: agenda_eventos; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_eventos ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tipo public.tipo_evento_agenda DEFAULT 'sessao'::public.tipo_evento_agenda NOT NULL, + status public.status_evento_agenda DEFAULT 'agendado'::public.status_evento_agenda NOT NULL, + titulo text, + observacoes text, + inicio_em timestamp with time zone NOT NULL, + fim_em timestamp with time zone NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + terapeuta_id uuid, + tenant_id uuid NOT NULL, + visibility_scope text DEFAULT 'public'::text NOT NULL, + mirror_of_event_id uuid, + mirror_source text, + patient_id uuid, + determined_commitment_id uuid, + link_online text, + titulo_custom text, + extra_fields jsonb, + recurrence_id uuid, + recurrence_date date, + modalidade text DEFAULT 'presencial'::text, + price numeric(10,2), + billing_contract_id uuid, + billed boolean DEFAULT false NOT NULL, + services_customized boolean DEFAULT false NOT NULL, + insurance_plan_id uuid, + insurance_guide_number text, + insurance_value numeric(10,2), + insurance_plan_service_id uuid, + CONSTRAINT agenda_eventos_check CHECK ((fim_em > inicio_em)) +); + + +-- +-- Name: COLUMN agenda_eventos.price; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agenda_eventos.price IS 'Valor da sess??o em BRL. Preenchido automaticamente pela tabela professional_pricing do profissional.'; + + +-- +-- Name: agenda_excecoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_excecoes ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + data date NOT NULL, + hora_inicio time without time zone, + hora_fim time without time zone, + tipo public.tipo_excecao_agenda NOT NULL, + motivo text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + status public.status_excecao_agenda DEFAULT 'ativo'::public.status_excecao_agenda NOT NULL, + fonte text DEFAULT 'manual'::text NOT NULL, + aplicavel_online boolean DEFAULT true NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_excecoes_check CHECK ((((hora_inicio IS NULL) AND (hora_fim IS NULL)) OR ((hora_inicio IS NOT NULL) AND (hora_fim IS NOT NULL) AND (hora_fim > hora_inicio)))), + CONSTRAINT agenda_excecoes_fonte_check CHECK ((fonte = ANY (ARRAY['manual'::text, 'feriado_google'::text, 'sistema'::text]))) +); + + +-- +-- Name: agenda_online_slots; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_online_slots ( + id bigint NOT NULL, + owner_id uuid NOT NULL, + weekday integer NOT NULL, + "time" time without time zone NOT NULL, + enabled boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_online_slots_weekday_check CHECK ((weekday = ANY (ARRAY[0, 1, 2, 3, 4, 5, 6]))) +); + + +-- +-- Name: agenda_online_slots_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.agenda_online_slots_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: agenda_online_slots_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.agenda_online_slots_id_seq OWNED BY public.agenda_online_slots.id; + + +-- +-- Name: agenda_regras_semanais; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_regras_semanais ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + dia_semana smallint NOT NULL, + hora_inicio time without time zone NOT NULL, + hora_fim time without time zone NOT NULL, + modalidade text DEFAULT 'ambos'::text NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_regras_semanais_check CHECK ((hora_fim > hora_inicio)), + CONSTRAINT agenda_regras_semanais_dia_semana_check CHECK (((dia_semana >= 0) AND (dia_semana <= 6))), + CONSTRAINT agenda_regras_semanais_modalidade_check CHECK (((modalidade = ANY (ARRAY['online'::text, 'presencial'::text, 'ambos'::text])) OR (modalidade IS NULL))) +); + + +-- +-- Name: agenda_slots_bloqueados_semanais; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_slots_bloqueados_semanais ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + dia_semana smallint NOT NULL, + hora_inicio time without time zone NOT NULL, + motivo text, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_slots_bloqueados_semanais_dia_semana_check CHECK (((dia_semana >= 0) AND (dia_semana <= 6))) +); + + +-- +-- Name: agenda_slots_regras; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agenda_slots_regras ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + dia_semana smallint NOT NULL, + passo_minutos integer NOT NULL, + offset_minutos integer DEFAULT 0 NOT NULL, + buffer_antes_min integer DEFAULT 0 NOT NULL, + buffer_depois_min integer DEFAULT 0 NOT NULL, + min_antecedencia_horas integer DEFAULT 0 NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL, + CONSTRAINT agenda_slots_regras_buffer_antes_min_check CHECK (((buffer_antes_min >= 0) AND (buffer_antes_min <= 240))), + CONSTRAINT agenda_slots_regras_buffer_depois_min_check CHECK (((buffer_depois_min >= 0) AND (buffer_depois_min <= 240))), + CONSTRAINT agenda_slots_regras_dia_semana_check CHECK (((dia_semana >= 0) AND (dia_semana <= 6))), + CONSTRAINT agenda_slots_regras_min_antecedencia_horas_check CHECK (((min_antecedencia_horas >= 0) AND (min_antecedencia_horas <= 720))), + CONSTRAINT agenda_slots_regras_offset_minutos_check CHECK (((offset_minutos >= 0) AND (offset_minutos <= 55))), + CONSTRAINT agenda_slots_regras_passo_minutos_check CHECK (((passo_minutos >= 5) AND (passo_minutos <= 240))) +); + + +-- +-- Name: agendador_configuracoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agendador_configuracoes ( + owner_id uuid NOT NULL, + tenant_id uuid, + ativo boolean DEFAULT false NOT NULL, + link_slug text, + imagem_fundo_url text, + imagem_header_url text, + logomarca_url text, + cor_primaria text DEFAULT '#4b6bff'::text, + nome_exibicao text, + endereco text, + botao_como_chegar_ativo boolean DEFAULT true NOT NULL, + maps_url text, + modo_aprovacao text DEFAULT 'aprovacao'::text NOT NULL, + modalidade text DEFAULT 'presencial'::text NOT NULL, + tipos_habilitados jsonb DEFAULT '["primeira", "retorno"]'::jsonb NOT NULL, + duracao_sessao_min integer DEFAULT 50 NOT NULL, + antecedencia_minima_horas integer DEFAULT 24 NOT NULL, + prazo_resposta_horas integer DEFAULT 2 NOT NULL, + reserva_horas integer DEFAULT 2 NOT NULL, + pagamento_obrigatorio boolean DEFAULT false NOT NULL, + pix_chave text, + pix_countdown_minutos integer DEFAULT 20 NOT NULL, + triagem_motivo boolean DEFAULT true NOT NULL, + triagem_como_conheceu boolean DEFAULT false NOT NULL, + verificacao_email boolean DEFAULT false NOT NULL, + exigir_aceite_lgpd boolean DEFAULT true NOT NULL, + mensagem_boas_vindas text, + texto_como_se_preparar text, + texto_termos_lgpd text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + pagamento_modo text DEFAULT 'sem_pagamento'::text NOT NULL, + pagamento_metodos_visiveis text[] DEFAULT '{}'::text[] NOT NULL, + CONSTRAINT agendador_configuracoes_antecedencia_check CHECK (((antecedencia_minima_horas >= 0) AND (antecedencia_minima_horas <= 720))), + CONSTRAINT agendador_configuracoes_duracao_check CHECK (((duracao_sessao_min >= 10) AND (duracao_sessao_min <= 240))), + CONSTRAINT agendador_configuracoes_modalidade_check CHECK ((modalidade = ANY (ARRAY['presencial'::text, 'online'::text, 'ambos'::text]))), + CONSTRAINT agendador_configuracoes_modo_check CHECK ((modo_aprovacao = ANY (ARRAY['automatico'::text, 'aprovacao'::text]))), + CONSTRAINT agendador_configuracoes_pix_countdown_check CHECK (((pix_countdown_minutos >= 5) AND (pix_countdown_minutos <= 120))), + CONSTRAINT agendador_configuracoes_prazo_check CHECK (((prazo_resposta_horas >= 1) AND (prazo_resposta_horas <= 72))), + CONSTRAINT agendador_configuracoes_reserva_check CHECK (((reserva_horas >= 1) AND (reserva_horas <= 48))) +); + + +-- +-- Name: COLUMN agendador_configuracoes.pagamento_modo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agendador_configuracoes.pagamento_modo IS 'sem_pagamento | pagar_na_hora | pix_antecipado'; + + +-- +-- Name: COLUMN agendador_configuracoes.pagamento_metodos_visiveis; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.agendador_configuracoes.pagamento_metodos_visiveis IS 'M??todos exibidos ao paciente quando pagamento_modo = pagar_na_hora. Ex: {pix, deposito, dinheiro, cartao, convenio}'; + + +-- +-- Name: agendador_solicitacoes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.agendador_solicitacoes ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + paciente_nome text NOT NULL, + paciente_sobrenome text, + paciente_email text NOT NULL, + paciente_celular text, + paciente_cpf text, + tipo text NOT NULL, + modalidade text NOT NULL, + data_solicitada date NOT NULL, + hora_solicitada time without time zone NOT NULL, + reservado_ate timestamp with time zone, + motivo text, + como_conheceu text, + pix_status text DEFAULT 'pendente'::text, + pix_pago_em timestamp with time zone, + status text DEFAULT 'pendente'::text NOT NULL, + recusado_motivo text, + autorizado_em timestamp with time zone, + autorizado_por uuid, + user_id uuid, + patient_id uuid, + evento_id uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT agendador_sol_modalidade_check CHECK ((modalidade = ANY (ARRAY['presencial'::text, 'online'::text]))), + CONSTRAINT agendador_sol_pix_check CHECK (((pix_status IS NULL) OR (pix_status = ANY (ARRAY['pendente'::text, 'pago'::text, 'expirado'::text])))), + CONSTRAINT agendador_sol_status_check CHECK ((status = ANY (ARRAY['pendente'::text, 'autorizado'::text, 'recusado'::text, 'expirado'::text, 'convertido'::text]))), + CONSTRAINT agendador_sol_tipo_check CHECK ((tipo = ANY (ARRAY['primeira'::text, 'retorno'::text, 'reagendar'::text]))) +); + + +-- +-- Name: billing_contracts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.billing_contracts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + patient_id uuid NOT NULL, + type text NOT NULL, + total_sessions integer, + sessions_used integer DEFAULT 0, + package_price numeric(10,2), + amount numeric(10,2), + billing_interval text, + active_from timestamp with time zone DEFAULT now(), + active_to timestamp with time zone, + status text DEFAULT 'active'::text NOT NULL, + created_at timestamp with time zone DEFAULT now(), + CONSTRAINT billing_contracts_interval_chk CHECK (((billing_interval IS NULL) OR (billing_interval = ANY (ARRAY['monthly'::text, 'weekly'::text])))), + CONSTRAINT billing_contracts_sess_used_chk CHECK (((sessions_used IS NULL) OR (sessions_used >= 0))), + CONSTRAINT billing_contracts_status_chk CHECK ((status = ANY (ARRAY['active'::text, 'completed'::text, 'cancelled'::text]))), + CONSTRAINT billing_contracts_total_sess_chk CHECK (((total_sessions IS NULL) OR (total_sessions > 0))), + CONSTRAINT billing_contracts_type_chk CHECK ((type = ANY (ARRAY['per_session'::text, 'package'::text, 'subscription'::text]))) +); + + +-- +-- Name: commitment_services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.commitment_services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + commitment_id uuid NOT NULL, + service_id uuid NOT NULL, + quantity integer DEFAULT 1 NOT NULL, + unit_price numeric(10,2) NOT NULL, + discount_pct numeric(5,2) DEFAULT 0, + discount_flat numeric(10,2) DEFAULT 0, + final_price numeric(10,2) NOT NULL, + created_at timestamp with time zone DEFAULT now(), + CONSTRAINT commitment_services_disc_flat_chk CHECK ((discount_flat >= (0)::numeric)), + CONSTRAINT commitment_services_disc_pct_chk CHECK (((discount_pct >= (0)::numeric) AND (discount_pct <= (100)::numeric))), + CONSTRAINT commitment_services_final_price_chk CHECK ((final_price >= (0)::numeric)), + CONSTRAINT commitment_services_quantity_chk CHECK ((quantity > 0)) +); + + +-- +-- Name: commitment_time_logs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.commitment_time_logs ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + commitment_id uuid NOT NULL, + calendar_event_id uuid, + source public.commitment_log_source DEFAULT 'manual'::public.commitment_log_source NOT NULL, + started_at timestamp with time zone NOT NULL, + ended_at timestamp with time zone NOT NULL, + minutes integer NOT NULL, + created_by uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: company_profiles; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.company_profiles ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + nome_fantasia text, + razao_social text, + tipo_empresa text, + cnpj text, + ie text, + im text, + cep text, + logradouro text, + numero text, + complemento text, + bairro text, + cidade text, + estado text, + email text, + telefone text, + site text, + logo_url text, + redes_sociais jsonb DEFAULT '[]'::jsonb NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: current_tenant_id; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.current_tenant_id AS + SELECT current_setting('request.jwt.claim.tenant_id'::text, true) AS current_setting; + + +-- +-- Name: determined_commitment_fields; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.determined_commitment_fields ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + commitment_id uuid NOT NULL, + key text NOT NULL, + label text NOT NULL, + field_type public.determined_field_type DEFAULT 'text'::public.determined_field_type NOT NULL, + required boolean DEFAULT false NOT NULL, + sort_order integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: determined_commitments; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.determined_commitments ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + created_by uuid, + is_native boolean DEFAULT false NOT NULL, + native_key text, + is_locked boolean DEFAULT false NOT NULL, + active boolean DEFAULT true NOT NULL, + name text NOT NULL, + description text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + bg_color text, + text_color text +); + + +-- +-- Name: dev_user_credentials; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.dev_user_credentials ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid, + email text NOT NULL, + password_dev text NOT NULL, + kind text DEFAULT 'custom'::text NOT NULL, + note text, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: email_layout_config; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.email_layout_config ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + header_config jsonb DEFAULT '{"layout": null, "content": "", "enabled": false}'::jsonb NOT NULL, + footer_config jsonb DEFAULT '{"layout": null, "content": "", "enabled": false}'::jsonb NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: email_templates_global; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.email_templates_global ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + domain text NOT NULL, + channel text DEFAULT 'email'::text NOT NULL, + subject text NOT NULL, + body_html text NOT NULL, + body_text text, + version integer DEFAULT 1 NOT NULL, + is_active boolean DEFAULT true NOT NULL, + variables jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: email_templates_tenant; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.email_templates_tenant ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid, + template_key text NOT NULL, + subject text, + body_html text, + body_text text, + enabled boolean DEFAULT true NOT NULL, + synced_version integer, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: entitlements_invalidation; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.entitlements_invalidation ( + owner_id uuid NOT NULL, + changed_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.features ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + description text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + descricao text DEFAULT ''::text NOT NULL, + name text DEFAULT ''::text NOT NULL +); + + +-- +-- Name: COLUMN features.descricao; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.features.descricao IS 'Descri????o humana da feature (exibi????o no admin e documenta????o).'; + + +-- +-- Name: feriados; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.feriados ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid, + owner_id uuid, + tipo text DEFAULT 'municipal'::text NOT NULL, + nome text NOT NULL, + data date NOT NULL, + cidade text, + estado text, + observacao text, + bloqueia_sessoes boolean DEFAULT false NOT NULL, + criado_em timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT feriados_tipo_check CHECK ((tipo = ANY (ARRAY['municipal'::text, 'personalizado'::text]))) +); + + +-- +-- Name: financial_categories; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.financial_categories ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid NOT NULL, + name text NOT NULL, + type public.financial_record_type DEFAULT 'receita'::public.financial_record_type NOT NULL, + color text DEFAULT '#6366f1'::text, + icon text DEFAULT 'pi pi-tag'::text, + sort_order integer DEFAULT 0, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: financial_exceptions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.financial_exceptions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid, + tenant_id uuid NOT NULL, + exception_type text NOT NULL, + charge_mode text NOT NULL, + charge_value numeric(10,2), + charge_pct numeric(5,2), + min_hours_notice integer, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + CONSTRAINT financial_exceptions_charge_chk CHECK ((charge_mode = ANY (ARRAY['none'::text, 'full'::text, 'fixed_fee'::text, 'percentage'::text]))), + CONSTRAINT financial_exceptions_type_chk CHECK ((exception_type = ANY (ARRAY['patient_no_show'::text, 'patient_cancellation'::text, 'professional_cancellation'::text]))) +); + + +-- +-- Name: global_notices; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.global_notices ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + title text, + message text DEFAULT ''::text NOT NULL, + variant text DEFAULT 'info'::text NOT NULL, + roles text[] DEFAULT '{}'::text[] NOT NULL, + contexts text[] DEFAULT '{}'::text[] NOT NULL, + starts_at timestamp with time zone, + ends_at timestamp with time zone, + is_active boolean DEFAULT true NOT NULL, + priority integer DEFAULT 0 NOT NULL, + dismissible boolean DEFAULT true NOT NULL, + persist_dismiss boolean DEFAULT true NOT NULL, + dismiss_scope text DEFAULT 'device'::text NOT NULL, + show_once boolean DEFAULT false NOT NULL, + max_views integer, + cooldown_minutes integer, + version integer DEFAULT 1 NOT NULL, + action_type text DEFAULT 'none'::text NOT NULL, + action_label text, + action_url text, + action_route text, + views_count integer DEFAULT 0 NOT NULL, + clicks_count integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + created_by uuid, + content_align text DEFAULT 'left'::text NOT NULL, + link_target text DEFAULT '_blank'::text NOT NULL, + CONSTRAINT global_notices_action_type_check CHECK ((action_type = ANY (ARRAY['none'::text, 'internal'::text, 'external'::text]))), + CONSTRAINT global_notices_content_align_check CHECK ((content_align = ANY (ARRAY['left'::text, 'center'::text, 'right'::text, 'justify'::text]))), + CONSTRAINT global_notices_dismiss_scope_check CHECK ((dismiss_scope = ANY (ARRAY['session'::text, 'device'::text, 'user'::text]))), + CONSTRAINT global_notices_link_target_check CHECK ((link_target = ANY (ARRAY['_blank'::text, '_self'::text, '_parent'::text, '_top'::text]))), + CONSTRAINT global_notices_variant_check CHECK ((variant = ANY (ARRAY['info'::text, 'success'::text, 'warning'::text, 'error'::text]))) +); + + +-- +-- Name: insurance_plan_services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.insurance_plan_services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + insurance_plan_id uuid NOT NULL, + name text NOT NULL, + value numeric(10,2) NOT NULL, + active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: insurance_plans; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.insurance_plans ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + name text NOT NULL, + notes text, + default_value numeric(10,2), + active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: login_carousel_slides; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.login_carousel_slides ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + title text NOT NULL, + body text NOT NULL, + icon text DEFAULT 'pi-star'::text NOT NULL, + ordem integer DEFAULT 0 NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: medicos; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.medicos ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + nome text NOT NULL, + crm text, + especialidade text, + telefone_profissional text, + telefone_pessoal text, + email text, + clinica text, + cidade text, + estado text DEFAULT 'SP'::text, + observacoes text, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: TABLE medicos; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.medicos IS 'Médicos e profissionais de referência cadastrados pelo terapeuta.'; + + +-- +-- Name: COLUMN medicos.owner_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.owner_id IS 'Terapeuta dono do cadastro (auth.uid()).'; + + +-- +-- Name: COLUMN medicos.tenant_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.tenant_id IS 'Tenant do terapeuta.'; + + +-- +-- Name: COLUMN medicos.nome; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.nome IS 'Nome completo do médico/profissional.'; + + +-- +-- Name: COLUMN medicos.crm; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.crm IS 'CRM com UF. Ex: 123456/SP. Único por owner_id.'; + + +-- +-- Name: COLUMN medicos.especialidade; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.especialidade IS 'Especialidade médica. Ex: Psiquiatria, Neurologia.'; + + +-- +-- Name: COLUMN medicos.telefone_profissional; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.telefone_profissional IS 'Telefone do consultório ou clínica.'; + + +-- +-- Name: COLUMN medicos.telefone_pessoal; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.telefone_pessoal IS 'Telefone pessoal / WhatsApp. Campo sensível.'; + + +-- +-- Name: COLUMN medicos.email; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.email IS 'E-mail profissional.'; + + +-- +-- Name: COLUMN medicos.clinica; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.clinica IS 'Nome da clínica ou hospital onde atua.'; + + +-- +-- Name: COLUMN medicos.cidade; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.cidade IS 'Cidade de atuação.'; + + +-- +-- Name: COLUMN medicos.estado; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.estado IS 'UF de atuação. Default SP.'; + + +-- +-- Name: COLUMN medicos.observacoes; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.observacoes IS 'Notas internas do terapeuta sobre o médico.'; + + +-- +-- Name: COLUMN medicos.ativo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.medicos.ativo IS 'Soft delete: false oculta da listagem.'; + + +-- +-- Name: module_features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.module_features ( + module_id uuid NOT NULL, + feature_id uuid NOT NULL, + enabled boolean DEFAULT true NOT NULL, + limits jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: modules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.modules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + name text NOT NULL, + description text, + is_active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: notice_dismissals; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notice_dismissals ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + notice_id uuid NOT NULL, + user_id uuid NOT NULL, + version integer DEFAULT 1 NOT NULL, + dismissed_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: notification_channels; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_channels ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + channel text NOT NULL, + provider text NOT NULL, + is_active boolean DEFAULT false NOT NULL, + display_name text, + sender_address text, + credentials jsonb DEFAULT '{}'::jsonb NOT NULL, + connection_status text DEFAULT 'disconnected'::text, + last_health_check timestamp with time zone, + metadata jsonb DEFAULT '{}'::jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + twilio_subaccount_sid text, + twilio_phone_number text, + twilio_phone_sid text, + webhook_url text, + cost_per_message_usd numeric(8,6) DEFAULT 0, + price_per_message_brl numeric(8,4) DEFAULT 0, + provisioned_at timestamp with time zone, + CONSTRAINT notification_channels_channel_check CHECK ((channel = ANY (ARRAY['whatsapp'::text, 'email'::text, 'sms'::text]))), + CONSTRAINT notification_channels_connection_status_check CHECK ((connection_status = ANY (ARRAY['connected'::text, 'disconnected'::text, 'connecting'::text, 'qr_pending'::text, 'error'::text]))), + CONSTRAINT notification_channels_provider_check CHECK ((provider = ANY (ARRAY['evolution_api'::text, 'meta_official'::text, 'twilio'::text, 'zenvia'::text, 'sendgrid'::text, 'resend'::text, 'smtp'::text, 'zapi'::text]))) +); + + +-- +-- Name: COLUMN notification_channels.twilio_subaccount_sid; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.twilio_subaccount_sid IS 'SID da subconta Twilio criada para este tenant'; + + +-- +-- Name: COLUMN notification_channels.twilio_phone_number; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.twilio_phone_number IS 'Número WhatsApp provisionado (E.164, ex: +5511999990000)'; + + +-- +-- Name: COLUMN notification_channels.twilio_phone_sid; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.twilio_phone_sid IS 'SID do número de telefone na subconta Twilio'; + + +-- +-- Name: COLUMN notification_channels.webhook_url; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.webhook_url IS 'URL do webhook configurada na Twilio para receber callbacks de status'; + + +-- +-- Name: COLUMN notification_channels.cost_per_message_usd; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.cost_per_message_usd IS 'Custo real Twilio por mensagem WhatsApp (USD)'; + + +-- +-- Name: COLUMN notification_channels.price_per_message_brl; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.price_per_message_brl IS 'Valor cobrado do tenant por mensagem (BRL, inclui margem SaaS)'; + + +-- +-- Name: COLUMN notification_channels.provisioned_at; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.notification_channels.provisioned_at IS 'Timestamp do provisionamento da subconta'; + + +-- +-- Name: notification_logs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_logs ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + queue_id uuid, + agenda_evento_id uuid, + patient_id uuid NOT NULL, + channel text NOT NULL, + template_key text NOT NULL, + schedule_key text, + recipient_address text NOT NULL, + resolved_message text, + resolved_vars jsonb, + status text NOT NULL, + provider text, + provider_message_id text, + provider_status text, + provider_response jsonb, + sent_at timestamp with time zone, + delivered_at timestamp with time zone, + read_at timestamp with time zone, + failed_at timestamp with time zone, + failure_reason text, + estimated_cost_brl numeric(8,4) DEFAULT 0, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT notification_logs_status_check CHECK ((status = ANY (ARRAY['sent'::text, 'delivered'::text, 'read'::text, 'failed'::text, 'bounced'::text, 'opted_out'::text]))) +); + + +-- +-- Name: notification_preferences; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_preferences ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + patient_id uuid NOT NULL, + whatsapp_opt_in boolean DEFAULT true NOT NULL, + email_opt_in boolean DEFAULT true NOT NULL, + sms_opt_in boolean DEFAULT false NOT NULL, + preferred_time_start time without time zone DEFAULT '08:00:00'::time without time zone, + preferred_time_end time without time zone DEFAULT '20:00:00'::time without time zone, + lgpd_consent_given boolean DEFAULT false NOT NULL, + lgpd_consent_date timestamp with time zone, + lgpd_consent_version text, + lgpd_consent_ip inet, + lgpd_opt_out_date timestamp with time zone, + lgpd_opt_out_reason text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone +); + + +-- +-- Name: notification_queue; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_queue ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + agenda_evento_id uuid, + patient_id uuid NOT NULL, + channel text NOT NULL, + template_key text NOT NULL, + schedule_key text NOT NULL, + resolved_vars jsonb DEFAULT '{}'::jsonb NOT NULL, + recipient_address text NOT NULL, + status text DEFAULT 'pendente'::text NOT NULL, + scheduled_at timestamp with time zone NOT NULL, + sent_at timestamp with time zone, + next_retry_at timestamp with time zone, + attempts integer DEFAULT 0 NOT NULL, + max_attempts integer DEFAULT 5 NOT NULL, + last_error text, + idempotency_key text NOT NULL, + provider_message_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT notification_queue_channel_check CHECK ((channel = ANY (ARRAY['whatsapp'::text, 'email'::text, 'sms'::text]))), + CONSTRAINT notification_queue_status_check CHECK ((status = ANY (ARRAY['pendente'::text, 'processando'::text, 'enviado'::text, 'falhou'::text, 'cancelado'::text, 'ignorado'::text]))) +); + + +-- +-- Name: notification_schedules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_schedules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + schedule_key text NOT NULL, + event_type text NOT NULL, + trigger_type text NOT NULL, + offset_minutes integer DEFAULT 0, + whatsapp_enabled boolean DEFAULT true NOT NULL, + email_enabled boolean DEFAULT true NOT NULL, + sms_enabled boolean DEFAULT false NOT NULL, + allowed_time_start time without time zone DEFAULT '08:00:00'::time without time zone, + allowed_time_end time without time zone DEFAULT '20:00:00'::time without time zone, + skip_weekends boolean DEFAULT false, + skip_holidays boolean DEFAULT false, + is_active boolean DEFAULT true NOT NULL, + sort_order integer DEFAULT 0, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + CONSTRAINT notification_schedules_event_type_check CHECK ((event_type = ANY (ARRAY['lembrete_sessao'::text, 'confirmacao_sessao'::text, 'cancelamento_sessao'::text, 'reagendamento'::text, 'cobranca_pendente'::text, 'boas_vindas_paciente'::text]))), + CONSTRAINT notification_schedules_trigger_type_check CHECK ((trigger_type = ANY (ARRAY['before_event'::text, 'after_event'::text, 'immediate'::text]))) +); + + +-- +-- Name: notification_templates; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notification_templates ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid, + owner_id uuid, + key text NOT NULL, + domain text NOT NULL, + channel text NOT NULL, + event_type text NOT NULL, + body_text text NOT NULL, + meta_template_name text, + meta_template_namespace text, + meta_components jsonb, + meta_status text DEFAULT 'draft'::text, + variables jsonb DEFAULT '[]'::jsonb, + version integer DEFAULT 1 NOT NULL, + is_active boolean DEFAULT true NOT NULL, + is_default boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + deleted_at timestamp with time zone, + CONSTRAINT notification_templates_channel_check CHECK ((channel = ANY (ARRAY['whatsapp'::text, 'sms'::text]))), + CONSTRAINT notification_templates_domain_check CHECK ((domain = ANY (ARRAY['session'::text, 'intake'::text, 'billing'::text, 'system'::text]))), + CONSTRAINT notification_templates_event_type_check CHECK ((event_type = ANY (ARRAY['lembrete_sessao'::text, 'confirmacao_sessao'::text, 'cancelamento_sessao'::text, 'reagendamento'::text, 'cobranca_pendente'::text, 'boas_vindas_paciente'::text, 'intake_recebido'::text, 'intake_aprovado'::text, 'intake_rejeitado'::text]))), + CONSTRAINT notification_templates_meta_status_check CHECK ((meta_status = ANY (ARRAY['draft'::text, 'pending_approval'::text, 'approved'::text, 'rejected'::text]))) +); + + +-- +-- Name: notifications; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.notifications ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + type text NOT NULL, + ref_id uuid, + ref_table text, + payload jsonb DEFAULT '{}'::jsonb NOT NULL, + read_at timestamp with time zone, + archived boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT notifications_type_check CHECK ((type = ANY (ARRAY['new_scheduling'::text, 'new_patient'::text, 'recurrence_alert'::text, 'session_status'::text]))) +); + + +-- +-- Name: plan_features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_features ( + plan_id uuid NOT NULL, + feature_id uuid NOT NULL, + enabled boolean DEFAULT true NOT NULL, + limits jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_modules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_modules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + module_id uuid NOT NULL, + status text DEFAULT 'active'::text NOT NULL, + settings jsonb, + provider text DEFAULT 'manual'::text NOT NULL, + provider_item_id text, + installed_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: owner_feature_entitlements; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.owner_feature_entitlements AS + WITH base AS ( + SELECT s.user_id AS owner_id, + f.key AS feature_key, + pf.limits, + 'plan'::text AS source + FROM ((public.subscriptions s + JOIN public.plan_features pf ON (((pf.plan_id = s.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))) + WHERE ((s.status = 'active'::text) AND (s.user_id IS NOT NULL)) + UNION ALL + SELECT tm.owner_id, + f.key AS feature_key, + mf.limits, + 'module'::text AS source + FROM (((public.tenant_modules tm + JOIN public.modules m ON (((m.id = tm.module_id) AND (m.is_active = true)))) + JOIN public.module_features mf ON (((mf.module_id = m.id) AND (mf.enabled = true)))) + JOIN public.features f ON ((f.id = mf.feature_id))) + WHERE ((tm.status = 'active'::text) AND (tm.owner_id IS NOT NULL)) + ) + SELECT owner_id, + feature_key, + array_agg(DISTINCT source) AS sources, + jsonb_agg(limits) FILTER (WHERE (limits IS NOT NULL)) AS limits_list + FROM base + GROUP BY owner_id, feature_key; + + +-- +-- Name: owner_users; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.owner_users ( + owner_id uuid NOT NULL, + user_id uuid NOT NULL, + role text DEFAULT 'admin'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: patient_contacts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_contacts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + patient_id uuid NOT NULL, + tenant_id uuid NOT NULL, + nome text NOT NULL, + tipo text NOT NULL, + relacao text, + telefone text, + email text, + cpf text, + especialidade text, + registro_profissional text, + is_primario boolean DEFAULT false NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT patient_contacts_tipo_check CHECK ((tipo = ANY (ARRAY['emergencia'::text, 'responsavel_legal'::text, 'profissional_saude'::text, 'outro'::text]))) +); + + +-- +-- Name: TABLE patient_contacts; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.patient_contacts IS 'Contatos vinculados ao paciente: emergência, responsável legal, outros profissionais de saúde'; + + +-- +-- Name: COLUMN patient_contacts.tipo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_contacts.tipo IS 'Categoria do contato: emergencia | responsavel_legal | profissional_saude | outro'; + + +-- +-- Name: COLUMN patient_contacts.is_primario; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_contacts.is_primario IS 'Contato de emergência principal — exibido em destaque no cadastro'; + + +-- +-- Name: patient_discounts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_discounts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + patient_id uuid NOT NULL, + discount_pct numeric(5,2) DEFAULT 0, + discount_flat numeric(10,2) DEFAULT 0, + reason text, + active boolean DEFAULT true NOT NULL, + active_from timestamp with time zone DEFAULT now(), + active_to timestamp with time zone, + created_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: patient_group_patient; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_group_patient ( + patient_group_id uuid NOT NULL, + patient_id uuid NOT NULL, + created_at timestamp with time zone DEFAULT now(), + tenant_id uuid NOT NULL +); + + +-- +-- Name: patient_groups; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_groups ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + nome text NOT NULL, + descricao text, + cor text, + is_active boolean DEFAULT true NOT NULL, + is_system boolean DEFAULT false NOT NULL, + owner_id uuid DEFAULT auth.uid() NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + therapist_id uuid, + tenant_id uuid NOT NULL +); + + +-- +-- Name: patient_intake_requests; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_intake_requests ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + token text NOT NULL, + consent boolean DEFAULT false NOT NULL, + status text DEFAULT 'new'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + converted_patient_id uuid, + rejected_reason text, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + cpf text, + rg text, + cep text, + nome_completo text, + email_principal text, + telefone text, + pais text, + cidade text, + estado text, + endereco text, + numero text, + bairro text, + complemento text, + data_nascimento date, + naturalidade text, + genero text, + estado_civil text, + onde_nos_conheceu text, + encaminhado_por text, + observacoes text, + notas_internas text, + email_alternativo text, + telefone_alternativo text, + profissao text, + escolaridade text, + nacionalidade text, + avatar_url text, + tenant_id uuid, + CONSTRAINT chk_intakes_status CHECK ((status = ANY (ARRAY['new'::text, 'converted'::text, 'rejected'::text]))) +); + + +-- +-- Name: patient_invites; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_invites ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + token text NOT NULL, + active boolean DEFAULT true NOT NULL, + expires_at timestamp with time zone, + max_uses integer, + uses integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid +); + + +-- +-- Name: patient_patient_tag; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_patient_tag ( + owner_id uuid NOT NULL, + patient_id uuid NOT NULL, + tag_id uuid NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + tenant_id uuid NOT NULL +); + + +-- +-- Name: patient_status_history; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_status_history ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + patient_id uuid NOT NULL, + tenant_id uuid NOT NULL, + status_anterior text, + status_novo text NOT NULL, + motivo text, + encaminhado_para text, + data_saida date, + alterado_por uuid, + alterado_em timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT psh_status_novo_check CHECK ((status_novo = ANY (ARRAY['Ativo'::text, 'Inativo'::text, 'Alta'::text, 'Encaminhado'::text, 'Arquivado'::text]))) +); + + +-- +-- Name: TABLE patient_status_history; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.patient_status_history IS 'Histórico imutável de todas as mudanças de status do paciente — não editar, apenas inserir'; + + +-- +-- Name: patient_support_contacts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_support_contacts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + patient_id uuid NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + nome text, + relacao text, + tipo text, + telefone text, + email text, + is_primario boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: TABLE patient_support_contacts; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.patient_support_contacts IS 'Rede de suporte do paciente. Exibida no card "Contatos & rede de suporte" do perfil.'; + + +-- +-- Name: COLUMN patient_support_contacts.tipo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_support_contacts.tipo IS 'emergencia | familiar | profissional_saude | amigo | outro'; + + +-- +-- Name: COLUMN patient_support_contacts.is_primario; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_support_contacts.is_primario IS 'true = badge vermelho "emergência" no perfil do paciente.'; + + +-- +-- Name: patient_tags; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_tags ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + nome text NOT NULL, + cor text, + is_padrao boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone, + tenant_id uuid NOT NULL +); + + +-- +-- Name: patient_timeline; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patient_timeline ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + patient_id uuid NOT NULL, + tenant_id uuid NOT NULL, + evento_tipo text NOT NULL, + titulo text NOT NULL, + descricao text, + icone_cor text DEFAULT 'gray'::text, + link_ref_tipo text, + link_ref_id uuid, + gerado_por uuid, + ocorrido_em timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT pt_evento_tipo_check CHECK ((evento_tipo = ANY (ARRAY['primeira_sessao'::text, 'sessao_realizada'::text, 'sessao_cancelada'::text, 'falta'::text, 'status_alterado'::text, 'risco_sinalizado'::text, 'risco_removido'::text, 'documento_assinado'::text, 'documento_adicionado'::text, 'escala_respondida'::text, 'escala_enviada'::text, 'pagamento_vencido'::text, 'pagamento_recebido'::text, 'tarefa_combinada'::text, 'contato_adicionado'::text, 'prontuario_editado'::text, 'nota_adicionada'::text, 'manual'::text]))), + CONSTRAINT pt_icone_cor_check CHECK ((icone_cor = ANY (ARRAY['green'::text, 'blue'::text, 'amber'::text, 'red'::text, 'gray'::text, 'purple'::text]))) +); + + +-- +-- Name: TABLE patient_timeline; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.patient_timeline IS 'Feed cronológico de eventos do paciente — alimentado por triggers e inserções manuais'; + + +-- +-- Name: COLUMN patient_timeline.link_ref_tipo; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_timeline.link_ref_tipo IS 'Tipo da entidade referenciada (polimórfico): agenda_evento | financial_record | documento | escala'; + + +-- +-- Name: COLUMN patient_timeline.link_ref_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patient_timeline.link_ref_id IS 'ID da entidade referenciada — sem FK formal para suportar múltiplos tipos'; + + +-- +-- Name: patients; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.patients ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + nome_completo text NOT NULL, + email_principal text, + telefone text, + created_at timestamp with time zone DEFAULT now(), + owner_id uuid, + avatar_url text, + status text DEFAULT 'Ativo'::text, + last_attended_at timestamp with time zone, + is_native boolean DEFAULT false, + naturalidade text, + data_nascimento date, + rg text, + cpf text, + identification_color text, + genero text, + estado_civil text, + email_alternativo text, + pais text DEFAULT 'Brasil'::text, + cep text, + cidade text, + estado text, + endereco text, + numero text, + bairro text, + complemento text, + escolaridade text, + profissao text, + nome_parente text, + grau_parentesco text, + telefone_alternativo text, + onde_nos_conheceu text, + encaminhado_por text, + nome_responsavel text, + telefone_responsavel text, + cpf_responsavel text, + observacao_responsavel text, + cobranca_no_responsavel boolean DEFAULT false, + observacoes text, + notas_internas text, + updated_at timestamp with time zone DEFAULT now(), + telefone_parente text, + tenant_id uuid NOT NULL, + responsible_member_id uuid NOT NULL, + user_id uuid, + patient_scope text DEFAULT 'clinic'::text NOT NULL, + therapist_member_id uuid, + nome_social text, + pronomes text, + etnia text, + religiao text, + faixa_renda text, + canal_preferido text DEFAULT 'whatsapp'::text, + horario_contato_inicio time without time zone DEFAULT '08:00:00'::time without time zone, + horario_contato_fim time without time zone DEFAULT '20:00:00'::time without time zone, + idioma text DEFAULT 'pt-BR'::text, + origem text, + metodo_pagamento_preferido text, + motivo_saida text, + data_saida date, + encaminhado_para text, + risco_elevado boolean DEFAULT false NOT NULL, + risco_nota text, + risco_sinalizado_em timestamp with time zone, + risco_sinalizado_por uuid, + horario_contato text, + convenio text, + convenio_id uuid, + CONSTRAINT cpf_responsavel_format_check CHECK (((cpf_responsavel IS NULL) OR (cpf_responsavel ~ '^\d{11}$'::text))), + CONSTRAINT patients_cpf_format_check CHECK (((cpf IS NULL) OR (cpf ~ '^\d{11}$'::text))), + CONSTRAINT patients_faixa_renda_check CHECK (((faixa_renda IS NULL) OR (faixa_renda = ANY (ARRAY['ate_1sm'::text, '1_3sm'::text, '3_6sm'::text, '6_10sm'::text, 'acima_10sm'::text, 'nao_informado'::text])))), + CONSTRAINT patients_metodo_pagamento_check CHECK (((metodo_pagamento_preferido IS NULL) OR (metodo_pagamento_preferido = ANY (ARRAY['pix'::text, 'cartao'::text, 'dinheiro'::text, 'deposito'::text, 'convenio'::text])))), + CONSTRAINT patients_risco_consistency_check CHECK (((risco_elevado = false) OR ((risco_elevado = true) AND (risco_nota IS NOT NULL) AND (risco_sinalizado_por IS NOT NULL)))), + CONSTRAINT patients_status_check CHECK ((status = ANY (ARRAY['Ativo'::text, 'Em espera'::text, 'Inativo'::text, 'Alta'::text, 'Encaminhado'::text, 'Arquivado'::text]))) +); + + +-- +-- Name: COLUMN patients.avatar_url; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.avatar_url IS 'URL p??blica da imagem de avatar armazenada no Supabase Storage'; + + +-- +-- Name: COLUMN patients.nome_social; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.nome_social IS 'Nome social / como prefere ser chamado(a) no atendimento.'; + + +-- +-- Name: COLUMN patients.pronomes; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.pronomes IS 'Pronomes de tratamento. Ex: ela/dela, ele/dele. Exibido no header do perfil.'; + + +-- +-- Name: COLUMN patients.etnia; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.etnia IS 'Etnia / raça autodeclarada. Exibida no card "Dados pessoais".'; + + +-- +-- Name: COLUMN patients.religiao; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.religiao IS 'Religião ou espiritualidade (opcional, relevante clinicamente)'; + + +-- +-- Name: COLUMN patients.faixa_renda; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.faixa_renda IS 'Faixa de renda em salários mínimos — usado para precificação solidária'; + + +-- +-- Name: COLUMN patients.canal_preferido; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.canal_preferido IS 'Canal preferido de contato. Ex: WhatsApp, Telefone, E-mail.'; + + +-- +-- Name: COLUMN patients.horario_contato_inicio; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.horario_contato_inicio IS 'Início da janela de horário preferida para contato'; + + +-- +-- Name: COLUMN patients.horario_contato_fim; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.horario_contato_fim IS 'Fim da janela de horário preferida para contato'; + + +-- +-- Name: COLUMN patients.origem; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.origem IS 'Como o paciente chegou: indicacao, agendador, redes_sociais, encaminhamento, outro'; + + +-- +-- Name: COLUMN patients.metodo_pagamento_preferido; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.metodo_pagamento_preferido IS 'Método de pagamento preferido. Ex: PIX, Cartão crédito. Exibido no card Origem.'; + + +-- +-- Name: COLUMN patients.motivo_saida; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.motivo_saida IS 'Motivo de encerramento do acompanhamento. Exibido no card Origem quando preenchido.'; + + +-- +-- Name: COLUMN patients.data_saida; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.data_saida IS 'Data em que o paciente foi desligado/encaminhado'; + + +-- +-- Name: COLUMN patients.encaminhado_para; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.encaminhado_para IS 'Nome ou serviço para onde o paciente foi encaminhado'; + + +-- +-- Name: COLUMN patients.risco_elevado; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.risco_elevado IS 'Flag de atenção clínica — exibe alerta no topo do cadastro e prontuário'; + + +-- +-- Name: COLUMN patients.risco_nota; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.risco_nota IS 'Descrição do risco (obrigatória quando risco_elevado = true)'; + + +-- +-- Name: COLUMN patients.risco_sinalizado_em; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.risco_sinalizado_em IS 'Timestamp em que o risco foi sinalizado'; + + +-- +-- Name: COLUMN patients.risco_sinalizado_por; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.risco_sinalizado_por IS 'Usuário que sinalizou o risco'; + + +-- +-- Name: COLUMN patients.horario_contato; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.horario_contato IS 'Horário preferido para contato. Ex: 08h–18h.'; + + +-- +-- Name: COLUMN patients.convenio; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.convenio IS 'Nome do convênio para exibição (badge azul no header). Derivado de convenio_id.'; + + +-- +-- Name: COLUMN patients.convenio_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.patients.convenio_id IS 'FK para insurance_plans.id. Vincula o paciente ao convênio cadastrado.'; + + +-- +-- Name: payment_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.payment_settings ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid, + pix_ativo boolean DEFAULT false NOT NULL, + pix_tipo text DEFAULT 'cpf'::text NOT NULL, + pix_chave text DEFAULT ''::text NOT NULL, + pix_nome_titular text DEFAULT ''::text NOT NULL, + deposito_ativo boolean DEFAULT false NOT NULL, + deposito_banco text DEFAULT ''::text NOT NULL, + deposito_agencia text DEFAULT ''::text NOT NULL, + deposito_conta text DEFAULT ''::text NOT NULL, + deposito_tipo_conta text DEFAULT 'corrente'::text NOT NULL, + deposito_titular text DEFAULT ''::text NOT NULL, + deposito_cpf_cnpj text DEFAULT ''::text NOT NULL, + dinheiro_ativo boolean DEFAULT false NOT NULL, + cartao_ativo boolean DEFAULT false NOT NULL, + cartao_instrucao text DEFAULT ''::text NOT NULL, + convenio_ativo boolean DEFAULT false NOT NULL, + convenio_lista text DEFAULT ''::text NOT NULL, + observacoes_pagamento text DEFAULT ''::text NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: plan_prices; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_prices ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + plan_id uuid NOT NULL, + currency text DEFAULT 'BRL'::text NOT NULL, + "interval" text NOT NULL, + amount_cents integer NOT NULL, + is_active boolean DEFAULT true NOT NULL, + active_from timestamp with time zone DEFAULT now() NOT NULL, + active_to timestamp with time zone, + source text DEFAULT 'manual'::text NOT NULL, + provider text, + provider_price_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT plan_prices_amount_cents_check CHECK ((amount_cents >= 0)), + CONSTRAINT plan_prices_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text]))) +); + + +-- +-- Name: TABLE plan_prices; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.plan_prices IS 'Hist??rico de pre??os por plano (fonte: manual/gateway).'; + + +-- +-- Name: plan_public; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_public ( + plan_id uuid NOT NULL, + public_name text DEFAULT ''::text NOT NULL, + public_description text DEFAULT ''::text NOT NULL, + badge text, + is_featured boolean DEFAULT false NOT NULL, + is_visible boolean DEFAULT true NOT NULL, + sort_order integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: TABLE plan_public; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.plan_public IS 'Configura????o de vitrine (p??gina p??blica) dos planos.'; + + +-- +-- Name: plan_public_bullets; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plan_public_bullets ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + plan_id uuid NOT NULL, + text text NOT NULL, + sort_order integer DEFAULT 0 NOT NULL, + highlight boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: plans; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.plans ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + key text NOT NULL, + name text NOT NULL, + description text, + is_active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + price_cents integer DEFAULT 0 NOT NULL, + currency text DEFAULT 'BRL'::text NOT NULL, + billing_interval text DEFAULT 'month'::text NOT NULL, + target text, + max_supervisees integer, + CONSTRAINT plans_target_check CHECK ((target = ANY (ARRAY['patient'::text, 'therapist'::text, 'clinic'::text, 'supervisor'::text]))) +); + + +-- +-- Name: COLUMN plans.name; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.plans.name IS 'Nome interno do plano (admin). A key ?? t??cnica/imut??vel.'; + + +-- +-- Name: COLUMN plans.target; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.plans.target IS 'P??blico-alvo do plano: patient, therapist ou clinic.'; + + +-- +-- Name: COLUMN plans.max_supervisees; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.plans.max_supervisees IS 'Limite de terapeutas que podem ser supervisionados. Apenas para planos target=supervisor. NULL = sem limite.'; + + +-- +-- Name: professional_pricing; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.professional_pricing ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + determined_commitment_id uuid, + price numeric(10,2) NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: TABLE professional_pricing; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.professional_pricing IS 'DEPRECATED: substitu??da por public.services. Manter at?? pr??xima release de limpeza.'; + + +-- +-- Name: profiles; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.profiles ( + id uuid NOT NULL, + role text DEFAULT 'tenant_member'::text NOT NULL, + full_name text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + avatar_url text, + phone text, + bio text, + language text DEFAULT 'pt-BR'::text NOT NULL, + timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL, + notify_system_email boolean DEFAULT true NOT NULL, + notify_reminders boolean DEFAULT true NOT NULL, + notify_news boolean DEFAULT false NOT NULL, + account_type text DEFAULT 'free'::text NOT NULL, + platform_roles text[] DEFAULT '{}'::text[] NOT NULL, + nickname text, + work_description text, + work_description_other text, + site_url text, + social_instagram text, + social_youtube text, + social_facebook text, + social_x text, + social_custom jsonb DEFAULT '[]'::jsonb NOT NULL, + CONSTRAINT profiles_account_type_check CHECK ((account_type = ANY (ARRAY['free'::text, 'patient'::text, 'therapist'::text, 'clinic'::text]))), + CONSTRAINT profiles_role_check CHECK ((role = ANY (ARRAY['saas_admin'::text, 'tenant_member'::text, 'portal_user'::text, 'patient'::text]))) +); + + +-- +-- Name: COLUMN profiles.phone; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.phone IS 'WhatsApp / telefone no formato (99) 99999-9999'; + + +-- +-- Name: COLUMN profiles.bio; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.bio IS 'Breve apresenta????o p??blica (m??x 300 chars no front)'; + + +-- +-- Name: COLUMN profiles.account_type; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.account_type IS 'Tipo de conta: free=sem perfil ainda, patient=paciente (imut??vel), therapist=terapeuta (imut??vel), clinic=cl??nica (imut??vel).'; + + +-- +-- Name: COLUMN profiles.platform_roles; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.platform_roles IS 'Papéis globais de plataforma, independentes de tenant. Ex: editor de microlearning. Atribuído pelo saas_admin.'; + + +-- +-- Name: COLUMN profiles.nickname; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.nickname IS 'Apelido preferido para comunica????o interna (Ag??ncia PSI)'; + + +-- +-- Name: COLUMN profiles.work_description; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.work_description IS 'Categoria de trabalho selecionada no perfil p??blico'; + + +-- +-- Name: COLUMN profiles.work_description_other; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.work_description_other IS 'Descri????o livre quando work_description = ''outro'''; + + +-- +-- Name: COLUMN profiles.site_url; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.site_url IS 'Endere??o do site pessoal ou profissional'; + + +-- +-- Name: COLUMN profiles.social_instagram; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_instagram IS 'Handle ou URL do Instagram'; + + +-- +-- Name: COLUMN profiles.social_youtube; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_youtube IS 'Handle ou URL do canal no YouTube'; + + +-- +-- Name: COLUMN profiles.social_facebook; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_facebook IS 'Handle ou URL da p??gina no Facebook'; + + +-- +-- Name: COLUMN profiles.social_x; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_x IS 'Handle ou URL do perfil no X (Twitter)'; + + +-- +-- Name: COLUMN profiles.social_custom; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.profiles.social_custom IS 'Array JSON com redes adicionais livres: [{name, url}]'; + + +-- +-- Name: recurrence_exceptions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.recurrence_exceptions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + recurrence_id uuid NOT NULL, + tenant_id uuid NOT NULL, + original_date date NOT NULL, + type public.recurrence_exception_type NOT NULL, + new_date date, + new_start_time time without time zone, + new_end_time time without time zone, + modalidade text, + observacoes text, + titulo_custom text, + extra_fields jsonb, + reason text, + agenda_evento_id uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: recurrence_rule_services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.recurrence_rule_services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + rule_id uuid NOT NULL, + service_id uuid NOT NULL, + quantity integer DEFAULT 1 NOT NULL, + unit_price numeric(10,2) NOT NULL, + discount_pct numeric(5,2) DEFAULT 0, + discount_flat numeric(10,2) DEFAULT 0, + final_price numeric(10,2) NOT NULL, + created_at timestamp with time zone DEFAULT now(), + CONSTRAINT recurrence_rule_services_disc_flat_chk CHECK ((discount_flat >= (0)::numeric)), + CONSTRAINT recurrence_rule_services_disc_pct_chk CHECK (((discount_pct >= (0)::numeric) AND (discount_pct <= (100)::numeric))), + CONSTRAINT recurrence_rule_services_final_price_chk CHECK ((final_price >= (0)::numeric)), + CONSTRAINT recurrence_rule_services_quantity_chk CHECK ((quantity > 0)) +); + + +-- +-- Name: recurrence_rules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.recurrence_rules ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + owner_id uuid NOT NULL, + therapist_id uuid, + patient_id uuid, + determined_commitment_id uuid, + type public.recurrence_type DEFAULT 'weekly'::public.recurrence_type NOT NULL, + "interval" smallint DEFAULT 1 NOT NULL, + weekdays smallint[] DEFAULT '{}'::smallint[] NOT NULL, + start_time time without time zone NOT NULL, + end_time time without time zone NOT NULL, + timezone text DEFAULT 'America/Sao_Paulo'::text NOT NULL, + duration_min smallint DEFAULT 50 NOT NULL, + start_date date NOT NULL, + end_date date, + max_occurrences integer, + open_ended boolean DEFAULT true NOT NULL, + modalidade text DEFAULT 'presencial'::text, + titulo_custom text, + observacoes text, + extra_fields jsonb, + status text DEFAULT 'ativo'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + price numeric(10,2), + insurance_plan_id uuid, + insurance_guide_number text, + insurance_value numeric(10,2), + insurance_plan_service_id uuid, + CONSTRAINT recurrence_rules_dates_chk CHECK (((end_date IS NULL) OR (end_date >= start_date))), + CONSTRAINT recurrence_rules_interval_chk CHECK (("interval" >= 1)), + CONSTRAINT recurrence_rules_status_check CHECK ((status = ANY (ARRAY['ativo'::text, 'pausado'::text, 'cancelado'::text]))), + CONSTRAINT recurrence_rules_times_chk CHECK ((end_time > start_time)) +); + + +-- +-- Name: saas_admins; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_admins ( + user_id uuid NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: saas_doc_votos; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_doc_votos ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + doc_id uuid NOT NULL, + user_id uuid NOT NULL, + util boolean NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: saas_docs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_docs ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + titulo text NOT NULL, + conteudo text DEFAULT ''::text NOT NULL, + medias jsonb DEFAULT '[]'::jsonb NOT NULL, + tipo_acesso text DEFAULT 'usuario'::text NOT NULL, + pagina_path text NOT NULL, + docs_relacionados uuid[] DEFAULT '{}'::uuid[] NOT NULL, + ativo boolean DEFAULT true NOT NULL, + ordem integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + categoria text, + exibir_no_faq boolean DEFAULT false NOT NULL, + votos_util integer DEFAULT 0 NOT NULL, + votos_nao_util integer DEFAULT 0 NOT NULL, + CONSTRAINT saas_docs_tipo_acesso_check CHECK ((tipo_acesso = ANY (ARRAY['admin'::text, 'usuario'::text]))) +); + + +-- +-- Name: COLUMN saas_docs.categoria; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.saas_docs.categoria IS 'Agrupa docs no portal FAQ (ex: Conta, Agenda, Pagamentos)'; + + +-- +-- Name: COLUMN saas_docs.exibir_no_faq; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.saas_docs.exibir_no_faq IS 'Se true, a doc e seus itens FAQ aparecem no portal de FAQ'; + + +-- +-- Name: saas_faq; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_faq ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + pergunta text NOT NULL, + categoria text, + publico boolean DEFAULT false NOT NULL, + votos integer DEFAULT 0 NOT NULL, + titulo text, + conteudo text, + tipo_acesso text DEFAULT 'usuario'::text NOT NULL, + pagina_path text NOT NULL, + pagina_label text, + medias jsonb DEFAULT '[]'::jsonb NOT NULL, + faqs_relacionados uuid[] DEFAULT '{}'::uuid[] NOT NULL, + ativo boolean DEFAULT true NOT NULL, + ordem integer DEFAULT 0 NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: saas_faq_itens; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.saas_faq_itens ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + doc_id uuid NOT NULL, + pergunta text NOT NULL, + resposta text, + ordem integer DEFAULT 0 NOT NULL, + ativo boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: TABLE saas_faq_itens; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.saas_faq_itens IS 'Pares pergunta/resposta vinculados a um documento de ajuda'; + + +-- +-- Name: services; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.services ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + name text NOT NULL, + description text, + price numeric(10,2) NOT NULL, + duration_min integer, + active boolean DEFAULT true NOT NULL, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now() +); + + +-- +-- Name: subscription_events; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_events ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + subscription_id uuid NOT NULL, + owner_id uuid NOT NULL, + event_type text NOT NULL, + old_plan_id uuid, + new_plan_id uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL, + created_by uuid, + source text DEFAULT 'admin_ui'::text, + reason text, + metadata jsonb, + owner_type text NOT NULL, + owner_ref uuid NOT NULL, + CONSTRAINT subscription_events_owner_ref_consistency_chk CHECK ((owner_id = owner_ref)), + CONSTRAINT subscription_events_owner_type_chk CHECK (((owner_type IS NULL) OR (owner_type = ANY (ARRAY['clinic'::text, 'therapist'::text])))) +); + + +-- +-- Name: subscription_intents_personal; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_intents_personal ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid NOT NULL, + created_by_user_id uuid, + email text NOT NULL, + plan_id uuid NOT NULL, + plan_key text, + "interval" text, + amount_cents integer, + currency text, + status text DEFAULT 'new'::text NOT NULL, + source text DEFAULT 'manual'::text NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + paid_at timestamp with time zone, + subscription_id uuid, + CONSTRAINT sint_personal_interval_check CHECK ((("interval" IS NULL) OR ("interval" = ANY (ARRAY['month'::text, 'year'::text])))), + CONSTRAINT sint_personal_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text]))) +); + + +-- +-- Name: subscription_intents_tenant; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_intents_tenant ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid NOT NULL, + created_by_user_id uuid, + email text NOT NULL, + plan_id uuid NOT NULL, + plan_key text, + "interval" text, + amount_cents integer, + currency text, + status text DEFAULT 'new'::text NOT NULL, + source text DEFAULT 'manual'::text NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + paid_at timestamp with time zone, + tenant_id uuid NOT NULL, + subscription_id uuid, + CONSTRAINT sint_tenant_interval_check CHECK ((("interval" IS NULL) OR ("interval" = ANY (ARRAY['month'::text, 'year'::text])))), + CONSTRAINT sint_tenant_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text]))) +); + + +-- +-- Name: subscription_intents; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.subscription_intents AS + SELECT t.id, + t.user_id, + t.created_by_user_id, + t.email, + t.plan_id, + t.plan_key, + t."interval", + t.amount_cents, + t.currency, + t.status, + t.source, + t.notes, + t.created_at, + t.paid_at, + t.tenant_id, + t.subscription_id, + 'clinic'::text AS plan_target + FROM public.subscription_intents_tenant t +UNION ALL + SELECT p.id, + p.user_id, + p.created_by_user_id, + p.email, + p.plan_id, + p.plan_key, + p."interval", + p.amount_cents, + p.currency, + p.status, + p.source, + p.notes, + p.created_at, + p.paid_at, + NULL::uuid AS tenant_id, + p.subscription_id, + 'therapist'::text AS plan_target + FROM public.subscription_intents_personal p; + + +-- +-- Name: subscription_intents_legacy; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.subscription_intents_legacy ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + user_id uuid, + email text, + plan_key text NOT NULL, + "interval" text NOT NULL, + amount_cents integer NOT NULL, + currency text DEFAULT 'BRL'::text NOT NULL, + status text DEFAULT 'new'::text NOT NULL, + source text DEFAULT 'landing'::text NOT NULL, + notes text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + paid_at timestamp with time zone, + tenant_id uuid NOT NULL, + created_by_user_id uuid, + CONSTRAINT subscription_intents_interval_check CHECK (("interval" = ANY (ARRAY['month'::text, 'year'::text]))), + CONSTRAINT subscription_intents_status_check CHECK ((status = ANY (ARRAY['new'::text, 'waiting_payment'::text, 'paid'::text, 'canceled'::text]))) +); + + +-- +-- Name: support_sessions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.support_sessions ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + admin_id uuid NOT NULL, + token text DEFAULT encode(extensions.gen_random_bytes(32), 'hex'::text) NOT NULL, + expires_at timestamp with time zone DEFAULT (now() + '01:00:00'::interval) NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_feature_exceptions_log; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_feature_exceptions_log ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + feature_key text NOT NULL, + enabled boolean NOT NULL, + reason text, + created_by uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_features; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_features ( + tenant_id uuid NOT NULL, + feature_key text NOT NULL, + enabled boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tenant_invites; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenant_invites ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + email text NOT NULL, + role text NOT NULL, + token uuid DEFAULT gen_random_uuid() NOT NULL, + invited_by uuid, + created_at timestamp with time zone DEFAULT now() NOT NULL, + expires_at timestamp with time zone DEFAULT (now() + '7 days'::interval) NOT NULL, + accepted_at timestamp with time zone, + accepted_by uuid, + revoked_at timestamp with time zone, + revoked_by uuid, + CONSTRAINT tenant_invites_role_check CHECK ((role = ANY (ARRAY['therapist'::text, 'secretary'::text]))) +); + + +-- +-- Name: tenants; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tenants ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + name text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + kind text DEFAULT 'saas'::text NOT NULL, + CONSTRAINT tenants_kind_check CHECK ((kind = ANY (ARRAY['therapist'::text, 'clinic_coworking'::text, 'clinic_reception'::text, 'clinic_full'::text, 'clinic'::text, 'saas'::text, 'supervisor'::text]))) +); + + +-- +-- Name: COLUMN tenants.kind; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.tenants.kind IS 'Tipo do tenant. Imut??vel ap??s cria????o. therapist=terapeuta solo. clinic_coworking/clinic_reception/clinic_full=cl??nicas. clinic e saas s??o legados.'; + + +-- +-- Name: therapist_payout_records; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.therapist_payout_records ( + payout_id uuid NOT NULL, + financial_record_id uuid NOT NULL +); + + +-- +-- Name: twilio_subaccount_usage; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.twilio_subaccount_usage ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + tenant_id uuid NOT NULL, + channel_id uuid NOT NULL, + twilio_subaccount_sid text NOT NULL, + period_start date NOT NULL, + period_end date NOT NULL, + messages_sent integer DEFAULT 0 NOT NULL, + messages_delivered integer DEFAULT 0 NOT NULL, + messages_failed integer DEFAULT 0 NOT NULL, + cost_usd numeric(12,6) DEFAULT 0 NOT NULL, + cost_brl numeric(12,4) DEFAULT 0 NOT NULL, + revenue_brl numeric(12,4) DEFAULT 0 NOT NULL, + margin_brl numeric(12,4) GENERATED ALWAYS AS ((revenue_brl - cost_brl)) STORED, + usd_brl_rate numeric(8,4) DEFAULT 0, + synced_at timestamp with time zone DEFAULT now(), + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT twilio_subaccount_usage_period_check CHECK ((period_end >= period_start)) +); + + +-- +-- Name: TABLE twilio_subaccount_usage; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.twilio_subaccount_usage IS 'Consumo mensal de mensagens WhatsApp por subconta Twilio. Sincronizado via Edge Function.'; + + +-- +-- Name: user_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.user_settings ( + user_id uuid NOT NULL, + theme_mode text DEFAULT 'dark'::text NOT NULL, + preset text DEFAULT 'Aura'::text NOT NULL, + primary_color text DEFAULT 'noir'::text NOT NULL, + surface_color text DEFAULT 'slate'::text NOT NULL, + menu_mode text DEFAULT 'static'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + layout_variant text DEFAULT 'classic'::text NOT NULL, + CONSTRAINT user_settings_layout_variant_check CHECK ((layout_variant = ANY (ARRAY['classic'::text, 'rail'::text]))), + CONSTRAINT user_settings_menu_mode_check CHECK ((menu_mode = ANY (ARRAY['static'::text, 'overlay'::text]))), + CONSTRAINT user_settings_theme_mode_check CHECK ((theme_mode = ANY (ARRAY['light'::text, 'dark'::text]))) +); + + +-- +-- Name: TABLE user_settings; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON TABLE public.user_settings IS 'Prefer??ncias de apar??ncia e layout por usu??rio'; + + +-- +-- Name: COLUMN user_settings.user_id; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.user_id IS 'FK = auth.users.id ??? um registro por usu??rio'; + + +-- +-- Name: COLUMN user_settings.theme_mode; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.theme_mode IS 'light | dark'; + + +-- +-- Name: COLUMN user_settings.preset; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.preset IS 'Preset PrimeVue: Aura | Lara | Nora'; + + +-- +-- Name: COLUMN user_settings.primary_color; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.primary_color IS 'Nome da cor prim??ria (ex: blue, emerald, noir)'; + + +-- +-- Name: COLUMN user_settings.surface_color; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.surface_color IS 'Nome da surface (ex: slate, zinc, neutral)'; + + +-- +-- Name: COLUMN user_settings.menu_mode; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.menu_mode IS 'static | overlay'; + + +-- +-- Name: COLUMN user_settings.layout_variant; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON COLUMN public.user_settings.layout_variant IS 'classic (sidebar) | rail (mini rail + painel)'; + + +-- +-- Name: v_auth_users_public; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_auth_users_public AS + SELECT id AS user_id, + email, + created_at, + last_sign_in_at + FROM auth.users u; + + +-- +-- Name: v_cashflow_projection; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_cashflow_projection WITH (security_invoker='on') AS + SELECT gs.mes, + to_char(gs.mes, 'YYYY-MM'::text) AS mes_label, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric) AS receitas_projetadas, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric) AS despesas_projetadas, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = 'pending'::text))), (0)::numeric) AS receitas_pendentes, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = 'overdue'::text))), (0)::numeric) AS receitas_vencidas, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = 'pending'::text))), (0)::numeric) AS despesas_pendentes, + COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = 'overdue'::text))), (0)::numeric) AS despesas_vencidas, + (COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric) - COALESCE(sum(fr.final_amount) FILTER (WHERE ((fr.type = 'despesa'::public.financial_record_type) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])))), (0)::numeric)) AS saldo_projetado, + count(fr.id) FILTER (WHERE (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text]))) AS count_registros + FROM (generate_series(((date_trunc('month'::text, (CURRENT_DATE)::timestamp with time zone))::date)::timestamp with time zone, (((date_trunc('month'::text, (CURRENT_DATE)::timestamp with time zone) + '5 mons'::interval))::date)::timestamp with time zone, '1 mon'::interval) gs(mes) + LEFT JOIN public.financial_records fr ON (((fr.deleted_at IS NULL) AND (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])) AND ((date_trunc('month'::text, (fr.due_date)::timestamp with time zone))::date = gs.mes)))) + GROUP BY gs.mes + ORDER BY gs.mes; + + +-- +-- Name: VIEW v_cashflow_projection; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.v_cashflow_projection IS 'Fluxo de caixa projetado: pr??ximos 6 meses com totais de pending+overdue por due_date. Usa security_invoker=on ??? filtra automaticamente pelo auth.uid() via RLS de financial_records.'; + + +-- +-- Name: v_commitment_totals; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_commitment_totals AS + SELECT c.tenant_id, + c.id AS commitment_id, + (COALESCE(sum(l.minutes), (0)::bigint))::integer AS total_minutes + FROM (public.determined_commitments c + LEFT JOIN public.commitment_time_logs l ON ((l.commitment_id = c.id))) + GROUP BY c.tenant_id, c.id; + + +-- +-- Name: v_patient_engajamento; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_patient_engajamento WITH (security_invoker='on') AS + WITH sessoes AS ( + SELECT ae.patient_id, + ae.tenant_id, + count(*) FILTER (WHERE (ae.status = 'realizado'::public.status_evento_agenda)) AS total_realizadas, + count(*) FILTER (WHERE (ae.status = ANY (ARRAY['realizado'::public.status_evento_agenda, 'cancelado'::public.status_evento_agenda, 'faltou'::public.status_evento_agenda]))) AS total_marcadas, + count(*) FILTER (WHERE (ae.status = 'faltou'::public.status_evento_agenda)) AS total_faltas, + max(ae.inicio_em) FILTER (WHERE (ae.status = 'realizado'::public.status_evento_agenda)) AS ultima_sessao_em, + min(ae.inicio_em) FILTER (WHERE (ae.status = 'realizado'::public.status_evento_agenda)) AS primeira_sessao_em, + count(*) FILTER (WHERE ((ae.status = 'realizado'::public.status_evento_agenda) AND (ae.inicio_em >= (now() - '30 days'::interval)))) AS sessoes_ultimo_mes + FROM public.agenda_eventos ae + WHERE (ae.patient_id IS NOT NULL) + GROUP BY ae.patient_id, ae.tenant_id + ), financeiro AS ( + SELECT fr.patient_id, + fr.tenant_id, + COALESCE(sum(fr.final_amount) FILTER (WHERE (fr.status = 'paid'::text)), (0)::numeric) AS total_pago, + COALESCE(avg(fr.final_amount) FILTER (WHERE (fr.status = 'paid'::text)), (0)::numeric) AS ticket_medio, + count(*) FILTER (WHERE ((fr.status = ANY (ARRAY['pending'::text, 'overdue'::text])) AND (fr.due_date < now()))) AS cobr_vencidas, + count(*) FILTER (WHERE (fr.status = ANY (ARRAY['pending'::text, 'overdue'::text]))) AS cobr_pendentes, + count(*) FILTER (WHERE ((fr.type = 'receita'::public.financial_record_type) AND (fr.status = 'paid'::text))) AS cobr_pagas + FROM public.financial_records fr + WHERE ((fr.patient_id IS NOT NULL) AND (fr.deleted_at IS NULL)) + GROUP BY fr.patient_id, fr.tenant_id + ) + SELECT p.id AS patient_id, + p.tenant_id, + p.nome_completo, + p.status, + p.risco_elevado, + COALESCE(s.total_realizadas, (0)::bigint) AS total_sessoes, + COALESCE(s.sessoes_ultimo_mes, (0)::bigint) AS sessoes_ultimo_mes, + s.primeira_sessao_em, + s.ultima_sessao_em, + (EXTRACT(day FROM (now() - s.ultima_sessao_em)))::integer AS dias_sem_sessao, + CASE + WHEN (COALESCE(s.total_marcadas, (0)::bigint) = 0) THEN NULL::numeric + ELSE round((((s.total_realizadas)::numeric / (s.total_marcadas)::numeric) * (100)::numeric), 1) + END AS taxa_comparecimento, + COALESCE(f.total_pago, (0)::numeric) AS ltv_total, + round(COALESCE(f.ticket_medio, (0)::numeric), 2) AS ticket_medio, + COALESCE(f.cobr_vencidas, (0)::bigint) AS cobr_vencidas, + COALESCE(f.cobr_pagas, (0)::bigint) AS cobr_pagas, + CASE + WHEN (COALESCE((f.cobr_pagas + f.cobr_vencidas), (0)::bigint) = 0) THEN NULL::numeric + ELSE round((((f.cobr_pagas)::numeric / ((f.cobr_pagas + f.cobr_vencidas))::numeric) * (100)::numeric), 1) + END AS taxa_pagamentos_dia, + round(LEAST((100)::numeric, COALESCE((( + CASE + WHEN (COALESCE(s.total_marcadas, (0)::bigint) = 0) THEN (50)::numeric + ELSE LEAST((50)::numeric, (((s.total_realizadas)::numeric / (s.total_marcadas)::numeric) * (50)::numeric)) + END + + CASE + WHEN (COALESCE((f.cobr_pagas + f.cobr_vencidas), (0)::bigint) = 0) THEN (30)::numeric + ELSE LEAST((30)::numeric, (((f.cobr_pagas)::numeric / ((f.cobr_pagas + f.cobr_vencidas))::numeric) * (30)::numeric)) + END) + ( + CASE + WHEN (s.ultima_sessao_em IS NULL) THEN 0 + WHEN (EXTRACT(day FROM (now() - s.ultima_sessao_em)) <= (14)::numeric) THEN 20 + WHEN (EXTRACT(day FROM (now() - s.ultima_sessao_em)) <= (30)::numeric) THEN 15 + WHEN (EXTRACT(day FROM (now() - s.ultima_sessao_em)) <= (60)::numeric) THEN 8 + ELSE 0 + END)::numeric), (0)::numeric)), 0) AS engajamento_score, + CASE + WHEN (s.primeira_sessao_em IS NULL) THEN NULL::integer + ELSE (EXTRACT(day FROM (now() - s.primeira_sessao_em)))::integer + END AS duracao_tratamento_dias + FROM ((public.patients p + LEFT JOIN sessoes s ON (((s.patient_id = p.id) AND (s.tenant_id = p.tenant_id)))) + LEFT JOIN financeiro f ON (((f.patient_id = p.id) AND (f.tenant_id = p.tenant_id)))); + + +-- +-- Name: VIEW v_patient_engajamento; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.v_patient_engajamento IS 'Score de engajamento e métricas consolidadas por paciente. Calculado em tempo real via RLS (security_invoker=on).'; + + +-- +-- Name: v_patient_groups_with_counts; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_patient_groups_with_counts AS + SELECT pg.id, + pg.nome, + pg.cor, + pg.owner_id, + pg.is_system, + pg.is_active, + pg.created_at, + pg.updated_at, + (COALESCE(count(pgp.patient_id), (0)::bigint))::integer AS patients_count + FROM (public.patient_groups pg + LEFT JOIN public.patient_group_patient pgp ON ((pgp.patient_group_id = pg.id))) + GROUP BY pg.id, pg.nome, pg.cor, pg.owner_id, pg.is_system, pg.is_active, pg.created_at, pg.updated_at; + + +-- +-- Name: v_patients_risco; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_patients_risco WITH (security_invoker='on') AS + SELECT p.id, + p.tenant_id, + p.nome_completo, + p.status, + p.risco_elevado, + p.risco_nota, + p.risco_sinalizado_em, + e.dias_sem_sessao, + e.engajamento_score, + e.taxa_comparecimento, + CASE + WHEN p.risco_elevado THEN 'risco_sinalizado'::text + WHEN ((COALESCE(e.dias_sem_sessao, 999) > 30) AND (p.status = 'Ativo'::text)) THEN 'sem_sessao_30d'::text + WHEN (COALESCE(e.taxa_comparecimento, (100)::numeric) < (60)::numeric) THEN 'baixo_comparecimento'::text + WHEN (COALESCE(e.cobr_vencidas, (0)::bigint) > 0) THEN 'cobranca_vencida'::text + ELSE 'ok'::text + END AS alerta_tipo + FROM (public.patients p + JOIN public.v_patient_engajamento e ON ((e.patient_id = p.id))) + WHERE ((p.status = 'Ativo'::text) AND ((p.risco_elevado = true) OR (COALESCE(e.dias_sem_sessao, 999) > 30) OR (COALESCE(e.taxa_comparecimento, (100)::numeric) < (60)::numeric) OR (COALESCE(e.cobr_vencidas, (0)::bigint) > 0))); + + +-- +-- Name: VIEW v_patients_risco; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.v_patients_risco IS 'Pacientes ativos que precisam de atenção: risco clínico, sem sessão há 30+ dias, baixo comparecimento ou cobrança vencida'; + + +-- +-- Name: v_plan_active_prices; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_plan_active_prices AS + SELECT plan_id, + max( + CASE + WHEN (("interval" = 'month'::text) AND is_active) THEN amount_cents + ELSE NULL::integer + END) AS monthly_cents, + max( + CASE + WHEN (("interval" = 'year'::text) AND is_active) THEN amount_cents + ELSE NULL::integer + END) AS yearly_cents, + max( + CASE + WHEN (("interval" = 'month'::text) AND is_active) THEN currency + ELSE NULL::text + END) AS monthly_currency, + max( + CASE + WHEN (("interval" = 'year'::text) AND is_active) THEN currency + ELSE NULL::text + END) AS yearly_currency + FROM public.plan_prices + GROUP BY plan_id; + + +-- +-- Name: v_public_pricing; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_public_pricing AS + SELECT p.id AS plan_id, + p.key AS plan_key, + p.name AS plan_name, + COALESCE(pp.public_name, ''::text) AS public_name, + COALESCE(pp.public_description, ''::text) AS public_description, + pp.badge, + COALESCE(pp.is_featured, false) AS is_featured, + COALESCE(pp.is_visible, true) AS is_visible, + COALESCE(pp.sort_order, 0) AS sort_order, + ap.monthly_cents, + ap.yearly_cents, + ap.monthly_currency, + ap.yearly_currency, + COALESCE(( SELECT jsonb_agg(jsonb_build_object('id', b.id, 'text', b.text, 'highlight', b.highlight, 'sort_order', b.sort_order) ORDER BY b.sort_order, b.created_at) AS jsonb_agg + FROM public.plan_public_bullets b + WHERE (b.plan_id = p.id)), '[]'::jsonb) AS bullets, + p.target AS plan_target + FROM ((public.plans p + LEFT JOIN public.plan_public pp ON ((pp.plan_id = p.id))) + LEFT JOIN public.v_plan_active_prices ap ON ((ap.plan_id = p.id))) + ORDER BY COALESCE(pp.sort_order, 0), p.key; + + +-- +-- Name: v_subscription_feature_mismatch; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_subscription_feature_mismatch AS + WITH expected AS ( + SELECT s.user_id AS owner_id, + f.key AS feature_key + FROM ((public.subscriptions s + JOIN public.plan_features pf ON (((pf.plan_id = s.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))) + WHERE ((s.status = 'active'::text) AND (s.tenant_id IS NULL) AND (s.user_id IS NOT NULL)) + ), actual AS ( + SELECT e.owner_id, + e.feature_key + FROM public.owner_feature_entitlements e + ) + SELECT COALESCE(expected.owner_id, actual.owner_id) AS owner_id, + COALESCE(expected.feature_key, actual.feature_key) AS feature_key, + CASE + WHEN ((expected.feature_key IS NOT NULL) AND (actual.feature_key IS NULL)) THEN 'missing_entitlement'::text + WHEN ((expected.feature_key IS NULL) AND (actual.feature_key IS NOT NULL)) THEN 'unexpected_entitlement'::text + ELSE NULL::text + END AS mismatch_type + FROM (expected + FULL JOIN actual ON (((expected.owner_id = actual.owner_id) AND (expected.feature_key = actual.feature_key)))) + WHERE ((expected.feature_key IS NULL) OR (actual.feature_key IS NULL)); + + +-- +-- Name: v_subscription_health; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_subscription_health AS + SELECT s.id AS subscription_id, + s.user_id AS owner_id, + s.status, + s.plan_id, + p.key AS plan_key, + s.current_period_start, + s.current_period_end, + s.updated_at, + CASE + WHEN (s.plan_id IS NULL) THEN 'missing_plan'::text + WHEN (p.id IS NULL) THEN 'invalid_plan'::text + WHEN ((s.status = 'active'::text) AND (s.current_period_end IS NOT NULL) AND (s.current_period_end < now())) THEN 'expired_but_active'::text + WHEN ((s.status = 'canceled'::text) AND (s.current_period_end > now())) THEN 'canceled_but_still_in_period'::text + ELSE 'ok'::text + END AS health_status, + CASE + WHEN (s.tenant_id IS NOT NULL) THEN 'clinic'::text + ELSE 'therapist'::text + END AS owner_type, + COALESCE(s.tenant_id, s.user_id) AS owner_ref + FROM (public.subscriptions s + LEFT JOIN public.plans p ON ((p.id = s.plan_id))); + + +-- +-- Name: v_subscription_health_v2; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_subscription_health_v2 AS + SELECT s.id AS subscription_id, + s.user_id AS owner_id, + CASE + WHEN (s.tenant_id IS NOT NULL) THEN 'clinic'::text + ELSE 'therapist'::text + END AS owner_type, + COALESCE(s.tenant_id, s.user_id) AS owner_ref, + s.status, + s.plan_id, + p.key AS plan_key, + s.current_period_start, + s.current_period_end, + s.updated_at, + CASE + WHEN (s.plan_id IS NULL) THEN 'missing_plan'::text + WHEN (p.id IS NULL) THEN 'invalid_plan'::text + WHEN ((s.status = 'active'::text) AND (s.current_period_end IS NOT NULL) AND (s.current_period_end < now())) THEN 'expired_but_active'::text + WHEN ((s.status = 'canceled'::text) AND (s.current_period_end > now())) THEN 'canceled_but_still_in_period'::text + ELSE 'ok'::text + END AS health_status + FROM (public.subscriptions s + LEFT JOIN public.plans p ON ((p.id = s.plan_id))); + + +-- +-- Name: v_tag_patient_counts; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tag_patient_counts AS + SELECT t.id, + t.owner_id, + t.nome, + t.cor, + t.is_padrao, + t.created_at, + t.updated_at, + (COALESCE(count(ppt.patient_id), (0)::bigint))::integer AS pacientes_count, + (COALESCE(count(ppt.patient_id), (0)::bigint))::integer AS patient_count + FROM (public.patient_tags t + LEFT JOIN public.patient_patient_tag ppt ON (((ppt.tag_id = t.id) AND (ppt.owner_id = t.owner_id)))) + GROUP BY t.id, t.owner_id, t.nome, t.cor, t.is_padrao, t.created_at, t.updated_at; + + +-- +-- Name: v_tenant_active_subscription; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_active_subscription AS + SELECT DISTINCT ON (tenant_id) tenant_id, + plan_id, + plan_key, + "interval", + status, + current_period_start, + current_period_end, + created_at + FROM public.subscriptions s + WHERE ((tenant_id IS NOT NULL) AND (status = 'active'::text) AND ((current_period_end IS NULL) OR (current_period_end > now()))) + ORDER BY tenant_id, created_at DESC; + + +-- +-- Name: v_tenant_entitlements; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_entitlements AS + SELECT a.tenant_id, + f.key AS feature_key, + true AS allowed + FROM ((public.v_tenant_active_subscription a + JOIN public.plan_features pf ON (((pf.plan_id = a.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))); + + +-- +-- Name: v_tenant_entitlements_full; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_entitlements_full AS + SELECT a.tenant_id, + f.key AS feature_key, + (pf.enabled = true) AS allowed, + pf.limits, + a.plan_id, + p.key AS plan_key + FROM (((public.v_tenant_active_subscription a + JOIN public.plan_features pf ON ((pf.plan_id = a.plan_id))) + JOIN public.features f ON ((f.id = pf.feature_id))) + JOIN public.plans p ON ((p.id = a.plan_id))); + + +-- +-- Name: v_tenant_entitlements_json; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_entitlements_json AS + SELECT tenant_id, + max(plan_key) AS plan_key, + jsonb_object_agg(feature_key, jsonb_build_object('allowed', allowed, 'limits', COALESCE(limits, '{}'::jsonb)) ORDER BY feature_key) AS entitlements + FROM public.v_tenant_entitlements_full + GROUP BY tenant_id; + + +-- +-- Name: v_tenant_feature_exceptions; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_feature_exceptions AS + SELECT tf.tenant_id, + a.plan_key, + tf.feature_key, + 'commercial_exception'::text AS exception_type + FROM ((public.tenant_features tf + JOIN public.v_tenant_active_subscription a ON ((a.tenant_id = tf.tenant_id))) + LEFT JOIN public.v_tenant_entitlements_full v ON (((v.tenant_id = tf.tenant_id) AND (v.feature_key = tf.feature_key)))) + WHERE ((tf.enabled = true) AND (COALESCE(v.allowed, false) = false)); + + +-- +-- Name: v_tenant_feature_mismatch; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_feature_mismatch AS + WITH plan_allowed AS ( + SELECT v.tenant_id, + v.feature_key, + v.allowed + FROM public.v_tenant_entitlements_full v + ), overrides AS ( + SELECT tf.tenant_id, + tf.feature_key, + tf.enabled + FROM public.tenant_features tf + ) + SELECT o.tenant_id, + o.feature_key, + CASE + WHEN ((o.enabled = true) AND (COALESCE(p.allowed, false) = false)) THEN 'unexpected_override'::text + ELSE NULL::text + END AS mismatch_type + FROM (overrides o + LEFT JOIN plan_allowed p ON (((p.tenant_id = o.tenant_id) AND (p.feature_key = o.feature_key)))) + WHERE ((o.enabled = true) AND (COALESCE(p.allowed, false) = false)); + + +-- +-- Name: v_tenant_members_with_profiles; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_members_with_profiles AS + SELECT tm.id AS tenant_member_id, + tm.tenant_id, + tm.user_id, + tm.role, + tm.status, + tm.created_at, + p.full_name, + au.email + FROM ((public.tenant_members tm + LEFT JOIN public.profiles p ON ((p.id = tm.user_id))) + LEFT JOIN auth.users au ON ((au.id = tm.user_id))); + + +-- +-- Name: v_tenant_people; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_people AS + SELECT 'member'::text AS type, + m.tenant_id, + m.user_id, + u.email, + m.role, + m.status, + NULL::uuid AS invite_token, + NULL::timestamp with time zone AS expires_at + FROM (public.tenant_members m + JOIN auth.users u ON ((u.id = m.user_id))) +UNION ALL + SELECT 'invite'::text AS type, + i.tenant_id, + NULL::uuid AS user_id, + i.email, + i.role, + 'invited'::text AS status, + i.token AS invite_token, + i.expires_at + FROM public.tenant_invites i + WHERE ((i.accepted_at IS NULL) AND (i.revoked_at IS NULL)); + + +-- +-- Name: v_tenant_staff; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_tenant_staff AS + SELECT ('m_'::text || (tm.id)::text) AS row_id, + tm.tenant_id, + tm.user_id, + tm.role, + tm.status, + tm.created_at, + p.full_name, + au.email, + NULL::uuid AS invite_token + FROM ((public.tenant_members tm + LEFT JOIN public.profiles p ON ((p.id = tm.user_id))) + LEFT JOIN auth.users au ON ((au.id = tm.user_id))) +UNION ALL + SELECT ('i_'::text || (ti.id)::text) AS row_id, + ti.tenant_id, + NULL::uuid AS user_id, + ti.role, + 'invited'::text AS status, + ti.created_at, + NULL::text AS full_name, + ti.email, + ti.token AS invite_token + FROM public.tenant_invites ti + WHERE ((ti.accepted_at IS NULL) AND (ti.revoked_at IS NULL) AND (ti.expires_at > now())); + + +-- +-- Name: v_twilio_whatsapp_overview; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_twilio_whatsapp_overview AS + SELECT nc.id AS channel_id, + nc.tenant_id, + nc.owner_id, + nc.is_active, + nc.connection_status, + nc.display_name, + nc.twilio_subaccount_sid, + nc.twilio_phone_number, + nc.twilio_phone_sid, + nc.cost_per_message_usd, + nc.price_per_message_brl, + nc.provisioned_at, + nc.created_at, + nc.updated_at, + COALESCE(u.messages_sent, 0) AS current_month_sent, + COALESCE(u.messages_delivered, 0) AS current_month_delivered, + COALESCE(u.messages_failed, 0) AS current_month_failed, + COALESCE(u.cost_usd, (0)::numeric) AS current_month_cost_usd, + COALESCE(u.cost_brl, (0)::numeric) AS current_month_cost_brl, + COALESCE(u.revenue_brl, (0)::numeric) AS current_month_revenue_brl, + COALESCE(u.margin_brl, (0)::numeric) AS current_month_margin_brl + FROM (public.notification_channels nc + LEFT JOIN public.twilio_subaccount_usage u ON (((u.channel_id = nc.id) AND (u.period_start = (date_trunc('month'::text, (CURRENT_DATE)::timestamp with time zone))::date)))) + WHERE ((nc.channel = 'whatsapp'::text) AND (nc.provider = 'twilio'::text) AND (nc.deleted_at IS NULL)); + + +-- +-- Name: VIEW v_twilio_whatsapp_overview; Type: COMMENT; Schema: public; Owner: - +-- + +COMMENT ON VIEW public.v_twilio_whatsapp_overview IS 'Visão consolidada de subcontas Twilio WhatsApp com uso do mês corrente.'; + + +-- +-- Name: v_user_active_subscription; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_user_active_subscription AS + SELECT DISTINCT ON (user_id) user_id, + plan_id, + plan_key, + "interval", + status, + current_period_start, + current_period_end, + created_at + FROM public.subscriptions s + WHERE ((tenant_id IS NULL) AND (user_id IS NOT NULL) AND (status = 'active'::text) AND ((current_period_end IS NULL) OR (current_period_end > now()))) + ORDER BY user_id, created_at DESC; + + +-- +-- Name: v_user_entitlements; Type: VIEW; Schema: public; Owner: - +-- + +CREATE VIEW public.v_user_entitlements AS + SELECT a.user_id, + f.key AS feature_key, + true AS allowed + FROM ((public.v_user_active_subscription a + JOIN public.plan_features pf ON (((pf.plan_id = a.plan_id) AND (pf.enabled = true)))) + JOIN public.features f ON ((f.id = pf.feature_id))); + + +-- +-- Name: messages; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +) +PARTITION BY RANGE (inserted_at); + + +-- +-- Name: messages_2026_03_26; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_26 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_27; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_27 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_28; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_28 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_29; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_29 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_30; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_30 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_03_31; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_03_31 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: messages_2026_04_01; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.messages_2026_04_01 ( + topic text NOT NULL, + extension text NOT NULL, + payload jsonb, + event text, + private boolean DEFAULT false, + updated_at timestamp without time zone DEFAULT now() NOT NULL, + inserted_at timestamp without time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL +); + + +-- +-- Name: schema_migrations; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.schema_migrations ( + version bigint NOT NULL, + inserted_at timestamp(0) without time zone +); + + +-- +-- Name: subscription; Type: TABLE; Schema: realtime; Owner: - +-- + +CREATE TABLE realtime.subscription ( + id bigint NOT NULL, + subscription_id uuid NOT NULL, + entity regclass NOT NULL, + filters realtime.user_defined_filter[] DEFAULT '{}'::realtime.user_defined_filter[] NOT NULL, + claims jsonb NOT NULL, + claims_role regrole GENERATED ALWAYS AS (realtime.to_regrole((claims ->> 'role'::text))) STORED NOT NULL, + created_at timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL +); + + +-- +-- Name: subscription_id_seq; Type: SEQUENCE; Schema: realtime; Owner: - +-- + +ALTER TABLE realtime.subscription ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY ( + SEQUENCE NAME realtime.subscription_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: buckets; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.buckets ( + id text NOT NULL, + name text NOT NULL, + owner uuid, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + public boolean DEFAULT false, + avif_autodetection boolean DEFAULT false, + file_size_limit bigint, + allowed_mime_types text[], + owner_id text, + type storage.buckettype DEFAULT 'STANDARD'::storage.buckettype NOT NULL +); + + +-- +-- Name: COLUMN buckets.owner; Type: COMMENT; Schema: storage; Owner: - +-- + +COMMENT ON COLUMN storage.buckets.owner IS 'Field is deprecated, use owner_id instead'; + + +-- +-- Name: buckets_analytics; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.buckets_analytics ( + name text NOT NULL, + type storage.buckettype DEFAULT 'ANALYTICS'::storage.buckettype NOT NULL, + format text DEFAULT 'ICEBERG'::text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + id uuid DEFAULT gen_random_uuid() NOT NULL, + deleted_at timestamp with time zone +); + + +-- +-- Name: buckets_vectors; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.buckets_vectors ( + id text NOT NULL, + type storage.buckettype DEFAULT 'VECTOR'::storage.buckettype NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: iceberg_namespaces; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.iceberg_namespaces ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + bucket_name text NOT NULL, + name text NOT NULL COLLATE pg_catalog."C", + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + metadata jsonb DEFAULT '{}'::jsonb NOT NULL, + catalog_id uuid NOT NULL +); + + +-- +-- Name: iceberg_tables; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.iceberg_tables ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + namespace_id uuid NOT NULL, + bucket_name text NOT NULL, + name text NOT NULL COLLATE pg_catalog."C", + location text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + remote_table_id text, + shard_key text, + shard_id text, + catalog_id uuid NOT NULL +); + + +-- +-- Name: migrations; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.migrations ( + id integer NOT NULL, + name character varying(100) NOT NULL, + hash character varying(40) NOT NULL, + executed_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP +); + + +-- +-- Name: objects; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.objects ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + bucket_id text, + name text, + owner uuid, + created_at timestamp with time zone DEFAULT now(), + updated_at timestamp with time zone DEFAULT now(), + last_accessed_at timestamp with time zone DEFAULT now(), + metadata jsonb, + path_tokens text[] GENERATED ALWAYS AS (string_to_array(name, '/'::text)) STORED, + version text, + owner_id text, + user_metadata jsonb +); + + +-- +-- Name: COLUMN objects.owner; Type: COMMENT; Schema: storage; Owner: - +-- + +COMMENT ON COLUMN storage.objects.owner IS 'Field is deprecated, use owner_id instead'; + + +-- +-- Name: s3_multipart_uploads; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.s3_multipart_uploads ( + id text NOT NULL, + in_progress_size bigint DEFAULT 0 NOT NULL, + upload_signature text NOT NULL, + bucket_id text NOT NULL, + key text NOT NULL COLLATE pg_catalog."C", + version text NOT NULL, + owner_id text, + created_at timestamp with time zone DEFAULT now() NOT NULL, + user_metadata jsonb +); + + +-- +-- Name: s3_multipart_uploads_parts; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.s3_multipart_uploads_parts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + upload_id text NOT NULL, + size bigint DEFAULT 0 NOT NULL, + part_number integer NOT NULL, + bucket_id text NOT NULL, + key text NOT NULL COLLATE pg_catalog."C", + etag text NOT NULL, + owner_id text, + version text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: vector_indexes; Type: TABLE; Schema: storage; Owner: - +-- + +CREATE TABLE storage.vector_indexes ( + id text DEFAULT gen_random_uuid() NOT NULL, + name text NOT NULL COLLATE pg_catalog."C", + bucket_id text NOT NULL, + data_type text NOT NULL, + dimension integer NOT NULL, + distance_metric text NOT NULL, + metadata_configuration jsonb, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: hooks; Type: TABLE; Schema: supabase_functions; Owner: - +-- + +CREATE TABLE supabase_functions.hooks ( + id bigint NOT NULL, + hook_table_id integer NOT NULL, + hook_name text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + request_id bigint +); + + +-- +-- Name: TABLE hooks; Type: COMMENT; Schema: supabase_functions; Owner: - +-- + +COMMENT ON TABLE supabase_functions.hooks IS 'Supabase Functions Hooks: Audit trail for triggered hooks.'; + + +-- +-- Name: hooks_id_seq; Type: SEQUENCE; Schema: supabase_functions; Owner: - +-- + +CREATE SEQUENCE supabase_functions.hooks_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: hooks_id_seq; Type: SEQUENCE OWNED BY; Schema: supabase_functions; Owner: - +-- + +ALTER SEQUENCE supabase_functions.hooks_id_seq OWNED BY supabase_functions.hooks.id; + + +-- +-- Name: migrations; Type: TABLE; Schema: supabase_functions; Owner: - +-- + +CREATE TABLE supabase_functions.migrations ( + version text NOT NULL, + inserted_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: messages_2026_03_26; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_26 FOR VALUES FROM ('2026-03-26 00:00:00') TO ('2026-03-27 00:00:00'); + + +-- +-- Name: messages_2026_03_27; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_27 FOR VALUES FROM ('2026-03-27 00:00:00') TO ('2026-03-28 00:00:00'); + + +-- +-- Name: messages_2026_03_28; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_28 FOR VALUES FROM ('2026-03-28 00:00:00') TO ('2026-03-29 00:00:00'); + + +-- +-- Name: messages_2026_03_29; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_29 FOR VALUES FROM ('2026-03-29 00:00:00') TO ('2026-03-30 00:00:00'); + + +-- +-- Name: messages_2026_03_30; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_30 FOR VALUES FROM ('2026-03-30 00:00:00') TO ('2026-03-31 00:00:00'); + + +-- +-- Name: messages_2026_03_31; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_03_31 FOR VALUES FROM ('2026-03-31 00:00:00') TO ('2026-04-01 00:00:00'); + + +-- +-- Name: messages_2026_04_01; Type: TABLE ATTACH; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages ATTACH PARTITION realtime.messages_2026_04_01 FOR VALUES FROM ('2026-04-01 00:00:00') TO ('2026-04-02 00:00:00'); + + +-- +-- Name: refresh_tokens id; Type: DEFAULT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens ALTER COLUMN id SET DEFAULT nextval('auth.refresh_tokens_id_seq'::regclass); + + +-- +-- Name: _db_migrations id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._db_migrations ALTER COLUMN id SET DEFAULT nextval('public._db_migrations_id_seq'::regclass); + + +-- +-- Name: agenda_online_slots id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots ALTER COLUMN id SET DEFAULT nextval('public.agenda_online_slots_id_seq'::regclass); + + +-- +-- Name: hooks id; Type: DEFAULT; Schema: supabase_functions; Owner: - +-- + +ALTER TABLE ONLY supabase_functions.hooks ALTER COLUMN id SET DEFAULT nextval('supabase_functions.hooks_id_seq'::regclass); + + +-- +-- Name: extensions extensions_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.extensions + ADD CONSTRAINT extensions_pkey PRIMARY KEY (id); + + +-- +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.schema_migrations + ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.tenants + ADD CONSTRAINT tenants_pkey PRIMARY KEY (id); + + +-- +-- Name: mfa_amr_claims amr_id_pk; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_amr_claims + ADD CONSTRAINT amr_id_pk PRIMARY KEY (id); + + +-- +-- Name: audit_log_entries audit_log_entries_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.audit_log_entries + ADD CONSTRAINT audit_log_entries_pkey PRIMARY KEY (id); + + +-- +-- Name: flow_state flow_state_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.flow_state + ADD CONSTRAINT flow_state_pkey PRIMARY KEY (id); + + +-- +-- Name: identities identities_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.identities + ADD CONSTRAINT identities_pkey PRIMARY KEY (id); + + +-- +-- Name: identities identities_provider_id_provider_unique; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.identities + ADD CONSTRAINT identities_provider_id_provider_unique UNIQUE (provider_id, provider); + + +-- +-- Name: instances instances_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.instances + ADD CONSTRAINT instances_pkey PRIMARY KEY (id); + + +-- +-- Name: mfa_amr_claims mfa_amr_claims_session_id_authentication_method_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_amr_claims + ADD CONSTRAINT mfa_amr_claims_session_id_authentication_method_pkey UNIQUE (session_id, authentication_method); + + +-- +-- Name: mfa_challenges mfa_challenges_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_challenges + ADD CONSTRAINT mfa_challenges_pkey PRIMARY KEY (id); + + +-- +-- Name: mfa_factors mfa_factors_last_challenged_at_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_factors + ADD CONSTRAINT mfa_factors_last_challenged_at_key UNIQUE (last_challenged_at); + + +-- +-- Name: mfa_factors mfa_factors_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_factors + ADD CONSTRAINT mfa_factors_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_authorizations oauth_authorizations_authorization_code_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_authorization_code_key UNIQUE (authorization_code); + + +-- +-- Name: oauth_authorizations oauth_authorizations_authorization_id_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_authorization_id_key UNIQUE (authorization_id); + + +-- +-- Name: oauth_authorizations oauth_authorizations_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_client_states oauth_client_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_client_states + ADD CONSTRAINT oauth_client_states_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_clients oauth_clients_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_clients + ADD CONSTRAINT oauth_clients_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_consents oauth_consents_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_consents oauth_consents_user_client_unique; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_user_client_unique UNIQUE (user_id, client_id); + + +-- +-- Name: one_time_tokens one_time_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.one_time_tokens + ADD CONSTRAINT one_time_tokens_pkey PRIMARY KEY (id); + + +-- +-- Name: refresh_tokens refresh_tokens_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens + ADD CONSTRAINT refresh_tokens_pkey PRIMARY KEY (id); + + +-- +-- Name: refresh_tokens refresh_tokens_token_unique; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens + ADD CONSTRAINT refresh_tokens_token_unique UNIQUE (token); + + +-- +-- Name: saml_providers saml_providers_entity_id_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_providers + ADD CONSTRAINT saml_providers_entity_id_key UNIQUE (entity_id); + + +-- +-- Name: saml_providers saml_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_providers + ADD CONSTRAINT saml_providers_pkey PRIMARY KEY (id); + + +-- +-- Name: saml_relay_states saml_relay_states_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_relay_states + ADD CONSTRAINT saml_relay_states_pkey PRIMARY KEY (id); + + +-- +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.schema_migrations + ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: sessions sessions_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sessions + ADD CONSTRAINT sessions_pkey PRIMARY KEY (id); + + +-- +-- Name: sso_domains sso_domains_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sso_domains + ADD CONSTRAINT sso_domains_pkey PRIMARY KEY (id); + + +-- +-- Name: sso_providers sso_providers_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sso_providers + ADD CONSTRAINT sso_providers_pkey PRIMARY KEY (id); + + +-- +-- Name: users users_phone_key; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.users + ADD CONSTRAINT users_phone_key UNIQUE (phone); + + +-- +-- Name: users users_pkey; Type: CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.users + ADD CONSTRAINT users_pkey PRIMARY KEY (id); + + +-- +-- Name: _db_migrations _db_migrations_filename_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._db_migrations + ADD CONSTRAINT _db_migrations_filename_key UNIQUE (filename); + + +-- +-- Name: _db_migrations _db_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public._db_migrations + ADD CONSTRAINT _db_migrations_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_credits addon_credits_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT addon_credits_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_products addon_products_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_products + ADD CONSTRAINT addon_products_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_products addon_products_slug_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_products + ADD CONSTRAINT addon_products_slug_key UNIQUE (slug); + + +-- +-- Name: addon_transactions addon_transactions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_bloqueios + ADD CONSTRAINT agenda_bloqueios_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_configuracoes + ADD CONSTRAINT agenda_configuracoes_pkey PRIMARY KEY (owner_id); + + +-- +-- Name: agenda_eventos agenda_eventos_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_eventos agenda_eventos_sem_sobreposicao; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_sem_sobreposicao EXCLUDE USING gist (owner_id WITH =, tstzrange(inicio_em, fim_em, '[)'::text) WITH &&); + + +-- +-- Name: agenda_excecoes agenda_excecoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_excecoes + ADD CONSTRAINT agenda_excecoes_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_online_slots agenda_online_slots_owner_id_weekday_time_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_owner_id_weekday_time_key UNIQUE (owner_id, weekday, "time"); + + +-- +-- Name: agenda_online_slots agenda_online_slots_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_regras_semanais + ADD CONSTRAINT agenda_regras_semanais_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_regras_semanais + ADD CONSTRAINT agenda_regras_semanais_unique UNIQUE (owner_id, dia_semana, hora_inicio, hora_fim, modalidade); + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_seman_owner_id_dia_semana_hora_inic_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais + ADD CONSTRAINT agenda_slots_bloqueados_seman_owner_id_dia_semana_hora_inic_key UNIQUE (owner_id, dia_semana, hora_inicio); + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais + ADD CONSTRAINT agenda_slots_bloqueados_semanais_pkey PRIMARY KEY (id); + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_owner_id_dia_semana_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_regras + ADD CONSTRAINT agenda_slots_regras_owner_id_dia_semana_key UNIQUE (owner_id, dia_semana); + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_regras + ADD CONSTRAINT agenda_slots_regras_pkey PRIMARY KEY (id); + + +-- +-- Name: agendador_configuracoes agendador_configuracoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_configuracoes + ADD CONSTRAINT agendador_configuracoes_pkey PRIMARY KEY (owner_id); + + +-- +-- Name: agendador_solicitacoes agendador_solicitacoes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_solicitacoes + ADD CONSTRAINT agendador_solicitacoes_pkey PRIMARY KEY (id); + + +-- +-- Name: billing_contracts billing_contracts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.billing_contracts + ADD CONSTRAINT billing_contracts_pkey PRIMARY KEY (id); + + +-- +-- Name: commitment_services commitment_services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_services + ADD CONSTRAINT commitment_services_pkey PRIMARY KEY (id); + + +-- +-- Name: commitment_time_logs commitment_time_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_pkey PRIMARY KEY (id); + + +-- +-- Name: company_profiles company_profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.company_profiles + ADD CONSTRAINT company_profiles_pkey PRIMARY KEY (id); + + +-- +-- Name: company_profiles company_profiles_tenant_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.company_profiles + ADD CONSTRAINT company_profiles_tenant_id_key UNIQUE (tenant_id); + + +-- +-- Name: determined_commitment_fields determined_commitment_fields_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitment_fields + ADD CONSTRAINT determined_commitment_fields_pkey PRIMARY KEY (id); + + +-- +-- Name: determined_commitments determined_commitments_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitments + ADD CONSTRAINT determined_commitments_pkey PRIMARY KEY (id); + + +-- +-- Name: determined_commitments determined_commitments_tenant_native_key_uq; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitments + ADD CONSTRAINT determined_commitments_tenant_native_key_uq UNIQUE (tenant_id, native_key); + + +-- +-- Name: dev_user_credentials dev_user_credentials_email_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.dev_user_credentials + ADD CONSTRAINT dev_user_credentials_email_key UNIQUE (email); + + +-- +-- Name: dev_user_credentials dev_user_credentials_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.dev_user_credentials + ADD CONSTRAINT dev_user_credentials_pkey PRIMARY KEY (id); + + +-- +-- Name: email_layout_config email_layout_config_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_layout_config + ADD CONSTRAINT email_layout_config_pkey PRIMARY KEY (id); + + +-- +-- Name: email_layout_config email_layout_config_tenant_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_layout_config + ADD CONSTRAINT email_layout_config_tenant_id_key UNIQUE (tenant_id); + + +-- +-- Name: email_templates_global email_templates_global_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_global + ADD CONSTRAINT email_templates_global_key_key UNIQUE (key); + + +-- +-- Name: email_templates_global email_templates_global_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_global + ADD CONSTRAINT email_templates_global_pkey PRIMARY KEY (id); + + +-- +-- Name: email_templates_tenant email_templates_tenant_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_pkey PRIMARY KEY (id); + + +-- +-- Name: email_templates_tenant email_templates_tenant_tenant_id_owner_id_template_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_tenant_id_owner_id_template_key_key UNIQUE (tenant_id, owner_id, template_key); + + +-- +-- Name: entitlements_invalidation entitlements_invalidation_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.entitlements_invalidation + ADD CONSTRAINT entitlements_invalidation_pkey PRIMARY KEY (owner_id); + + +-- +-- Name: features features_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.features + ADD CONSTRAINT features_key_key UNIQUE (key); + + +-- +-- Name: features features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.features + ADD CONSTRAINT features_pkey PRIMARY KEY (id); + + +-- +-- Name: feriados feriados_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_pkey PRIMARY KEY (id); + + +-- +-- Name: feriados feriados_tenant_id_data_nome_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_tenant_id_data_nome_key UNIQUE (tenant_id, data, nome); + + +-- +-- Name: financial_categories financial_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_categories + ADD CONSTRAINT financial_categories_pkey PRIMARY KEY (id); + + +-- +-- Name: financial_exceptions financial_exceptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_exceptions + ADD CONSTRAINT financial_exceptions_pkey PRIMARY KEY (id); + + +-- +-- Name: financial_records financial_records_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_pkey PRIMARY KEY (id); + + +-- +-- Name: global_notices global_notices_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.global_notices + ADD CONSTRAINT global_notices_pkey PRIMARY KEY (id); + + +-- +-- Name: insurance_plan_services insurance_plan_services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plan_services + ADD CONSTRAINT insurance_plan_services_pkey PRIMARY KEY (id); + + +-- +-- Name: insurance_plans insurance_plans_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plans + ADD CONSTRAINT insurance_plans_pkey PRIMARY KEY (id); + + +-- +-- Name: login_carousel_slides login_carousel_slides_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.login_carousel_slides + ADD CONSTRAINT login_carousel_slides_pkey PRIMARY KEY (id); + + +-- +-- Name: medicos medicos_crm_owner_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.medicos + ADD CONSTRAINT medicos_crm_owner_unique UNIQUE NULLS NOT DISTINCT (owner_id, crm); + + +-- +-- Name: medicos medicos_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.medicos + ADD CONSTRAINT medicos_pkey PRIMARY KEY (id); + + +-- +-- Name: module_features module_features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.module_features + ADD CONSTRAINT module_features_pkey PRIMARY KEY (module_id, feature_id); + + +-- +-- Name: modules modules_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.modules + ADD CONSTRAINT modules_key_key UNIQUE (key); + + +-- +-- Name: modules modules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.modules + ADD CONSTRAINT modules_pkey PRIMARY KEY (id); + + +-- +-- Name: notice_dismissals notice_dismissals_notice_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_notice_id_user_id_key UNIQUE (notice_id, user_id); + + +-- +-- Name: notice_dismissals notice_dismissals_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_channels notification_channels_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_channels + ADD CONSTRAINT notification_channels_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_logs notification_logs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_logs + ADD CONSTRAINT notification_logs_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_preferences notification_preferences_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_preferences + ADD CONSTRAINT notification_preferences_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_queue notification_queue_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_queue + ADD CONSTRAINT notification_queue_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_schedules notification_schedules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_schedules + ADD CONSTRAINT notification_schedules_pkey PRIMARY KEY (id); + + +-- +-- Name: notification_templates notification_templates_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_templates + ADD CONSTRAINT notification_templates_pkey PRIMARY KEY (id); + + +-- +-- Name: notifications notifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT notifications_pkey PRIMARY KEY (id); + + +-- +-- Name: owner_users owner_users_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.owner_users + ADD CONSTRAINT owner_users_pkey PRIMARY KEY (owner_id, user_id); + + +-- +-- Name: patient_contacts patient_contacts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_contacts + ADD CONSTRAINT patient_contacts_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_discounts patient_discounts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_discounts + ADD CONSTRAINT patient_discounts_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_group_patient patient_group_patient_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_group_patient + ADD CONSTRAINT patient_group_patient_pkey PRIMARY KEY (patient_group_id, patient_id); + + +-- +-- Name: patient_groups patient_groups_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_groups + ADD CONSTRAINT patient_groups_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_intake_requests patient_intake_requests_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_intake_requests + ADD CONSTRAINT patient_intake_requests_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_invites patient_invites_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_invites + ADD CONSTRAINT patient_invites_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_invites patient_invites_token_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_invites + ADD CONSTRAINT patient_invites_token_key UNIQUE (token); + + +-- +-- Name: patient_patient_tag patient_patient_tag_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT patient_patient_tag_pkey PRIMARY KEY (patient_id, tag_id); + + +-- +-- Name: patient_status_history patient_status_history_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_support_contacts patient_support_contacts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_support_contacts + ADD CONSTRAINT patient_support_contacts_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_tags patient_tags_owner_name_uniq; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_tags + ADD CONSTRAINT patient_tags_owner_name_uniq UNIQUE (owner_id, nome); + + +-- +-- Name: patient_tags patient_tags_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_tags + ADD CONSTRAINT patient_tags_pkey PRIMARY KEY (id); + + +-- +-- Name: patient_timeline patient_timeline_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_pkey PRIMARY KEY (id); + + +-- +-- Name: patients patients_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_pkey PRIMARY KEY (id); + + +-- +-- Name: payment_settings payment_settings_owner_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_owner_id_key UNIQUE (owner_id); + + +-- +-- Name: payment_settings payment_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_pkey PRIMARY KEY (id); + + +-- +-- Name: plan_features plan_features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_features + ADD CONSTRAINT plan_features_pkey PRIMARY KEY (plan_id, feature_id); + + +-- +-- Name: plan_prices plan_prices_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_prices + ADD CONSTRAINT plan_prices_pkey PRIMARY KEY (id); + + +-- +-- Name: plan_public_bullets plan_public_bullets_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public_bullets + ADD CONSTRAINT plan_public_bullets_pkey PRIMARY KEY (id); + + +-- +-- Name: plan_public plan_public_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public + ADD CONSTRAINT plan_public_pkey PRIMARY KEY (plan_id); + + +-- +-- Name: plans plans_key_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plans + ADD CONSTRAINT plans_key_key UNIQUE (key); + + +-- +-- Name: plans plans_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plans + ADD CONSTRAINT plans_pkey PRIMARY KEY (id); + + +-- +-- Name: professional_pricing professional_pricing_owner_commitment_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.professional_pricing + ADD CONSTRAINT professional_pricing_owner_commitment_key UNIQUE (owner_id, determined_commitment_id); + + +-- +-- Name: professional_pricing professional_pricing_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.professional_pricing + ADD CONSTRAINT professional_pricing_pkey PRIMARY KEY (id); + + +-- +-- Name: profiles profiles_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.profiles + ADD CONSTRAINT profiles_pkey PRIMARY KEY (id); + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_pkey PRIMARY KEY (id); + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_unique UNIQUE (recurrence_id, original_date); + + +-- +-- Name: recurrence_rule_services recurrence_rule_services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rule_services + ADD CONSTRAINT recurrence_rule_services_pkey PRIMARY KEY (id); + + +-- +-- Name: recurrence_rules recurrence_rules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rules + ADD CONSTRAINT recurrence_rules_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_admins saas_admins_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_admins + ADD CONSTRAINT saas_admins_pkey PRIMARY KEY (user_id); + + +-- +-- Name: saas_doc_votos saas_doc_votos_doc_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_doc_id_user_id_key UNIQUE (doc_id, user_id); + + +-- +-- Name: saas_doc_votos saas_doc_votos_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_docs saas_docs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_docs + ADD CONSTRAINT saas_docs_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_faq_itens saas_faq_itens_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_faq_itens + ADD CONSTRAINT saas_faq_itens_pkey PRIMARY KEY (id); + + +-- +-- Name: saas_faq saas_faq_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_faq + ADD CONSTRAINT saas_faq_pkey PRIMARY KEY (id); + + +-- +-- Name: services services_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.services + ADD CONSTRAINT services_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_events subscription_events_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_events + ADD CONSTRAINT subscription_events_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_intents_personal subscription_intents_personal_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_personal + ADD CONSTRAINT subscription_intents_personal_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_intents_legacy subscription_intents_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_legacy + ADD CONSTRAINT subscription_intents_pkey PRIMARY KEY (id); + + +-- +-- Name: subscription_intents_tenant subscription_intents_tenant_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_tenant + ADD CONSTRAINT subscription_intents_tenant_pkey PRIMARY KEY (id); + + +-- +-- Name: subscriptions subscriptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscriptions + ADD CONSTRAINT subscriptions_pkey PRIMARY KEY (id); + + +-- +-- Name: support_sessions support_sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_pkey PRIMARY KEY (id); + + +-- +-- Name: support_sessions support_sessions_token_unique; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_token_unique UNIQUE (token); + + +-- +-- Name: tenant_feature_exceptions_log tenant_feature_exceptions_log_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_feature_exceptions_log + ADD CONSTRAINT tenant_feature_exceptions_log_pkey PRIMARY KEY (id); + + +-- +-- Name: tenant_features tenant_features_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_features + ADD CONSTRAINT tenant_features_pkey PRIMARY KEY (tenant_id, feature_key); + + +-- +-- Name: tenant_invites tenant_invites_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_pkey PRIMARY KEY (id); + + +-- +-- Name: tenant_members tenant_members_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_pkey PRIMARY KEY (id); + + +-- +-- Name: tenant_members tenant_members_tenant_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_tenant_id_user_id_key UNIQUE (tenant_id, user_id); + + +-- +-- Name: tenant_modules tenant_modules_owner_id_module_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_owner_id_module_id_key UNIQUE (owner_id, module_id); + + +-- +-- Name: tenant_modules tenant_modules_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_pkey PRIMARY KEY (id); + + +-- +-- Name: tenants tenants_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenants + ADD CONSTRAINT tenants_pkey PRIMARY KEY (id); + + +-- +-- Name: therapist_payout_records therapist_payout_records_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payout_records + ADD CONSTRAINT therapist_payout_records_pkey PRIMARY KEY (payout_id, financial_record_id); + + +-- +-- Name: therapist_payouts therapist_payouts_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payouts + ADD CONSTRAINT therapist_payouts_pkey PRIMARY KEY (id); + + +-- +-- Name: twilio_subaccount_usage twilio_subaccount_usage_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.twilio_subaccount_usage + ADD CONSTRAINT twilio_subaccount_usage_pkey PRIMARY KEY (id); + + +-- +-- Name: addon_credits uq_addon_credits_tenant_type; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT uq_addon_credits_tenant_type UNIQUE (tenant_id, addon_type); + + +-- +-- Name: notification_channels uq_channel_per_owner; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_channels + ADD CONSTRAINT uq_channel_per_owner UNIQUE NULLS NOT DISTINCT (owner_id, channel, deleted_at); + + +-- +-- Name: notification_preferences uq_notif_prefs_patient_owner; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_preferences + ADD CONSTRAINT uq_notif_prefs_patient_owner UNIQUE NULLS NOT DISTINCT (owner_id, patient_id, deleted_at); + + +-- +-- Name: notification_queue uq_notif_queue_idempotency; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_queue + ADD CONSTRAINT uq_notif_queue_idempotency UNIQUE (idempotency_key); + + +-- +-- Name: notification_schedules uq_notif_schedule_owner; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_schedules + ADD CONSTRAINT uq_notif_schedule_owner UNIQUE NULLS NOT DISTINCT (owner_id, schedule_key, deleted_at); + + +-- +-- Name: notification_templates uq_notif_template_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_templates + ADD CONSTRAINT uq_notif_template_key UNIQUE NULLS NOT DISTINCT (tenant_id, owner_id, key, deleted_at); + + +-- +-- Name: user_settings user_settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_settings + ADD CONSTRAINT user_settings_pkey PRIMARY KEY (user_id); + + +-- +-- Name: messages messages_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages + ADD CONSTRAINT messages_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_26 messages_2026_03_26_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_26 + ADD CONSTRAINT messages_2026_03_26_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_27 messages_2026_03_27_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_27 + ADD CONSTRAINT messages_2026_03_27_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_28 messages_2026_03_28_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_28 + ADD CONSTRAINT messages_2026_03_28_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_29 messages_2026_03_29_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_29 + ADD CONSTRAINT messages_2026_03_29_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_30 messages_2026_03_30_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_30 + ADD CONSTRAINT messages_2026_03_30_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_03_31 messages_2026_03_31_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_03_31 + ADD CONSTRAINT messages_2026_03_31_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: messages_2026_04_01 messages_2026_04_01_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.messages_2026_04_01 + ADD CONSTRAINT messages_2026_04_01_pkey PRIMARY KEY (id, inserted_at); + + +-- +-- Name: subscription pk_subscription; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.subscription + ADD CONSTRAINT pk_subscription PRIMARY KEY (id); + + +-- +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: realtime; Owner: - +-- + +ALTER TABLE ONLY realtime.schema_migrations + ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: buckets_analytics buckets_analytics_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.buckets_analytics + ADD CONSTRAINT buckets_analytics_pkey PRIMARY KEY (id); + + +-- +-- Name: buckets buckets_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.buckets + ADD CONSTRAINT buckets_pkey PRIMARY KEY (id); + + +-- +-- Name: buckets_vectors buckets_vectors_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.buckets_vectors + ADD CONSTRAINT buckets_vectors_pkey PRIMARY KEY (id); + + +-- +-- Name: iceberg_namespaces iceberg_namespaces_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_namespaces + ADD CONSTRAINT iceberg_namespaces_pkey PRIMARY KEY (id); + + +-- +-- Name: iceberg_tables iceberg_tables_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_tables + ADD CONSTRAINT iceberg_tables_pkey PRIMARY KEY (id); + + +-- +-- Name: migrations migrations_name_key; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.migrations + ADD CONSTRAINT migrations_name_key UNIQUE (name); + + +-- +-- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.migrations + ADD CONSTRAINT migrations_pkey PRIMARY KEY (id); + + +-- +-- Name: objects objects_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.objects + ADD CONSTRAINT objects_pkey PRIMARY KEY (id); + + +-- +-- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads_parts + ADD CONSTRAINT s3_multipart_uploads_parts_pkey PRIMARY KEY (id); + + +-- +-- Name: s3_multipart_uploads s3_multipart_uploads_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads + ADD CONSTRAINT s3_multipart_uploads_pkey PRIMARY KEY (id); + + +-- +-- Name: vector_indexes vector_indexes_pkey; Type: CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.vector_indexes + ADD CONSTRAINT vector_indexes_pkey PRIMARY KEY (id); + + +-- +-- Name: hooks hooks_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: - +-- + +ALTER TABLE ONLY supabase_functions.hooks + ADD CONSTRAINT hooks_pkey PRIMARY KEY (id); + + +-- +-- Name: migrations migrations_pkey; Type: CONSTRAINT; Schema: supabase_functions; Owner: - +-- + +ALTER TABLE ONLY supabase_functions.migrations + ADD CONSTRAINT migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: extensions_tenant_external_id_index; Type: INDEX; Schema: _realtime; Owner: - +-- + +CREATE INDEX extensions_tenant_external_id_index ON _realtime.extensions USING btree (tenant_external_id); + + +-- +-- Name: extensions_tenant_external_id_type_index; Type: INDEX; Schema: _realtime; Owner: - +-- + +CREATE UNIQUE INDEX extensions_tenant_external_id_type_index ON _realtime.extensions USING btree (tenant_external_id, type); + + +-- +-- Name: tenants_external_id_index; Type: INDEX; Schema: _realtime; Owner: - +-- + +CREATE UNIQUE INDEX tenants_external_id_index ON _realtime.tenants USING btree (external_id); + + +-- +-- Name: audit_logs_instance_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX audit_logs_instance_id_idx ON auth.audit_log_entries USING btree (instance_id); + + +-- +-- Name: confirmation_token_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX confirmation_token_idx ON auth.users USING btree (confirmation_token) WHERE ((confirmation_token)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: email_change_token_current_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX email_change_token_current_idx ON auth.users USING btree (email_change_token_current) WHERE ((email_change_token_current)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: email_change_token_new_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX email_change_token_new_idx ON auth.users USING btree (email_change_token_new) WHERE ((email_change_token_new)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: factor_id_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX factor_id_created_at_idx ON auth.mfa_factors USING btree (user_id, created_at); + + +-- +-- Name: flow_state_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX flow_state_created_at_idx ON auth.flow_state USING btree (created_at DESC); + + +-- +-- Name: identities_email_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX identities_email_idx ON auth.identities USING btree (email text_pattern_ops); + + +-- +-- Name: INDEX identities_email_idx; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON INDEX auth.identities_email_idx IS 'Auth: Ensures indexed queries on the email column'; + + +-- +-- Name: identities_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX identities_user_id_idx ON auth.identities USING btree (user_id); + + +-- +-- Name: idx_auth_code; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX idx_auth_code ON auth.flow_state USING btree (auth_code); + + +-- +-- Name: idx_oauth_client_states_created_at; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX idx_oauth_client_states_created_at ON auth.oauth_client_states USING btree (created_at); + + +-- +-- Name: idx_user_id_auth_method; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX idx_user_id_auth_method ON auth.flow_state USING btree (user_id, authentication_method); + + +-- +-- Name: mfa_challenge_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX mfa_challenge_created_at_idx ON auth.mfa_challenges USING btree (created_at DESC); + + +-- +-- Name: mfa_factors_user_friendly_name_unique; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX mfa_factors_user_friendly_name_unique ON auth.mfa_factors USING btree (friendly_name, user_id) WHERE (TRIM(BOTH FROM friendly_name) <> ''::text); + + +-- +-- Name: mfa_factors_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX mfa_factors_user_id_idx ON auth.mfa_factors USING btree (user_id); + + +-- +-- Name: oauth_auth_pending_exp_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_auth_pending_exp_idx ON auth.oauth_authorizations USING btree (expires_at) WHERE (status = 'pending'::auth.oauth_authorization_status); + + +-- +-- Name: oauth_clients_deleted_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_clients_deleted_at_idx ON auth.oauth_clients USING btree (deleted_at); + + +-- +-- Name: oauth_consents_active_client_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_consents_active_client_idx ON auth.oauth_consents USING btree (client_id) WHERE (revoked_at IS NULL); + + +-- +-- Name: oauth_consents_active_user_client_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_consents_active_user_client_idx ON auth.oauth_consents USING btree (user_id, client_id) WHERE (revoked_at IS NULL); + + +-- +-- Name: oauth_consents_user_order_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX oauth_consents_user_order_idx ON auth.oauth_consents USING btree (user_id, granted_at DESC); + + +-- +-- Name: one_time_tokens_relates_to_hash_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX one_time_tokens_relates_to_hash_idx ON auth.one_time_tokens USING hash (relates_to); + + +-- +-- Name: one_time_tokens_token_hash_hash_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX one_time_tokens_token_hash_hash_idx ON auth.one_time_tokens USING hash (token_hash); + + +-- +-- Name: one_time_tokens_user_id_token_type_key; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX one_time_tokens_user_id_token_type_key ON auth.one_time_tokens USING btree (user_id, token_type); + + +-- +-- Name: reauthentication_token_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX reauthentication_token_idx ON auth.users USING btree (reauthentication_token) WHERE ((reauthentication_token)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: recovery_token_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX recovery_token_idx ON auth.users USING btree (recovery_token) WHERE ((recovery_token)::text !~ '^[0-9 ]*$'::text); + + +-- +-- Name: refresh_tokens_instance_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_instance_id_idx ON auth.refresh_tokens USING btree (instance_id); + + +-- +-- Name: refresh_tokens_instance_id_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_instance_id_user_id_idx ON auth.refresh_tokens USING btree (instance_id, user_id); + + +-- +-- Name: refresh_tokens_parent_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_parent_idx ON auth.refresh_tokens USING btree (parent); + + +-- +-- Name: refresh_tokens_session_id_revoked_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_session_id_revoked_idx ON auth.refresh_tokens USING btree (session_id, revoked); + + +-- +-- Name: refresh_tokens_updated_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX refresh_tokens_updated_at_idx ON auth.refresh_tokens USING btree (updated_at DESC); + + +-- +-- Name: saml_providers_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_providers_sso_provider_id_idx ON auth.saml_providers USING btree (sso_provider_id); + + +-- +-- Name: saml_relay_states_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_relay_states_created_at_idx ON auth.saml_relay_states USING btree (created_at DESC); + + +-- +-- Name: saml_relay_states_for_email_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_relay_states_for_email_idx ON auth.saml_relay_states USING btree (for_email); + + +-- +-- Name: saml_relay_states_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX saml_relay_states_sso_provider_id_idx ON auth.saml_relay_states USING btree (sso_provider_id); + + +-- +-- Name: sessions_not_after_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sessions_not_after_idx ON auth.sessions USING btree (not_after DESC); + + +-- +-- Name: sessions_oauth_client_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sessions_oauth_client_id_idx ON auth.sessions USING btree (oauth_client_id); + + +-- +-- Name: sessions_user_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sessions_user_id_idx ON auth.sessions USING btree (user_id); + + +-- +-- Name: sso_domains_domain_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX sso_domains_domain_idx ON auth.sso_domains USING btree (lower(domain)); + + +-- +-- Name: sso_domains_sso_provider_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sso_domains_sso_provider_id_idx ON auth.sso_domains USING btree (sso_provider_id); + + +-- +-- Name: sso_providers_resource_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX sso_providers_resource_id_idx ON auth.sso_providers USING btree (lower(resource_id)); + + +-- +-- Name: sso_providers_resource_id_pattern_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX sso_providers_resource_id_pattern_idx ON auth.sso_providers USING btree (resource_id text_pattern_ops); + + +-- +-- Name: unique_phone_factor_per_user; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX unique_phone_factor_per_user ON auth.mfa_factors USING btree (user_id, phone); + + +-- +-- Name: user_id_created_at_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX user_id_created_at_idx ON auth.sessions USING btree (user_id, created_at); + + +-- +-- Name: users_email_partial_key; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE UNIQUE INDEX users_email_partial_key ON auth.users USING btree (email) WHERE (is_sso_user = false); + + +-- +-- Name: INDEX users_email_partial_key; Type: COMMENT; Schema: auth; Owner: - +-- + +COMMENT ON INDEX auth.users_email_partial_key IS 'Auth: A partial unique index that applies only when is_sso_user is false'; + + +-- +-- Name: users_instance_id_email_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX users_instance_id_email_idx ON auth.users USING btree (instance_id, lower((email)::text)); + + +-- +-- Name: users_instance_id_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX users_instance_id_idx ON auth.users USING btree (instance_id); + + +-- +-- Name: users_is_anonymous_idx; Type: INDEX; Schema: auth; Owner: - +-- + +CREATE INDEX users_is_anonymous_idx ON auth.users USING btree (is_anonymous); + + +-- +-- Name: agenda_bloqueios_owner_data_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_owner_data_idx ON public.agenda_bloqueios USING btree (owner_id, data_inicio, data_fim); + + +-- +-- Name: agenda_bloqueios_owner_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_owner_id_idx ON public.agenda_bloqueios USING btree (owner_id); + + +-- +-- Name: agenda_bloqueios_recorrente_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_recorrente_idx ON public.agenda_bloqueios USING btree (owner_id, dia_semana) WHERE (recorrente = true); + + +-- +-- Name: agenda_bloqueios_tenant_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_bloqueios_tenant_id_idx ON public.agenda_bloqueios USING btree (tenant_id); + + +-- +-- Name: agenda_configuracoes_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_configuracoes_tenant_idx ON public.agenda_configuracoes USING btree (tenant_id); + + +-- +-- Name: agenda_configuracoes_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_configuracoes_tenant_owner_idx ON public.agenda_configuracoes USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_eventos_billing_contract_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_billing_contract_idx ON public.agenda_eventos USING btree (billing_contract_id) WHERE (billing_contract_id IS NOT NULL); + + +-- +-- Name: agenda_eventos_insurance_plan_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_insurance_plan_idx ON public.agenda_eventos USING btree (insurance_plan_id); + + +-- +-- Name: agenda_eventos_owner_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_owner_inicio_idx ON public.agenda_eventos USING btree (owner_id, inicio_em); + + +-- +-- Name: agenda_eventos_owner_terapeuta_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_owner_terapeuta_inicio_idx ON public.agenda_eventos USING btree (owner_id, terapeuta_id, inicio_em); + + +-- +-- Name: agenda_eventos_recurrence_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_recurrence_idx ON public.agenda_eventos USING btree (recurrence_id) WHERE (recurrence_id IS NOT NULL); + + +-- +-- Name: agenda_eventos_tenant_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_tenant_inicio_idx ON public.agenda_eventos USING btree (tenant_id, inicio_em); + + +-- +-- Name: agenda_eventos_tenant_owner_inicio_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_eventos_tenant_owner_inicio_idx ON public.agenda_eventos USING btree (tenant_id, owner_id, inicio_em); + + +-- +-- Name: agenda_excecoes_owner_data_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_excecoes_owner_data_idx ON public.agenda_excecoes USING btree (owner_id, data); + + +-- +-- Name: agenda_excecoes_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_excecoes_tenant_idx ON public.agenda_excecoes USING btree (tenant_id); + + +-- +-- Name: agenda_excecoes_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_excecoes_tenant_owner_idx ON public.agenda_excecoes USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_online_slots_owner_weekday_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_online_slots_owner_weekday_idx ON public.agenda_online_slots USING btree (owner_id, weekday); + + +-- +-- Name: agenda_online_slots_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_online_slots_tenant_idx ON public.agenda_online_slots USING btree (tenant_id); + + +-- +-- Name: agenda_online_slots_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_online_slots_tenant_owner_idx ON public.agenda_online_slots USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_regras_semanais_owner_dia_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_regras_semanais_owner_dia_idx ON public.agenda_regras_semanais USING btree (owner_id, dia_semana); + + +-- +-- Name: agenda_regras_semanais_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_regras_semanais_tenant_idx ON public.agenda_regras_semanais USING btree (tenant_id); + + +-- +-- Name: agenda_regras_semanais_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_regras_semanais_tenant_owner_idx ON public.agenda_regras_semanais USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_slots_bloqueados_semanais_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_bloqueados_semanais_tenant_idx ON public.agenda_slots_bloqueados_semanais USING btree (tenant_id); + + +-- +-- Name: agenda_slots_bloqueados_semanais_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_bloqueados_semanais_tenant_owner_idx ON public.agenda_slots_bloqueados_semanais USING btree (tenant_id, owner_id); + + +-- +-- Name: agenda_slots_regras_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_regras_tenant_idx ON public.agenda_slots_regras USING btree (tenant_id); + + +-- +-- Name: agenda_slots_regras_tenant_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agenda_slots_regras_tenant_owner_idx ON public.agenda_slots_regras USING btree (tenant_id, owner_id); + + +-- +-- Name: agendador_cfg_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_cfg_tenant_idx ON public.agendador_configuracoes USING btree (tenant_id); + + +-- +-- Name: agendador_sol_data_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_sol_data_idx ON public.agendador_solicitacoes USING btree (data_solicitada, hora_solicitada); + + +-- +-- Name: agendador_sol_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_sol_owner_idx ON public.agendador_solicitacoes USING btree (owner_id, status); + + +-- +-- Name: agendador_sol_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX agendador_sol_tenant_idx ON public.agendador_solicitacoes USING btree (tenant_id); + + +-- +-- Name: billing_contracts_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX billing_contracts_owner_idx ON public.billing_contracts USING btree (owner_id); + + +-- +-- Name: billing_contracts_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX billing_contracts_patient_idx ON public.billing_contracts USING btree (patient_id); + + +-- +-- Name: billing_contracts_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX billing_contracts_tenant_idx ON public.billing_contracts USING btree (tenant_id); + + +-- +-- Name: commitment_services_commitment_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_services_commitment_idx ON public.commitment_services USING btree (commitment_id); + + +-- +-- Name: commitment_services_service_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_services_service_idx ON public.commitment_services USING btree (service_id); + + +-- +-- Name: commitment_time_logs_calendar_event_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_time_logs_calendar_event_idx ON public.commitment_time_logs USING btree (calendar_event_id); + + +-- +-- Name: commitment_time_logs_commitment_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_time_logs_commitment_idx ON public.commitment_time_logs USING btree (commitment_id, created_at DESC); + + +-- +-- Name: commitment_time_logs_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX commitment_time_logs_tenant_idx ON public.commitment_time_logs USING btree (tenant_id, created_at DESC); + + +-- +-- Name: determined_commitment_fields_commitment_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitment_fields_commitment_idx ON public.determined_commitment_fields USING btree (commitment_id, sort_order); + + +-- +-- Name: determined_commitment_fields_key_uniq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX determined_commitment_fields_key_uniq ON public.determined_commitment_fields USING btree (commitment_id, key); + + +-- +-- Name: determined_commitment_fields_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitment_fields_tenant_idx ON public.determined_commitment_fields USING btree (tenant_id); + + +-- +-- Name: determined_commitments_active_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitments_active_idx ON public.determined_commitments USING btree (tenant_id, active); + + +-- +-- Name: determined_commitments_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX determined_commitments_tenant_idx ON public.determined_commitments USING btree (tenant_id); + + +-- +-- Name: determined_commitments_tenant_name_uniq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX determined_commitments_tenant_name_uniq ON public.determined_commitments USING btree (tenant_id, lower(name)); + + +-- +-- Name: feriados_global_unique; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX feriados_global_unique ON public.feriados USING btree (data, nome) WHERE (tenant_id IS NULL); + + +-- +-- Name: financial_exceptions_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX financial_exceptions_owner_idx ON public.financial_exceptions USING btree (owner_id); + + +-- +-- Name: financial_exceptions_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX financial_exceptions_tenant_idx ON public.financial_exceptions USING btree (tenant_id); + + +-- +-- Name: idx_addon_credits_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_credits_tenant ON public.addon_credits USING btree (tenant_id) WHERE (is_active = true); + + +-- +-- Name: idx_addon_credits_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_credits_type ON public.addon_credits USING btree (addon_type) WHERE (is_active = true); + + +-- +-- Name: idx_addon_products_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_products_active ON public.addon_products USING btree (is_active, is_visible) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_addon_products_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_products_type ON public.addon_products USING btree (addon_type) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_addon_tx_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_created ON public.addon_transactions USING btree (created_at DESC); + + +-- +-- Name: idx_addon_tx_queue; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_queue ON public.addon_transactions USING btree (queue_id) WHERE (queue_id IS NOT NULL); + + +-- +-- Name: idx_addon_tx_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_tenant ON public.addon_transactions USING btree (tenant_id, addon_type); + + +-- +-- Name: idx_addon_tx_type; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_addon_tx_type ON public.addon_transactions USING btree (type); + + +-- +-- Name: idx_agenda_eventos_determined_commitment_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_agenda_eventos_determined_commitment_id ON public.agenda_eventos USING btree (determined_commitment_id); + + +-- +-- Name: idx_agenda_excecoes_owner_data; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_agenda_excecoes_owner_data ON public.agenda_excecoes USING btree (owner_id, data); + + +-- +-- Name: idx_agenda_slots_regras_owner_dia; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_agenda_slots_regras_owner_dia ON public.agenda_slots_regras USING btree (owner_id, dia_semana); + + +-- +-- Name: idx_email_templates_global_domain; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_global_domain ON public.email_templates_global USING btree (domain) WHERE (is_active = true); + + +-- +-- Name: idx_email_templates_global_key; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_global_key ON public.email_templates_global USING btree (key) WHERE (is_active = true); + + +-- +-- Name: idx_email_templates_tenant_lookup; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_tenant_lookup ON public.email_templates_tenant USING btree (tenant_id, template_key) WHERE (enabled = true); + + +-- +-- Name: idx_email_templates_tenant_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_email_templates_tenant_owner ON public.email_templates_tenant USING btree (owner_id, template_key) WHERE ((enabled = true) AND (owner_id IS NOT NULL)); + + +-- +-- Name: idx_financial_categories_user_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_categories_user_id ON public.financial_categories USING btree (user_id); + + +-- +-- Name: idx_financial_records_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_active ON public.financial_records USING btree (owner_id, paid_at DESC) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_financial_records_agenda_evento_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_agenda_evento_id ON public.financial_records USING btree (agenda_evento_id) WHERE (agenda_evento_id IS NOT NULL); + + +-- +-- Name: idx_financial_records_category_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_category_id ON public.financial_records USING btree (category_id) WHERE (category_id IS NOT NULL); + + +-- +-- Name: idx_financial_records_due_date; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_due_date ON public.financial_records USING btree (due_date); + + +-- +-- Name: idx_financial_records_installment_group; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_installment_group ON public.financial_records USING btree (installment_group) WHERE (installment_group IS NOT NULL); + + +-- +-- Name: idx_financial_records_owner_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_owner_id ON public.financial_records USING btree (owner_id); + + +-- +-- Name: idx_financial_records_paid_at; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_paid_at ON public.financial_records USING btree (paid_at DESC); + + +-- +-- Name: idx_financial_records_patient_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_patient_id ON public.financial_records USING btree (patient_id); + + +-- +-- Name: idx_financial_records_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_status ON public.financial_records USING btree (status) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_financial_records_tenant_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_tenant_active ON public.financial_records USING btree (tenant_id, paid_at DESC) WHERE ((deleted_at IS NULL) AND (tenant_id IS NOT NULL)); + + +-- +-- Name: idx_financial_records_tenant_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_tenant_id ON public.financial_records USING btree (tenant_id); + + +-- +-- Name: idx_financial_records_type_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_financial_records_type_status ON public.financial_records USING btree (type, status); + + +-- +-- Name: idx_global_notices_active_priority; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_global_notices_active_priority ON public.global_notices USING btree (is_active, priority DESC, starts_at, ends_at); + + +-- +-- Name: idx_intakes_converted_patient_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_converted_patient_id ON public.patient_intake_requests USING btree (converted_patient_id); + + +-- +-- Name: idx_intakes_owner_cpf; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_owner_cpf ON public.patient_intake_requests USING btree (owner_id, cpf); + + +-- +-- Name: idx_intakes_owner_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_owner_created ON public.patient_intake_requests USING btree (owner_id, created_at DESC); + + +-- +-- Name: idx_intakes_owner_status_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_owner_status_created ON public.patient_intake_requests USING btree (owner_id, status, created_at DESC); + + +-- +-- Name: idx_intakes_status_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_intakes_status_created ON public.patient_intake_requests USING btree (status, created_at DESC); + + +-- +-- Name: idx_notice_dismissals_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notice_dismissals_user ON public.notice_dismissals USING btree (user_id, notice_id); + + +-- +-- Name: idx_notif_channels_owner_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_channels_owner_active ON public.notification_channels USING btree (owner_id, channel) WHERE ((is_active = true) AND (deleted_at IS NULL)); + + +-- +-- Name: idx_notif_channels_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_channels_tenant ON public.notification_channels USING btree (tenant_id) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_logs_owner_date; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_owner_date ON public.notification_logs USING btree (owner_id, created_at DESC); + + +-- +-- Name: idx_notif_logs_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_patient ON public.notification_logs USING btree (patient_id, created_at DESC); + + +-- +-- Name: idx_notif_logs_provider_msg; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_provider_msg ON public.notification_logs USING btree (provider_message_id) WHERE (provider_message_id IS NOT NULL); + + +-- +-- Name: idx_notif_logs_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_status ON public.notification_logs USING btree (status, created_at DESC); + + +-- +-- Name: idx_notif_logs_tenant_date; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_logs_tenant_date ON public.notification_logs USING btree (tenant_id, created_at DESC); + + +-- +-- Name: idx_notif_prefs_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_prefs_owner ON public.notification_preferences USING btree (owner_id) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_prefs_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_prefs_patient ON public.notification_preferences USING btree (patient_id) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_prefs_whatsapp_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_prefs_whatsapp_active ON public.notification_preferences USING btree (owner_id, patient_id) WHERE ((whatsapp_opt_in = true) AND (deleted_at IS NULL)); + + +-- +-- Name: idx_notif_queue_evento; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_evento ON public.notification_queue USING btree (agenda_evento_id) WHERE (status = ANY (ARRAY['pendente'::text, 'processando'::text])); + + +-- +-- Name: idx_notif_queue_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_patient ON public.notification_queue USING btree (patient_id, channel) WHERE (status = 'pendente'::text); + + +-- +-- Name: idx_notif_queue_pending; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_pending ON public.notification_queue USING btree (scheduled_at) WHERE (status = 'pendente'::text); + + +-- +-- Name: idx_notif_queue_processing; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_processing ON public.notification_queue USING btree (status, updated_at) WHERE (status = 'processando'::text); + + +-- +-- Name: idx_notif_queue_retry; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_retry ON public.notification_queue USING btree (next_retry_at) WHERE ((status = 'pendente'::text) AND (attempts > 0)); + + +-- +-- Name: idx_notif_queue_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_queue_tenant ON public.notification_queue USING btree (tenant_id, created_at DESC); + + +-- +-- Name: idx_notif_schedules_owner_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_schedules_owner_active ON public.notification_schedules USING btree (owner_id, event_type) WHERE ((is_active = true) AND (deleted_at IS NULL)); + + +-- +-- Name: idx_notif_templates_default; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_templates_default ON public.notification_templates USING btree (channel, event_type) WHERE ((is_default = true) AND (deleted_at IS NULL) AND (tenant_id IS NULL)); + + +-- +-- Name: idx_notif_templates_lookup; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_templates_lookup ON public.notification_templates USING btree (channel, event_type, is_active) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_notif_templates_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notif_templates_tenant ON public.notification_templates USING btree (tenant_id, channel, event_type) WHERE ((deleted_at IS NULL) AND (is_active = true)); + + +-- +-- Name: idx_notification_channels_twilio_subaccount_sid; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_notification_channels_twilio_subaccount_sid ON public.notification_channels USING btree (twilio_subaccount_sid) WHERE (twilio_subaccount_sid IS NOT NULL); + + +-- +-- Name: idx_patient_contacts_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_contacts_patient ON public.patient_contacts USING btree (patient_id); + + +-- +-- Name: idx_patient_contacts_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_contacts_tenant ON public.patient_contacts USING btree (tenant_id); + + +-- +-- Name: idx_patient_group_patient_group_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_group_patient_group_id ON public.patient_group_patient USING btree (patient_group_id); + + +-- +-- Name: idx_patient_groups_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_groups_owner ON public.patient_groups USING btree (owner_id); + + +-- +-- Name: idx_patient_groups_owner_system_nome; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_groups_owner_system_nome ON public.patient_groups USING btree (owner_id, is_system, nome); + + +-- +-- Name: idx_patient_tags_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patient_tags_owner ON public.patient_tags USING btree (owner_id); + + +-- +-- Name: idx_patients_created_at; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_created_at ON public.patients USING btree (created_at DESC); + + +-- +-- Name: idx_patients_last_attended; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_last_attended ON public.patients USING btree (last_attended_at DESC); + + +-- +-- Name: idx_patients_origem; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_origem ON public.patients USING btree (tenant_id, origem) WHERE (origem IS NOT NULL); + + +-- +-- Name: idx_patients_owner_email_principal; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_owner_email_principal ON public.patients USING btree (owner_id, email_principal); + + +-- +-- Name: idx_patients_owner_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_owner_id ON public.patients USING btree (owner_id); + + +-- +-- Name: idx_patients_owner_nome; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_owner_nome ON public.patients USING btree (owner_id, nome_completo); + + +-- +-- Name: idx_patients_responsible_member; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_responsible_member ON public.patients USING btree (responsible_member_id); + + +-- +-- Name: idx_patients_risco_elevado; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_risco_elevado ON public.patients USING btree (tenant_id, risco_elevado) WHERE (risco_elevado = true); + + +-- +-- Name: idx_patients_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_status ON public.patients USING btree (status); + + +-- +-- Name: idx_patients_status_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_status_tenant ON public.patients USING btree (tenant_id, status); + + +-- +-- Name: idx_patients_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_tenant ON public.patients USING btree (tenant_id); + + +-- +-- Name: idx_patients_tenant_email_norm; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_patients_tenant_email_norm ON public.patients USING btree (tenant_id, lower(TRIM(BOTH FROM email_principal))); + + +-- +-- Name: idx_pgp_group; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pgp_group ON public.patient_group_patient USING btree (patient_group_id); + + +-- +-- Name: idx_pgp_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pgp_patient ON public.patient_group_patient USING btree (patient_id); + + +-- +-- Name: idx_ppt_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_ppt_patient ON public.patient_patient_tag USING btree (patient_id); + + +-- +-- Name: idx_ppt_tag; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_ppt_tag ON public.patient_patient_tag USING btree (tag_id); + + +-- +-- Name: idx_psh_patient; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_psh_patient ON public.patient_status_history USING btree (patient_id, alterado_em DESC); + + +-- +-- Name: idx_psh_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_psh_tenant ON public.patient_status_history USING btree (tenant_id, alterado_em DESC); + + +-- +-- Name: idx_pt_evento_tipo; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pt_evento_tipo ON public.patient_timeline USING btree (patient_id, evento_tipo); + + +-- +-- Name: idx_pt_patient_ocorrido; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pt_patient_ocorrido ON public.patient_timeline USING btree (patient_id, ocorrido_em DESC); + + +-- +-- Name: idx_pt_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_pt_tenant ON public.patient_timeline USING btree (tenant_id, ocorrido_em DESC); + + +-- +-- Name: idx_slots_bloq_owner_dia; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_slots_bloq_owner_dia ON public.agenda_slots_bloqueados_semanais USING btree (owner_id, dia_semana); + + +-- +-- Name: idx_subscription_intents_plan_interval; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_subscription_intents_plan_interval ON public.subscription_intents_legacy USING btree (plan_key, "interval"); + + +-- +-- Name: idx_subscription_intents_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_subscription_intents_status ON public.subscription_intents_legacy USING btree (status); + + +-- +-- Name: idx_subscription_intents_user_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_subscription_intents_user_id ON public.subscription_intents_legacy USING btree (user_id); + + +-- +-- Name: idx_tenant_features_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_tenant_features_tenant ON public.tenant_features USING btree (tenant_id); + + +-- +-- Name: idx_tenant_invites_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_tenant_invites_tenant ON public.tenant_invites USING btree (tenant_id); + + +-- +-- Name: idx_tenant_invites_token; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_tenant_invites_token ON public.tenant_invites USING btree (token); + + +-- +-- Name: idx_therapist_payout_records_financial_record_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payout_records_financial_record_id ON public.therapist_payout_records USING btree (financial_record_id); + + +-- +-- Name: idx_therapist_payouts_owner_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_owner_id ON public.therapist_payouts USING btree (owner_id); + + +-- +-- Name: idx_therapist_payouts_period; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_period ON public.therapist_payouts USING btree (period_start, period_end); + + +-- +-- Name: idx_therapist_payouts_status; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_status ON public.therapist_payouts USING btree (status) WHERE (status = 'pending'::text); + + +-- +-- Name: idx_therapist_payouts_tenant_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_therapist_payouts_tenant_id ON public.therapist_payouts USING btree (tenant_id); + + +-- +-- Name: idx_twilio_usage_channel; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_twilio_usage_channel ON public.twilio_subaccount_usage USING btree (channel_id, period_start DESC); + + +-- +-- Name: idx_twilio_usage_tenant_period; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_twilio_usage_tenant_period ON public.twilio_subaccount_usage USING btree (tenant_id, period_start DESC); + + +-- +-- Name: idx_twilio_usage_unique_period; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX idx_twilio_usage_unique_period ON public.twilio_subaccount_usage USING btree (channel_id, period_start, period_end); + + +-- +-- Name: insurance_plans_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX insurance_plans_owner_idx ON public.insurance_plans USING btree (owner_id); + + +-- +-- Name: insurance_plans_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX insurance_plans_tenant_idx ON public.insurance_plans USING btree (tenant_id); + + +-- +-- Name: ix_plan_prices_plan; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ix_plan_prices_plan ON public.plan_prices USING btree (plan_id); + + +-- +-- Name: ix_plan_public_bullets_plan; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ix_plan_public_bullets_plan ON public.plan_public_bullets USING btree (plan_id); + + +-- +-- Name: ix_plan_public_sort; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ix_plan_public_sort ON public.plan_public USING btree (sort_order); + + +-- +-- Name: medicos_especialidade_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX medicos_especialidade_idx ON public.medicos USING btree (especialidade); + + +-- +-- Name: medicos_nome_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX medicos_nome_idx ON public.medicos USING btree (nome); + + +-- +-- Name: medicos_nome_trgm_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX medicos_nome_trgm_idx ON public.medicos USING gin (nome public.gin_trgm_ops); + + +-- +-- Name: medicos_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX medicos_owner_idx ON public.medicos USING btree (owner_id); + + +-- +-- Name: medicos_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX medicos_tenant_idx ON public.medicos USING btree (tenant_id); + + +-- +-- Name: notifications_owner_created; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX notifications_owner_created ON public.notifications USING btree (owner_id, created_at DESC); + + +-- +-- Name: notifications_owner_unread; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX notifications_owner_unread ON public.notifications USING btree (owner_id, read_at) WHERE (read_at IS NULL); + + +-- +-- Name: patient_discounts_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_discounts_owner_idx ON public.patient_discounts USING btree (owner_id); + + +-- +-- Name: patient_discounts_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_discounts_patient_idx ON public.patient_discounts USING btree (patient_id); + + +-- +-- Name: patient_discounts_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_discounts_tenant_idx ON public.patient_discounts USING btree (tenant_id); + + +-- +-- Name: patient_group_patient_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_group_patient_tenant_idx ON public.patient_group_patient USING btree (tenant_id); + + +-- +-- Name: patient_groups_owner_nome_uniq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX patient_groups_owner_nome_uniq ON public.patient_groups USING btree (owner_id, nome); + + +-- +-- Name: patient_groups_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_groups_tenant_idx ON public.patient_groups USING btree (tenant_id); + + +-- +-- Name: patient_intake_owner_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_owner_id_idx ON public.patient_intake_requests USING btree (owner_id); + + +-- +-- Name: patient_intake_requests_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_requests_tenant_idx ON public.patient_intake_requests USING btree (tenant_id); + + +-- +-- Name: patient_intake_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_status_idx ON public.patient_intake_requests USING btree (status); + + +-- +-- Name: patient_intake_token_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_intake_token_idx ON public.patient_intake_requests USING btree (token); + + +-- +-- Name: patient_invites_one_active_per_owner; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX patient_invites_one_active_per_owner ON public.patient_invites USING btree (owner_id) WHERE (active = true); + + +-- +-- Name: patient_invites_owner_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_invites_owner_id_idx ON public.patient_invites USING btree (owner_id); + + +-- +-- Name: patient_invites_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_invites_tenant_idx ON public.patient_invites USING btree (tenant_id); + + +-- +-- Name: patient_invites_token_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_invites_token_idx ON public.patient_invites USING btree (token); + + +-- +-- Name: patient_patient_tag_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_patient_tag_tenant_idx ON public.patient_patient_tag USING btree (tenant_id); + + +-- +-- Name: patient_tags_owner_name_uq; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX patient_tags_owner_name_uq ON public.patient_tags USING btree (owner_id, lower(nome)); + + +-- +-- Name: patient_tags_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patient_tags_tenant_idx ON public.patient_tags USING btree (tenant_id); + + +-- +-- Name: patients_convenio_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patients_convenio_id_idx ON public.patients USING btree (convenio_id); + + +-- +-- Name: patients_etnia_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patients_etnia_idx ON public.patients USING btree (etnia); + + +-- +-- Name: patients_pronomes_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX patients_pronomes_idx ON public.patients USING btree (pronomes); + + +-- +-- Name: payment_settings_tenant_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX payment_settings_tenant_id_idx ON public.payment_settings USING btree (tenant_id); + + +-- +-- Name: ppt_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ppt_owner_idx ON public.patient_patient_tag USING btree (owner_id); + + +-- +-- Name: ppt_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ppt_patient_idx ON public.patient_patient_tag USING btree (patient_id); + + +-- +-- Name: ppt_tag_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX ppt_tag_idx ON public.patient_patient_tag USING btree (tag_id); + + +-- +-- Name: professional_pricing_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX professional_pricing_tenant_idx ON public.professional_pricing USING btree (tenant_id); + + +-- +-- Name: profiles_work_description_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX profiles_work_description_idx ON public.profiles USING btree (work_description) WHERE (work_description IS NOT NULL); + + +-- +-- Name: psc_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX psc_owner_idx ON public.patient_support_contacts USING btree (owner_id); + + +-- +-- Name: psc_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX psc_patient_idx ON public.patient_support_contacts USING btree (patient_id); + + +-- +-- Name: recurrence_exceptions_rule_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_exceptions_rule_idx ON public.recurrence_exceptions USING btree (recurrence_id); + + +-- +-- Name: recurrence_exceptions_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_exceptions_tenant_idx ON public.recurrence_exceptions USING btree (tenant_id); + + +-- +-- Name: recurrence_rule_services_rule_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rule_services_rule_idx ON public.recurrence_rule_services USING btree (rule_id); + + +-- +-- Name: recurrence_rule_services_service_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rule_services_service_idx ON public.recurrence_rule_services USING btree (service_id); + + +-- +-- Name: recurrence_rules_active_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_active_idx ON public.recurrence_rules USING btree (owner_id, status) WHERE (status = 'ativo'::text); + + +-- +-- Name: recurrence_rules_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_owner_idx ON public.recurrence_rules USING btree (owner_id); + + +-- +-- Name: recurrence_rules_patient_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_patient_idx ON public.recurrence_rules USING btree (patient_id); + + +-- +-- Name: recurrence_rules_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX recurrence_rules_tenant_idx ON public.recurrence_rules USING btree (tenant_id); + + +-- +-- Name: saas_doc_votos_doc_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_doc_votos_doc_id_idx ON public.saas_doc_votos USING btree (doc_id); + + +-- +-- Name: saas_doc_votos_user_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_doc_votos_user_id_idx ON public.saas_doc_votos USING btree (user_id); + + +-- +-- Name: saas_docs_categoria_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_docs_categoria_idx ON public.saas_docs USING btree (categoria); + + +-- +-- Name: saas_docs_exibir_no_faq_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_docs_exibir_no_faq_idx ON public.saas_docs USING btree (exibir_no_faq) WHERE (exibir_no_faq = true); + + +-- +-- Name: saas_docs_path_ativo_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_docs_path_ativo_idx ON public.saas_docs USING btree (pagina_path, ativo); + + +-- +-- Name: saas_faq_ativo_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_ativo_idx ON public.saas_faq USING btree (ativo); + + +-- +-- Name: saas_faq_categoria_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_categoria_idx ON public.saas_faq USING btree (categoria); + + +-- +-- Name: saas_faq_fts_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_fts_idx ON public.saas_faq USING gin (to_tsvector('portuguese'::regconfig, ((COALESCE(pergunta, ''::text) || ' '::text) || COALESCE(conteudo, ''::text)))); + + +-- +-- Name: saas_faq_itens_ativo_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_itens_ativo_idx ON public.saas_faq_itens USING btree (ativo); + + +-- +-- Name: saas_faq_itens_doc_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_itens_doc_id_idx ON public.saas_faq_itens USING btree (doc_id); + + +-- +-- Name: saas_faq_pagina_path_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_pagina_path_idx ON public.saas_faq USING btree (pagina_path); + + +-- +-- Name: saas_faq_publico_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_publico_idx ON public.saas_faq USING btree (publico); + + +-- +-- Name: saas_faq_votos_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX saas_faq_votos_idx ON public.saas_faq USING btree (votos DESC); + + +-- +-- Name: services_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX services_owner_idx ON public.services USING btree (owner_id); + + +-- +-- Name: services_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX services_tenant_idx ON public.services USING btree (tenant_id); + + +-- +-- Name: sint_personal_created_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_personal_created_idx ON public.subscription_intents_personal USING btree (created_at DESC); + + +-- +-- Name: sint_personal_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_personal_status_idx ON public.subscription_intents_personal USING btree (status); + + +-- +-- Name: sint_tenant_created_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_tenant_created_idx ON public.subscription_intents_tenant USING btree (created_at DESC); + + +-- +-- Name: sint_tenant_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_tenant_status_idx ON public.subscription_intents_tenant USING btree (status); + + +-- +-- Name: sint_tenant_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX sint_tenant_tenant_idx ON public.subscription_intents_tenant USING btree (tenant_id); + + +-- +-- Name: subscription_events_created_at_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_created_at_idx ON public.subscription_events USING btree (created_at DESC); + + +-- +-- Name: subscription_events_owner_ref_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_owner_ref_idx ON public.subscription_events USING btree (owner_type, owner_ref); + + +-- +-- Name: subscription_events_sub_created_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_sub_created_idx ON public.subscription_events USING btree (subscription_id, created_at DESC); + + +-- +-- Name: subscription_events_subscription_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscription_events_subscription_id_idx ON public.subscription_events USING btree (subscription_id); + + +-- +-- Name: subscriptions_one_active_per_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX subscriptions_one_active_per_tenant ON public.subscriptions USING btree (tenant_id) WHERE (status = 'active'::text); + + +-- +-- Name: subscriptions_one_active_per_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX subscriptions_one_active_per_user ON public.subscriptions USING btree (user_id) WHERE (status = 'active'::text); + + +-- +-- Name: subscriptions_one_active_per_user_personal; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX subscriptions_one_active_per_user_personal ON public.subscriptions USING btree (user_id) WHERE ((tenant_id IS NULL) AND (status = 'active'::text)); + + +-- +-- Name: subscriptions_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_owner_idx ON public.subscriptions USING btree (user_id); + + +-- +-- Name: subscriptions_plan_key_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_plan_key_idx ON public.subscriptions USING btree (plan_key); + + +-- +-- Name: subscriptions_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_status_idx ON public.subscriptions USING btree (status); + + +-- +-- Name: subscriptions_tenant_id_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_tenant_id_idx ON public.subscriptions USING btree (tenant_id); + + +-- +-- Name: subscriptions_tenant_period_end_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_tenant_period_end_idx ON public.subscriptions USING btree (tenant_id, current_period_end); + + +-- +-- Name: subscriptions_tenant_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_tenant_status_idx ON public.subscriptions USING btree (tenant_id, status); + + +-- +-- Name: subscriptions_user_status_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX subscriptions_user_status_idx ON public.subscriptions USING btree (user_id, status, created_at DESC); + + +-- +-- Name: support_sessions_expires_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX support_sessions_expires_idx ON public.support_sessions USING btree (expires_at); + + +-- +-- Name: support_sessions_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX support_sessions_tenant_idx ON public.support_sessions USING btree (tenant_id); + + +-- +-- Name: support_sessions_token_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX support_sessions_token_idx ON public.support_sessions USING btree (token); + + +-- +-- Name: tenant_members_tenant_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX tenant_members_tenant_idx ON public.tenant_members USING btree (tenant_id); + + +-- +-- Name: tenant_members_user_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX tenant_members_user_idx ON public.tenant_members USING btree (user_id); + + +-- +-- Name: tenant_modules_owner_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX tenant_modules_owner_idx ON public.tenant_modules USING btree (owner_id); + + +-- +-- Name: unique_member_per_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX unique_member_per_tenant ON public.tenant_members USING btree (tenant_id, user_id); + + +-- +-- Name: uq_patient_contacts_primario; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_patient_contacts_primario ON public.patient_contacts USING btree (patient_id) WHERE ((is_primario = true) AND (ativo = true)); + + +-- +-- Name: uq_patients_tenant_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_patients_tenant_user ON public.patients USING btree (tenant_id, user_id) WHERE (user_id IS NOT NULL); + + +-- +-- Name: uq_plan_price_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_plan_price_active ON public.plan_prices USING btree (plan_id, "interval", currency) WHERE ((is_active = true) AND (active_to IS NULL)); + + +-- +-- Name: uq_plan_prices_active; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_plan_prices_active ON public.plan_prices USING btree (plan_id, "interval") WHERE (is_active = true); + + +-- +-- Name: uq_subscriptions_active_by_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_subscriptions_active_by_tenant ON public.subscriptions USING btree (tenant_id) WHERE ((tenant_id IS NOT NULL) AND (status = 'active'::text)); + + +-- +-- Name: uq_subscriptions_active_personal_by_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_subscriptions_active_personal_by_user ON public.subscriptions USING btree (user_id) WHERE ((tenant_id IS NULL) AND (status = 'active'::text)); + + +-- +-- Name: uq_tenant_invites_pending; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_tenant_invites_pending ON public.tenant_invites USING btree (tenant_id, lower(email), role) WHERE ((accepted_at IS NULL) AND (revoked_at IS NULL)); + + +-- +-- Name: uq_tenant_members_tenant_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX uq_tenant_members_tenant_user ON public.tenant_members USING btree (tenant_id, user_id); + + +-- +-- Name: ux_subscriptions_active_per_personal_user; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX ux_subscriptions_active_per_personal_user ON public.subscriptions USING btree (user_id) WHERE ((status = 'active'::text) AND (tenant_id IS NULL)); + + +-- +-- Name: ux_subscriptions_active_per_tenant; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX ux_subscriptions_active_per_tenant ON public.subscriptions USING btree (tenant_id) WHERE ((status = 'active'::text) AND (tenant_id IS NOT NULL)); + + +-- +-- Name: ix_realtime_subscription_entity; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX ix_realtime_subscription_entity ON realtime.subscription USING btree (entity); + + +-- +-- Name: messages_inserted_at_topic_index; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_inserted_at_topic_index ON ONLY realtime.messages USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_26_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_26_inserted_at_topic_idx ON realtime.messages_2026_03_26 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_27_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_27_inserted_at_topic_idx ON realtime.messages_2026_03_27 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_28_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_28_inserted_at_topic_idx ON realtime.messages_2026_03_28 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_29_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_29_inserted_at_topic_idx ON realtime.messages_2026_03_29 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_30_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_30_inserted_at_topic_idx ON realtime.messages_2026_03_30 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_03_31_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_03_31_inserted_at_topic_idx ON realtime.messages_2026_03_31 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: messages_2026_04_01_inserted_at_topic_idx; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE INDEX messages_2026_04_01_inserted_at_topic_idx ON realtime.messages_2026_04_01 USING btree (inserted_at DESC, topic) WHERE ((extension = 'broadcast'::text) AND (private IS TRUE)); + + +-- +-- Name: subscription_subscription_id_entity_filters_key; Type: INDEX; Schema: realtime; Owner: - +-- + +CREATE UNIQUE INDEX subscription_subscription_id_entity_filters_key ON realtime.subscription USING btree (subscription_id, entity, filters); + + +-- +-- Name: bname; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX bname ON storage.buckets USING btree (name); + + +-- +-- Name: bucketid_objname; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX bucketid_objname ON storage.objects USING btree (bucket_id, name); + + +-- +-- Name: buckets_analytics_unique_name_idx; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX buckets_analytics_unique_name_idx ON storage.buckets_analytics USING btree (name) WHERE (deleted_at IS NULL); + + +-- +-- Name: idx_iceberg_namespaces_bucket_id; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX idx_iceberg_namespaces_bucket_id ON storage.iceberg_namespaces USING btree (catalog_id, name); + + +-- +-- Name: idx_iceberg_tables_location; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX idx_iceberg_tables_location ON storage.iceberg_tables USING btree (location); + + +-- +-- Name: idx_iceberg_tables_namespace_id; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX idx_iceberg_tables_namespace_id ON storage.iceberg_tables USING btree (catalog_id, namespace_id, name); + + +-- +-- Name: idx_multipart_uploads_list; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX idx_multipart_uploads_list ON storage.s3_multipart_uploads USING btree (bucket_id, key, created_at); + + +-- +-- Name: idx_objects_bucket_id_name; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX idx_objects_bucket_id_name ON storage.objects USING btree (bucket_id, name COLLATE "C"); + + +-- +-- Name: idx_objects_bucket_id_name_lower; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX idx_objects_bucket_id_name_lower ON storage.objects USING btree (bucket_id, lower(name) COLLATE "C"); + + +-- +-- Name: name_prefix_search; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE INDEX name_prefix_search ON storage.objects USING btree (name text_pattern_ops); + + +-- +-- Name: vector_indexes_name_bucket_id_idx; Type: INDEX; Schema: storage; Owner: - +-- + +CREATE UNIQUE INDEX vector_indexes_name_bucket_id_idx ON storage.vector_indexes USING btree (name, bucket_id); + + +-- +-- Name: supabase_functions_hooks_h_table_id_h_name_idx; Type: INDEX; Schema: supabase_functions; Owner: - +-- + +CREATE INDEX supabase_functions_hooks_h_table_id_h_name_idx ON supabase_functions.hooks USING btree (hook_table_id, hook_name); + + +-- +-- Name: supabase_functions_hooks_request_id_idx; Type: INDEX; Schema: supabase_functions; Owner: - +-- + +CREATE INDEX supabase_functions_hooks_request_id_idx ON supabase_functions.hooks USING btree (request_id); + + +-- +-- Name: messages_2026_03_26_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_26_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_26_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_26_pkey; + + +-- +-- Name: messages_2026_03_27_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_27_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_27_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_27_pkey; + + +-- +-- Name: messages_2026_03_28_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_28_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_28_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_28_pkey; + + +-- +-- Name: messages_2026_03_29_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_29_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_29_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_29_pkey; + + +-- +-- Name: messages_2026_03_30_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_30_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_30_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_30_pkey; + + +-- +-- Name: messages_2026_03_31_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_03_31_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_03_31_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_03_31_pkey; + + +-- +-- Name: messages_2026_04_01_inserted_at_topic_idx; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_inserted_at_topic_index ATTACH PARTITION realtime.messages_2026_04_01_inserted_at_topic_idx; + + +-- +-- Name: messages_2026_04_01_pkey; Type: INDEX ATTACH; Schema: realtime; Owner: - +-- + +ALTER INDEX realtime.messages_pkey ATTACH PARTITION realtime.messages_2026_04_01_pkey; + + +-- +-- Name: users on_auth_user_created; Type: TRIGGER; Schema: auth; Owner: - +-- + +CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.handle_new_user(); + + +-- +-- Name: users trg_seed_patient_groups; Type: TRIGGER; Schema: auth; Owner: - +-- + +CREATE TRIGGER trg_seed_patient_groups AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION public.on_new_user_seed_patient_groups(); + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER agenda_bloqueios_updated_at BEFORE UPDATE ON public.agenda_bloqueios FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agendador_configuracoes agendador_slug_trigger; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER agendador_slug_trigger BEFORE INSERT OR UPDATE ON public.agendador_configuracoes FOR EACH ROW EXECUTE FUNCTION public.agendador_gerar_slug(); + + +-- +-- Name: tenant_members prevent_saas_membership_trigger; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER prevent_saas_membership_trigger BEFORE INSERT ON public.tenant_members FOR EACH ROW EXECUTE FUNCTION public.prevent_saas_membership(); + + +-- +-- Name: insurance_plan_services set_insurance_plan_services_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER set_insurance_plan_services_updated_at BEFORE UPDATE ON public.insurance_plan_services FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: user_settings t_user_settings_set_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER t_user_settings_set_updated_at BEFORE UPDATE ON public.user_settings FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_configuracoes tg_agenda_configuracoes_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_configuracoes_updated_at BEFORE UPDATE ON public.agenda_configuracoes FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_eventos tg_agenda_eventos_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_eventos_updated_at BEFORE UPDATE ON public.agenda_eventos FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_excecoes tg_agenda_excecoes_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_excecoes_updated_at BEFORE UPDATE ON public.agenda_excecoes FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: agenda_regras_semanais tg_agenda_regras_semanais_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_agenda_regras_semanais_updated_at BEFORE UPDATE ON public.agenda_regras_semanais FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: recurrence_rules tg_recurrence_rules_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tg_recurrence_rules_updated_at BEFORE UPDATE ON public.recurrence_rules FOR EACH ROW EXECUTE FUNCTION public.set_updated_at_recurrence(); + + +-- +-- Name: plan_public tr_plan_public_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER tr_plan_public_updated_at BEFORE UPDATE ON public.plan_public FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: profiles trg_account_type_immutable; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_account_type_immutable BEFORE UPDATE OF account_type ON public.profiles FOR EACH ROW EXECUTE FUNCTION public.guard_account_type_immutable(); + + +-- +-- Name: agenda_configuracoes trg_agenda_cfg_sync; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_cfg_sync BEFORE INSERT OR UPDATE ON public.agenda_configuracoes FOR EACH ROW EXECUTE FUNCTION public.agenda_cfg_sync(); + + +-- +-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_del; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_eventos_busy_mirror_del AFTER DELETE ON public.agenda_eventos FOR EACH ROW WHEN (((old.mirror_of_event_id IS NULL) AND (old.tenant_id = old.owner_id))) EXECUTE FUNCTION public.sync_busy_mirror_agenda_eventos(); + + +-- +-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_ins; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_eventos_busy_mirror_ins AFTER INSERT ON public.agenda_eventos FOR EACH ROW WHEN (((new.mirror_of_event_id IS NULL) AND (new.tenant_id = new.owner_id) AND (new.visibility_scope = ANY (ARRAY['busy_only'::text, 'private'::text])))) EXECUTE FUNCTION public.sync_busy_mirror_agenda_eventos(); + + +-- +-- Name: agenda_eventos trg_agenda_eventos_busy_mirror_upd; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_eventos_busy_mirror_upd AFTER UPDATE ON public.agenda_eventos FOR EACH ROW WHEN (((new.mirror_of_event_id IS NULL) AND (new.tenant_id = new.owner_id) AND ((new.visibility_scope IS DISTINCT FROM old.visibility_scope) OR (new.inicio_em IS DISTINCT FROM old.inicio_em) OR (new.fim_em IS DISTINCT FROM old.fim_em) OR (new.owner_id IS DISTINCT FROM old.owner_id) OR (new.tenant_id IS DISTINCT FROM old.tenant_id)))) EXECUTE FUNCTION public.sync_busy_mirror_agenda_eventos(); + + +-- +-- Name: agenda_regras_semanais trg_agenda_regras_semanais_no_overlap; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_agenda_regras_semanais_no_overlap BEFORE INSERT OR UPDATE ON public.agenda_regras_semanais FOR EACH ROW EXECUTE FUNCTION public.fn_agenda_regras_semanais_no_overlap(); + + +-- +-- Name: agenda_eventos trg_auto_financial_from_session; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_auto_financial_from_session AFTER UPDATE OF status ON public.agenda_eventos FOR EACH ROW EXECUTE FUNCTION public.auto_create_financial_record_from_session(); + + +-- +-- Name: notification_preferences trg_cancel_notifs_on_opt_out; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_cancel_notifs_on_opt_out AFTER UPDATE ON public.notification_preferences FOR EACH ROW EXECUTE FUNCTION public.cancel_notifications_on_opt_out(); + + +-- +-- Name: agenda_eventos trg_cancel_notifs_on_session_cancel; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_cancel_notifs_on_session_cancel AFTER UPDATE ON public.agenda_eventos FOR EACH ROW WHEN ((new.status IS DISTINCT FROM old.status)) EXECUTE FUNCTION public.cancel_notifications_on_session_cancel(); + + +-- +-- Name: company_profiles trg_company_profiles_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_company_profiles_updated_at BEFORE UPDATE ON public.company_profiles FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: determined_commitment_fields trg_determined_commitment_fields_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_determined_commitment_fields_updated_at BEFORE UPDATE ON public.determined_commitment_fields FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: determined_commitments trg_determined_commitments_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_determined_commitments_updated_at BEFORE UPDATE ON public.determined_commitments FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: email_layout_config trg_email_layout_config_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_email_layout_config_updated_at BEFORE UPDATE ON public.email_layout_config FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: email_templates_global trg_email_templates_global_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_email_templates_global_updated_at BEFORE UPDATE ON public.email_templates_global FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: email_templates_tenant trg_email_templates_tenant_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_email_templates_tenant_updated_at BEFORE UPDATE ON public.email_templates_tenant FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: financial_exceptions trg_financial_exceptions_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_financial_exceptions_updated_at BEFORE UPDATE ON public.financial_exceptions FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: financial_records trg_financial_records_auto_overdue; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_financial_records_auto_overdue BEFORE UPDATE ON public.financial_records FOR EACH ROW EXECUTE FUNCTION public.trg_fn_financial_records_auto_overdue(); + + +-- +-- Name: financial_records trg_financial_records_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_financial_records_updated_at BEFORE UPDATE ON public.financial_records FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: global_notices trg_global_notices_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_global_notices_updated_at BEFORE UPDATE ON public.global_notices FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: insurance_plans trg_insurance_plans_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_insurance_plans_updated_at BEFORE UPDATE ON public.insurance_plans FOR EACH ROW EXECUTE FUNCTION public.set_insurance_plans_updated_at(); + + +-- +-- Name: medicos trg_medicos_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_medicos_updated_at BEFORE UPDATE ON public.medicos FOR EACH ROW EXECUTE FUNCTION public.set_medicos_updated_at(); + + +-- +-- Name: plans trg_no_change_core_plan_key; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_no_change_core_plan_key BEFORE UPDATE ON public.plans FOR EACH ROW EXECUTE FUNCTION public.guard_no_change_core_plan_key(); + + +-- +-- Name: plans trg_no_change_plan_target; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_no_change_plan_target BEFORE UPDATE ON public.plans FOR EACH ROW EXECUTE FUNCTION public.guard_no_change_plan_target(); + + +-- +-- Name: plans trg_no_delete_core_plans; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_no_delete_core_plans BEFORE DELETE ON public.plans FOR EACH ROW EXECUTE FUNCTION public.guard_no_delete_core_plans(); + + +-- +-- Name: notification_channels trg_notification_channels_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_channels_updated_at BEFORE UPDATE ON public.notification_channels FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_logs trg_notification_logs_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_logs_updated_at BEFORE UPDATE ON public.notification_logs FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_preferences trg_notification_preferences_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_preferences_updated_at BEFORE UPDATE ON public.notification_preferences FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_queue trg_notification_queue_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_queue_updated_at BEFORE UPDATE ON public.notification_queue FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_schedules trg_notification_schedules_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_schedules_updated_at BEFORE UPDATE ON public.notification_schedules FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: notification_templates trg_notification_templates_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notification_templates_updated_at BEFORE UPDATE ON public.notification_templates FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patient_intake_requests trg_notify_on_intake; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notify_on_intake AFTER INSERT ON public.patient_intake_requests FOR EACH ROW EXECUTE FUNCTION public.notify_on_intake(); + + +-- +-- Name: agendador_solicitacoes trg_notify_on_scheduling; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notify_on_scheduling AFTER INSERT ON public.agendador_solicitacoes FOR EACH ROW EXECUTE FUNCTION public.notify_on_scheduling(); + + +-- +-- Name: agenda_eventos trg_notify_on_session_status; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_notify_on_session_status AFTER UPDATE OF status ON public.agenda_eventos FOR EACH ROW EXECUTE FUNCTION public.notify_on_session_status(); + + +-- +-- Name: tenant_members trg_patient_cannot_own_tenant; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_cannot_own_tenant BEFORE INSERT OR UPDATE ON public.tenant_members FOR EACH ROW EXECUTE FUNCTION public.guard_patient_cannot_own_tenant(); + + +-- +-- Name: patient_contacts trg_patient_contacts_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_contacts_updated_at BEFORE UPDATE ON public.patient_contacts FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patient_groups trg_patient_groups_set_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_groups_set_updated_at BEFORE UPDATE ON public.patient_groups FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patient_intake_requests trg_patient_intake_requests_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_intake_requests_updated_at BEFORE UPDATE ON public.patient_intake_requests FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patients trg_patient_risco_timeline; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_risco_timeline AFTER UPDATE OF risco_elevado ON public.patients FOR EACH ROW EXECUTE FUNCTION public.trg_fn_patient_risco_timeline(); + + +-- +-- Name: patients trg_patient_status_history; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_status_history AFTER INSERT OR UPDATE OF status ON public.patients FOR EACH ROW EXECUTE FUNCTION public.trg_fn_patient_status_history(); + + +-- +-- Name: patients trg_patient_status_timeline; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_status_timeline AFTER INSERT OR UPDATE OF status ON public.patients FOR EACH ROW EXECUTE FUNCTION public.trg_fn_patient_status_timeline(); + + +-- +-- Name: patient_tags trg_patient_tags_set_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patient_tags_set_updated_at BEFORE UPDATE ON public.patient_tags FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patients trg_patients_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patients_updated_at BEFORE UPDATE ON public.patients FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patients trg_patients_validate_members; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_patients_validate_members BEFORE INSERT OR UPDATE OF tenant_id, responsible_member_id, patient_scope, therapist_member_id ON public.patients FOR EACH ROW EXECUTE FUNCTION public.patients_validate_member_consistency(); + + +-- +-- Name: payment_settings trg_payment_settings_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_payment_settings_updated_at BEFORE UPDATE ON public.payment_settings FOR EACH ROW EXECUTE FUNCTION public.update_payment_settings_updated_at(); + + +-- +-- Name: patient_groups trg_prevent_promoting_to_system; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_prevent_promoting_to_system BEFORE UPDATE ON public.patient_groups FOR EACH ROW EXECUTE FUNCTION public.prevent_promoting_to_system(); + + +-- +-- Name: patient_groups trg_prevent_system_group_changes; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_prevent_system_group_changes BEFORE DELETE OR UPDATE ON public.patient_groups FOR EACH ROW EXECUTE FUNCTION public.prevent_system_group_changes(); + + +-- +-- Name: professional_pricing trg_professional_pricing_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_professional_pricing_updated_at BEFORE UPDATE ON public.professional_pricing FOR EACH ROW EXECUTE FUNCTION public.update_professional_pricing_updated_at(); + + +-- +-- Name: profiles trg_profiles_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_profiles_updated_at BEFORE UPDATE ON public.profiles FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: patient_support_contacts trg_psc_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_psc_updated_at BEFORE UPDATE ON public.patient_support_contacts FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: services trg_services_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_services_updated_at BEFORE UPDATE ON public.services FOR EACH ROW EXECUTE FUNCTION public.set_services_updated_at(); + + +-- +-- Name: subscription_intents trg_subscription_intents_view_insert; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_subscription_intents_view_insert INSTEAD OF INSERT ON public.subscription_intents FOR EACH ROW EXECUTE FUNCTION public.subscription_intents_view_insert(); + + +-- +-- Name: subscriptions trg_subscriptions_validate_scope; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_subscriptions_validate_scope BEFORE INSERT OR UPDATE ON public.subscriptions FOR EACH ROW EXECUTE FUNCTION public.subscriptions_validate_scope(); + + +-- +-- Name: tenant_features trg_tenant_features_guard_with_plan; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_tenant_features_guard_with_plan BEFORE INSERT OR UPDATE ON public.tenant_features FOR EACH ROW EXECUTE FUNCTION public.tenant_features_guard_with_plan(); + + +-- +-- Name: tenant_features trg_tenant_features_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_tenant_features_updated_at BEFORE UPDATE ON public.tenant_features FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: tenants trg_tenant_kind_immutable; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_tenant_kind_immutable BEFORE UPDATE OF kind ON public.tenants FOR EACH ROW EXECUTE FUNCTION public.guard_tenant_kind_immutable(); + + +-- +-- Name: therapist_payouts trg_therapist_payouts_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_therapist_payouts_updated_at BEFORE UPDATE ON public.therapist_payouts FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: user_settings trg_user_settings_updated_at; Type: TRIGGER; Schema: public; Owner: - +-- + +CREATE TRIGGER trg_user_settings_updated_at BEFORE UPDATE ON public.user_settings FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + + +-- +-- Name: subscription tr_check_filters; Type: TRIGGER; Schema: realtime; Owner: - +-- + +CREATE TRIGGER tr_check_filters BEFORE INSERT OR UPDATE ON realtime.subscription FOR EACH ROW EXECUTE FUNCTION realtime.subscription_check_filters(); + + +-- +-- Name: buckets enforce_bucket_name_length_trigger; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER enforce_bucket_name_length_trigger BEFORE INSERT OR UPDATE OF name ON storage.buckets FOR EACH ROW EXECUTE FUNCTION storage.enforce_bucket_name_length(); + + +-- +-- Name: buckets protect_buckets_delete; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER protect_buckets_delete BEFORE DELETE ON storage.buckets FOR EACH STATEMENT EXECUTE FUNCTION storage.protect_delete(); + + +-- +-- Name: objects protect_objects_delete; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER protect_objects_delete BEFORE DELETE ON storage.objects FOR EACH STATEMENT EXECUTE FUNCTION storage.protect_delete(); + + +-- +-- Name: objects update_objects_updated_at; Type: TRIGGER; Schema: storage; Owner: - +-- + +CREATE TRIGGER update_objects_updated_at BEFORE UPDATE ON storage.objects FOR EACH ROW EXECUTE FUNCTION storage.update_updated_at_column(); + + +-- +-- Name: extensions extensions_tenant_external_id_fkey; Type: FK CONSTRAINT; Schema: _realtime; Owner: - +-- + +ALTER TABLE ONLY _realtime.extensions + ADD CONSTRAINT extensions_tenant_external_id_fkey FOREIGN KEY (tenant_external_id) REFERENCES _realtime.tenants(external_id) ON DELETE CASCADE; + + +-- +-- Name: identities identities_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.identities + ADD CONSTRAINT identities_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: mfa_amr_claims mfa_amr_claims_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_amr_claims + ADD CONSTRAINT mfa_amr_claims_session_id_fkey FOREIGN KEY (session_id) REFERENCES auth.sessions(id) ON DELETE CASCADE; + + +-- +-- Name: mfa_challenges mfa_challenges_auth_factor_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_challenges + ADD CONSTRAINT mfa_challenges_auth_factor_id_fkey FOREIGN KEY (factor_id) REFERENCES auth.mfa_factors(id) ON DELETE CASCADE; + + +-- +-- Name: mfa_factors mfa_factors_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.mfa_factors + ADD CONSTRAINT mfa_factors_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_authorizations oauth_authorizations_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_client_id_fkey FOREIGN KEY (client_id) REFERENCES auth.oauth_clients(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_authorizations oauth_authorizations_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_authorizations + ADD CONSTRAINT oauth_authorizations_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_consents oauth_consents_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_client_id_fkey FOREIGN KEY (client_id) REFERENCES auth.oauth_clients(id) ON DELETE CASCADE; + + +-- +-- Name: oauth_consents oauth_consents_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.oauth_consents + ADD CONSTRAINT oauth_consents_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: one_time_tokens one_time_tokens_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.one_time_tokens + ADD CONSTRAINT one_time_tokens_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: refresh_tokens refresh_tokens_session_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.refresh_tokens + ADD CONSTRAINT refresh_tokens_session_id_fkey FOREIGN KEY (session_id) REFERENCES auth.sessions(id) ON DELETE CASCADE; + + +-- +-- Name: saml_providers saml_providers_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_providers + ADD CONSTRAINT saml_providers_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; + + +-- +-- Name: saml_relay_states saml_relay_states_flow_state_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_relay_states + ADD CONSTRAINT saml_relay_states_flow_state_id_fkey FOREIGN KEY (flow_state_id) REFERENCES auth.flow_state(id) ON DELETE CASCADE; + + +-- +-- Name: saml_relay_states saml_relay_states_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.saml_relay_states + ADD CONSTRAINT saml_relay_states_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; + + +-- +-- Name: sessions sessions_oauth_client_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sessions + ADD CONSTRAINT sessions_oauth_client_id_fkey FOREIGN KEY (oauth_client_id) REFERENCES auth.oauth_clients(id) ON DELETE CASCADE; + + +-- +-- Name: sessions sessions_user_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sessions + ADD CONSTRAINT sessions_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: sso_domains sso_domains_sso_provider_id_fkey; Type: FK CONSTRAINT; Schema: auth; Owner: - +-- + +ALTER TABLE ONLY auth.sso_domains + ADD CONSTRAINT sso_domains_sso_provider_id_fkey FOREIGN KEY (sso_provider_id) REFERENCES auth.sso_providers(id) ON DELETE CASCADE; + + +-- +-- Name: addon_credits addon_credits_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT addon_credits_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id); + + +-- +-- Name: addon_credits addon_credits_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_credits + ADD CONSTRAINT addon_credits_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id); + + +-- +-- Name: addon_transactions addon_transactions_admin_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_admin_user_id_fkey FOREIGN KEY (admin_user_id) REFERENCES auth.users(id); + + +-- +-- Name: addon_transactions addon_transactions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id); + + +-- +-- Name: addon_transactions addon_transactions_product_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_product_id_fkey FOREIGN KEY (product_id) REFERENCES public.addon_products(id); + + +-- +-- Name: addon_transactions addon_transactions_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_transactions + ADD CONSTRAINT addon_transactions_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id); + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_bloqueios + ADD CONSTRAINT agenda_bloqueios_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_bloqueios agenda_bloqueios_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_bloqueios + ADD CONSTRAINT agenda_bloqueios_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_configuracoes + ADD CONSTRAINT agenda_configuracoes_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_eventos agenda_eventos_billing_contract_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_billing_contract_id_fkey FOREIGN KEY (billing_contract_id) REFERENCES public.billing_contracts(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_determined_commitment_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_determined_commitment_fk FOREIGN KEY (determined_commitment_id) REFERENCES public.determined_commitments(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_insurance_plan_id_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_insurance_plan_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_insurance_plan_service_id_fkey FOREIGN KEY (insurance_plan_service_id) REFERENCES public.insurance_plan_services(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_recurrence_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_recurrence_id_fkey FOREIGN KEY (recurrence_id) REFERENCES public.recurrence_rules(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_eventos agenda_eventos_terapeuta_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_eventos + ADD CONSTRAINT agenda_eventos_terapeuta_fk FOREIGN KEY (terapeuta_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: agenda_excecoes agenda_excecoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_excecoes + ADD CONSTRAINT agenda_excecoes_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_online_slots agenda_online_slots_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_online_slots agenda_online_slots_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_online_slots + ADD CONSTRAINT agenda_online_slots_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_regras_semanais + ADD CONSTRAINT agenda_regras_semanais_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_bloqueados_semanais + ADD CONSTRAINT agenda_slots_bloqueados_semanais_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agenda_slots_regras + ADD CONSTRAINT agenda_slots_regras_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_configuracoes agendador_configuracoes_owner_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_configuracoes + ADD CONSTRAINT agendador_configuracoes_owner_fk FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_configuracoes agendador_configuracoes_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_configuracoes + ADD CONSTRAINT agendador_configuracoes_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_solicitacoes agendador_sol_owner_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_solicitacoes + ADD CONSTRAINT agendador_sol_owner_fk FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: agendador_solicitacoes agendador_sol_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.agendador_solicitacoes + ADD CONSTRAINT agendador_sol_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: billing_contracts billing_contracts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.billing_contracts + ADD CONSTRAINT billing_contracts_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: billing_contracts billing_contracts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.billing_contracts + ADD CONSTRAINT billing_contracts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: commitment_services commitment_services_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_services + ADD CONSTRAINT commitment_services_commitment_id_fkey FOREIGN KEY (commitment_id) REFERENCES public.agenda_eventos(id) ON DELETE CASCADE; + + +-- +-- Name: commitment_services commitment_services_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_services + ADD CONSTRAINT commitment_services_service_id_fkey FOREIGN KEY (service_id) REFERENCES public.services(id) ON DELETE RESTRICT; + + +-- +-- Name: commitment_time_logs commitment_time_logs_calendar_event_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_calendar_event_id_fkey FOREIGN KEY (calendar_event_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; + + +-- +-- Name: commitment_time_logs commitment_time_logs_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_commitment_id_fkey FOREIGN KEY (commitment_id) REFERENCES public.determined_commitments(id) ON DELETE CASCADE; + + +-- +-- Name: commitment_time_logs commitment_time_logs_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.commitment_time_logs + ADD CONSTRAINT commitment_time_logs_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: company_profiles company_profiles_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.company_profiles + ADD CONSTRAINT company_profiles_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: determined_commitment_fields determined_commitment_fields_commitment_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitment_fields + ADD CONSTRAINT determined_commitment_fields_commitment_id_fkey FOREIGN KEY (commitment_id) REFERENCES public.determined_commitments(id) ON DELETE CASCADE; + + +-- +-- Name: determined_commitment_fields determined_commitment_fields_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitment_fields + ADD CONSTRAINT determined_commitment_fields_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: determined_commitments determined_commitments_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.determined_commitments + ADD CONSTRAINT determined_commitments_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: email_layout_config email_layout_config_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_layout_config + ADD CONSTRAINT email_layout_config_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: email_templates_tenant email_templates_tenant_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: email_templates_tenant email_templates_tenant_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.email_templates_tenant + ADD CONSTRAINT email_templates_tenant_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: feriados feriados_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: feriados feriados_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.feriados + ADD CONSTRAINT feriados_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: financial_categories financial_categories_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_categories + ADD CONSTRAINT financial_categories_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: financial_exceptions financial_exceptions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_exceptions + ADD CONSTRAINT financial_exceptions_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: financial_records financial_records_agenda_evento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_agenda_evento_id_fkey FOREIGN KEY (agenda_evento_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_category_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_category_id_fkey FOREIGN KEY (category_id) REFERENCES public.financial_categories(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_insurance_plan_id_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE SET NULL; + + +-- +-- Name: financial_records financial_records_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.financial_records + ADD CONSTRAINT financial_records_user_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: global_notices global_notices_created_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.global_notices + ADD CONSTRAINT global_notices_created_by_fkey FOREIGN KEY (created_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: insurance_plan_services insurance_plan_services_plan_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plan_services + ADD CONSTRAINT insurance_plan_services_plan_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE CASCADE; + + +-- +-- Name: insurance_plans insurance_plans_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.insurance_plans + ADD CONSTRAINT insurance_plans_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: module_features module_features_feature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.module_features + ADD CONSTRAINT module_features_feature_id_fkey FOREIGN KEY (feature_id) REFERENCES public.features(id) ON DELETE CASCADE; + + +-- +-- Name: module_features module_features_module_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.module_features + ADD CONSTRAINT module_features_module_id_fkey FOREIGN KEY (module_id) REFERENCES public.modules(id) ON DELETE CASCADE; + + +-- +-- Name: notice_dismissals notice_dismissals_notice_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_notice_id_fkey FOREIGN KEY (notice_id) REFERENCES public.global_notices(id) ON DELETE CASCADE; + + +-- +-- Name: notice_dismissals notice_dismissals_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notice_dismissals + ADD CONSTRAINT notice_dismissals_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notification_channels notification_channels_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_channels + ADD CONSTRAINT notification_channels_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notification_logs notification_logs_queue_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_logs + ADD CONSTRAINT notification_logs_queue_id_fkey FOREIGN KEY (queue_id) REFERENCES public.notification_queue(id) ON DELETE SET NULL; + + +-- +-- Name: notification_schedules notification_schedules_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_schedules + ADD CONSTRAINT notification_schedules_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notification_templates notification_templates_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notification_templates + ADD CONSTRAINT notification_templates_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: notifications notifications_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.notifications + ADD CONSTRAINT notifications_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: patient_contacts patient_contacts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_contacts + ADD CONSTRAINT patient_contacts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_contacts patient_contacts_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_contacts + ADD CONSTRAINT patient_contacts_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_discounts patient_discounts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_discounts + ADD CONSTRAINT patient_discounts_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: patient_discounts patient_discounts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_discounts + ADD CONSTRAINT patient_discounts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_group_patient patient_group_patient_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_group_patient + ADD CONSTRAINT patient_group_patient_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_group_patient patient_group_patient_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_group_patient + ADD CONSTRAINT patient_group_patient_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_groups patient_groups_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_groups + ADD CONSTRAINT patient_groups_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_intake_requests patient_intake_requests_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_intake_requests + ADD CONSTRAINT patient_intake_requests_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_invites patient_invites_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_invites + ADD CONSTRAINT patient_invites_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag patient_patient_tag_tag_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT patient_patient_tag_tag_id_fkey FOREIGN KEY (tag_id) REFERENCES public.patient_tags(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag patient_patient_tag_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT patient_patient_tag_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_status_history patient_status_history_alterado_por_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_alterado_por_fkey FOREIGN KEY (alterado_por) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: patient_status_history patient_status_history_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_status_history patient_status_history_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_status_history + ADD CONSTRAINT patient_status_history_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_support_contacts patient_support_contacts_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_support_contacts + ADD CONSTRAINT patient_support_contacts_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_tags patient_tags_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_tags + ADD CONSTRAINT patient_tags_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patient_timeline patient_timeline_gerado_por_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_gerado_por_fkey FOREIGN KEY (gerado_por) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: patient_timeline patient_timeline_patient_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_patient_id_fkey FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_timeline patient_timeline_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_timeline + ADD CONSTRAINT patient_timeline_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patients patients_convenio_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_convenio_id_fkey FOREIGN KEY (convenio_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + + +-- +-- Name: patients patients_responsible_member_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_responsible_member_id_fkey FOREIGN KEY (responsible_member_id) REFERENCES public.tenant_members(id) ON DELETE RESTRICT; + + +-- +-- Name: patients patients_risco_sinalizado_por_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_risco_sinalizado_por_fkey FOREIGN KEY (risco_sinalizado_por) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: patients patients_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: patients patients_therapist_member_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_therapist_member_id_fkey FOREIGN KEY (therapist_member_id) REFERENCES public.tenant_members(id); + + +-- +-- Name: patients patients_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patients + ADD CONSTRAINT patients_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: payment_settings payment_settings_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: payment_settings payment_settings_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.payment_settings + ADD CONSTRAINT payment_settings_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: plan_features plan_features_feature_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_features + ADD CONSTRAINT plan_features_feature_id_fkey FOREIGN KEY (feature_id) REFERENCES public.features(id) ON DELETE CASCADE; + + +-- +-- Name: plan_features plan_features_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_features + ADD CONSTRAINT plan_features_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: plan_prices plan_prices_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_prices + ADD CONSTRAINT plan_prices_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: plan_public_bullets plan_public_bullets_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public_bullets + ADD CONSTRAINT plan_public_bullets_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: plan_public plan_public_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.plan_public + ADD CONSTRAINT plan_public_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag ppt_patient_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT ppt_patient_fk FOREIGN KEY (patient_id) REFERENCES public.patients(id) ON DELETE CASCADE; + + +-- +-- Name: patient_patient_tag ppt_tag_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.patient_patient_tag + ADD CONSTRAINT ppt_tag_fk FOREIGN KEY (tag_id) REFERENCES public.patient_tags(id) ON DELETE CASCADE; + + +-- +-- Name: professional_pricing professional_pricing_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.professional_pricing + ADD CONSTRAINT professional_pricing_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: profiles profiles_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.profiles + ADD CONSTRAINT profiles_id_fkey FOREIGN KEY (id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_agenda_evento_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_agenda_evento_id_fkey FOREIGN KEY (agenda_evento_id) REFERENCES public.agenda_eventos(id) ON DELETE SET NULL; + + +-- +-- Name: recurrence_exceptions recurrence_exceptions_recurrence_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_exceptions + ADD CONSTRAINT recurrence_exceptions_recurrence_id_fkey FOREIGN KEY (recurrence_id) REFERENCES public.recurrence_rules(id) ON DELETE CASCADE; + + +-- +-- Name: recurrence_rule_services recurrence_rule_services_rule_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rule_services + ADD CONSTRAINT recurrence_rule_services_rule_id_fkey FOREIGN KEY (rule_id) REFERENCES public.recurrence_rules(id) ON DELETE CASCADE; + + +-- +-- Name: recurrence_rule_services recurrence_rule_services_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rule_services + ADD CONSTRAINT recurrence_rule_services_service_id_fkey FOREIGN KEY (service_id) REFERENCES public.services(id) ON DELETE RESTRICT; + + +-- +-- Name: recurrence_rules recurrence_rules_insurance_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rules + ADD CONSTRAINT recurrence_rules_insurance_plan_id_fkey FOREIGN KEY (insurance_plan_id) REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + + +-- +-- Name: recurrence_rules recurrence_rules_insurance_plan_service_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.recurrence_rules + ADD CONSTRAINT recurrence_rules_insurance_plan_service_id_fkey FOREIGN KEY (insurance_plan_service_id) REFERENCES public.insurance_plan_services(id) ON DELETE SET NULL; + + +-- +-- Name: saas_admins saas_admins_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_admins + ADD CONSTRAINT saas_admins_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: saas_doc_votos saas_doc_votos_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_doc_id_fkey FOREIGN KEY (doc_id) REFERENCES public.saas_docs(id) ON DELETE CASCADE; + + +-- +-- Name: saas_doc_votos saas_doc_votos_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_doc_votos + ADD CONSTRAINT saas_doc_votos_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: saas_faq_itens saas_faq_itens_doc_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.saas_faq_itens + ADD CONSTRAINT saas_faq_itens_doc_id_fkey FOREIGN KEY (doc_id) REFERENCES public.saas_docs(id) ON DELETE CASCADE; + + +-- +-- Name: services services_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.services + ADD CONSTRAINT services_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: subscription_intents_personal sint_personal_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_personal + ADD CONSTRAINT sint_personal_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES public.subscriptions(id) ON DELETE SET NULL; + + +-- +-- Name: subscription_intents_tenant sint_tenant_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_tenant + ADD CONSTRAINT sint_tenant_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES public.subscriptions(id) ON DELETE SET NULL; + + +-- +-- Name: subscription_events subscription_events_subscription_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_events + ADD CONSTRAINT subscription_events_subscription_id_fkey FOREIGN KEY (subscription_id) REFERENCES public.subscriptions(id) ON DELETE CASCADE; + + +-- +-- Name: subscription_intents_personal subscription_intents_personal_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_personal + ADD CONSTRAINT subscription_intents_personal_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE RESTRICT; + + +-- +-- Name: subscription_intents_tenant subscription_intents_tenant_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_tenant + ADD CONSTRAINT subscription_intents_tenant_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE RESTRICT; + + +-- +-- Name: subscription_intents_legacy subscription_intents_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscription_intents_legacy + ADD CONSTRAINT subscription_intents_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: subscriptions subscriptions_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscriptions + ADD CONSTRAINT subscriptions_owner_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: subscriptions subscriptions_plan_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.subscriptions + ADD CONSTRAINT subscriptions_plan_id_fkey FOREIGN KEY (plan_id) REFERENCES public.plans(id) ON DELETE RESTRICT; + + +-- +-- Name: support_sessions support_sessions_admin_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_admin_fk FOREIGN KEY (admin_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: support_sessions support_sessions_tenant_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.support_sessions + ADD CONSTRAINT support_sessions_tenant_fk FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_features tenant_features_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_features + ADD CONSTRAINT tenant_features_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_invites tenant_invites_accepted_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_accepted_by_fkey FOREIGN KEY (accepted_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: tenant_invites tenant_invites_invited_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_invited_by_fkey FOREIGN KEY (invited_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: tenant_invites tenant_invites_revoked_by_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_revoked_by_fkey FOREIGN KEY (revoked_by) REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- +-- Name: tenant_invites tenant_invites_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_invites + ADD CONSTRAINT tenant_invites_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_members tenant_members_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_members tenant_members_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_members + ADD CONSTRAINT tenant_members_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_modules tenant_modules_module_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_module_id_fkey FOREIGN KEY (module_id) REFERENCES public.modules(id) ON DELETE CASCADE; + + +-- +-- Name: tenant_modules tenant_modules_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tenant_modules + ADD CONSTRAINT tenant_modules_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: therapist_payout_records therapist_payout_records_financial_record_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payout_records + ADD CONSTRAINT therapist_payout_records_financial_record_id_fkey FOREIGN KEY (financial_record_id) REFERENCES public.financial_records(id) ON DELETE RESTRICT; + + +-- +-- Name: therapist_payout_records therapist_payout_records_payout_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payout_records + ADD CONSTRAINT therapist_payout_records_payout_id_fkey FOREIGN KEY (payout_id) REFERENCES public.therapist_payouts(id) ON DELETE CASCADE; + + +-- +-- Name: therapist_payouts therapist_payouts_owner_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payouts + ADD CONSTRAINT therapist_payouts_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: therapist_payouts therapist_payouts_tenant_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.therapist_payouts + ADD CONSTRAINT therapist_payouts_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE; + + +-- +-- Name: twilio_subaccount_usage twilio_subaccount_usage_channel_fk; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.twilio_subaccount_usage + ADD CONSTRAINT twilio_subaccount_usage_channel_fk FOREIGN KEY (channel_id) REFERENCES public.notification_channels(id) ON DELETE CASCADE; + + +-- +-- Name: user_settings user_settings_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.user_settings + ADD CONSTRAINT user_settings_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users(id) ON DELETE CASCADE; + + +-- +-- Name: iceberg_namespaces iceberg_namespaces_catalog_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_namespaces + ADD CONSTRAINT iceberg_namespaces_catalog_id_fkey FOREIGN KEY (catalog_id) REFERENCES storage.buckets_analytics(id) ON DELETE CASCADE; + + +-- +-- Name: iceberg_tables iceberg_tables_catalog_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_tables + ADD CONSTRAINT iceberg_tables_catalog_id_fkey FOREIGN KEY (catalog_id) REFERENCES storage.buckets_analytics(id) ON DELETE CASCADE; + + +-- +-- Name: iceberg_tables iceberg_tables_namespace_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.iceberg_tables + ADD CONSTRAINT iceberg_tables_namespace_id_fkey FOREIGN KEY (namespace_id) REFERENCES storage.iceberg_namespaces(id) ON DELETE CASCADE; + + +-- +-- Name: objects objects_bucketId_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.objects + ADD CONSTRAINT "objects_bucketId_fkey" FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); + + +-- +-- Name: s3_multipart_uploads s3_multipart_uploads_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads + ADD CONSTRAINT s3_multipart_uploads_bucket_id_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); + + +-- +-- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads_parts + ADD CONSTRAINT s3_multipart_uploads_parts_bucket_id_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets(id); + + +-- +-- Name: s3_multipart_uploads_parts s3_multipart_uploads_parts_upload_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.s3_multipart_uploads_parts + ADD CONSTRAINT s3_multipart_uploads_parts_upload_id_fkey FOREIGN KEY (upload_id) REFERENCES storage.s3_multipart_uploads(id) ON DELETE CASCADE; + + +-- +-- Name: vector_indexes vector_indexes_bucket_id_fkey; Type: FK CONSTRAINT; Schema: storage; Owner: - +-- + +ALTER TABLE ONLY storage.vector_indexes + ADD CONSTRAINT vector_indexes_bucket_id_fkey FOREIGN KEY (bucket_id) REFERENCES storage.buckets_vectors(id); + + +-- +-- Name: audit_log_entries; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.audit_log_entries ENABLE ROW LEVEL SECURITY; + +-- +-- Name: flow_state; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.flow_state ENABLE ROW LEVEL SECURITY; + +-- +-- Name: identities; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.identities ENABLE ROW LEVEL SECURITY; + +-- +-- Name: instances; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.instances ENABLE ROW LEVEL SECURITY; + +-- +-- Name: mfa_amr_claims; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.mfa_amr_claims ENABLE ROW LEVEL SECURITY; + +-- +-- Name: mfa_challenges; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.mfa_challenges ENABLE ROW LEVEL SECURITY; + +-- +-- Name: mfa_factors; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.mfa_factors ENABLE ROW LEVEL SECURITY; + +-- +-- Name: one_time_tokens; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.one_time_tokens ENABLE ROW LEVEL SECURITY; + +-- +-- Name: refresh_tokens; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.refresh_tokens ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saml_providers; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.saml_providers ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saml_relay_states; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.saml_relay_states ENABLE ROW LEVEL SECURITY; + +-- +-- Name: schema_migrations; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.schema_migrations ENABLE ROW LEVEL SECURITY; + +-- +-- Name: sessions; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.sessions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: sso_domains; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.sso_domains ENABLE ROW LEVEL SECURITY; + +-- +-- Name: sso_providers; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.sso_providers ENABLE ROW LEVEL SECURITY; + +-- +-- Name: users; Type: ROW SECURITY; Schema: auth; Owner: - +-- + +ALTER TABLE auth.users ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_credits; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.addon_credits ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_credits addon_credits_admin_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_credits_admin_select ON public.addon_credits FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_credits addon_credits_admin_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_credits_admin_write ON public.addon_credits TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_credits addon_credits_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_credits_select_own ON public.addon_credits FOR SELECT TO authenticated USING ((public.is_tenant_member(tenant_id) OR (owner_id = auth.uid()))); + + +-- +-- Name: addon_products; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.addon_products ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_products addon_products_admin_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_products_admin_all ON public.addon_products TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_products addon_products_select_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_products_select_authenticated ON public.addon_products FOR SELECT TO authenticated USING (((deleted_at IS NULL) AND (is_active = true) AND (is_visible = true))); + + +-- +-- Name: addon_transactions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.addon_transactions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: addon_transactions addon_transactions_admin_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_transactions_admin_insert ON public.addon_transactions FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_transactions addon_transactions_admin_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_transactions_admin_select ON public.addon_transactions FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: addon_transactions addon_transactions_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY addon_transactions_select_own ON public.addon_transactions FOR SELECT TO authenticated USING ((public.is_tenant_member(tenant_id) OR (owner_id = auth.uid()))); + + +-- +-- Name: agenda_bloqueios; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_bloqueios ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_configuracoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_configuracoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_configuracoes agenda_configuracoes_clinic_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_configuracoes_clinic_read ON public.agenda_configuracoes FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_clinic_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_configuracoes_clinic_write ON public.agenda_configuracoes USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_configuracoes agenda_configuracoes_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_configuracoes_owner ON public.agenda_configuracoes USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_eventos; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_eventos ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_eventos agenda_eventos_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_delete ON public.agenda_eventos FOR DELETE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.delete'::text))); + + +-- +-- Name: agenda_eventos agenda_eventos_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_insert ON public.agenda_eventos FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.create'::text))); + + +-- +-- Name: agenda_eventos agenda_eventos_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_owner_all ON public.agenda_eventos TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_eventos agenda_eventos_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_select ON public.agenda_eventos FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_eventos agenda_eventos_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_eventos_update ON public.agenda_eventos FOR UPDATE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_excecoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_excecoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_excecoes agenda_excecoes_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_excecoes_owner ON public.agenda_excecoes USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_excecoes agenda_excecoes_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_excecoes_select ON public.agenda_excecoes FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_excecoes agenda_excecoes_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_excecoes_write ON public.agenda_excecoes USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_online_slots; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_online_slots ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_online_slots agenda_online_slots_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_online_slots_owner ON public.agenda_online_slots USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_online_slots agenda_online_slots_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_online_slots_select ON public.agenda_online_slots FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_online_slots agenda_online_slots_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_online_slots_write ON public.agenda_online_slots USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_regras_semanais; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_regras_semanais ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_regras_semanais_owner ON public.agenda_regras_semanais USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_regras_semanais_select ON public.agenda_regras_semanais FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_regras_semanais agenda_regras_semanais_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_regras_semanais_write ON public.agenda_regras_semanais USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_slots_bloqueados_semanais; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_slots_bloqueados_semanais ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_bloqueados_semanais_select ON public.agenda_slots_bloqueados_semanais FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_slots_bloqueados_semanais agenda_slots_bloqueados_semanais_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_bloqueados_semanais_write ON public.agenda_slots_bloqueados_semanais USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agenda_slots_regras; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agenda_slots_regras ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_slots_regras agenda_slots_regras_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_regras_select ON public.agenda_slots_regras FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: agenda_slots_regras agenda_slots_regras_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agenda_slots_regras_write ON public.agenda_slots_regras USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: agendador_configuracoes agendador_cfg_public_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_cfg_public_read ON public.agendador_configuracoes FOR SELECT TO anon USING (((ativo = true) AND (link_slug IS NOT NULL))); + + +-- +-- Name: agendador_configuracoes agendador_cfg_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_cfg_select ON public.agendador_configuracoes FOR SELECT USING ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_configuracoes agendador_cfg_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_cfg_write ON public.agendador_configuracoes USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_configuracoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agendador_configuracoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agendador_solicitacoes agendador_sol_owner_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_owner_select ON public.agendador_solicitacoes FOR SELECT USING ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_solicitacoes agendador_sol_owner_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_owner_write ON public.agendador_solicitacoes USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: agendador_solicitacoes agendador_sol_patient_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_patient_read ON public.agendador_solicitacoes FOR SELECT TO authenticated USING (((auth.uid() = user_id) OR (auth.uid() = owner_id))); + + +-- +-- Name: agendador_solicitacoes agendador_sol_public_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY agendador_sol_public_insert ON public.agendador_solicitacoes FOR INSERT TO anon WITH CHECK (true); + + +-- +-- Name: agendador_solicitacoes; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.agendador_solicitacoes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: billing_contracts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.billing_contracts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: billing_contracts billing_contracts: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "billing_contracts: owner full access" ON public.billing_contracts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_delete ON public.agenda_bloqueios FOR DELETE TO authenticated USING ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_insert ON public.agenda_bloqueios FOR INSERT TO authenticated WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_select_clinic; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_select_clinic ON public.agenda_bloqueios FOR SELECT TO authenticated USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE ((tenant_members.user_id = auth.uid()) AND (tenant_members.role = ANY (ARRAY['admin'::text, 'clinic_admin'::text, 'tenant_admin'::text, 'secretary'::text])))))); + + +-- +-- Name: agenda_bloqueios bloqueios_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_select_own ON public.agenda_bloqueios FOR SELECT TO authenticated USING ((owner_id = auth.uid())); + + +-- +-- Name: agenda_bloqueios bloqueios_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY bloqueios_update ON public.agenda_bloqueios FOR UPDATE TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: saas_docs clinic_admin_read_all_docs; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY clinic_admin_read_all_docs ON public.saas_docs FOR SELECT TO authenticated USING (((ativo = true) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = ANY (ARRAY['clinic_admin'::text, 'tenant_admin'::text]))))))); + + +-- +-- Name: commitment_services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.commitment_services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: commitment_services commitment_services: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "commitment_services: owner full access" ON public.commitment_services USING ((EXISTS ( SELECT 1 + FROM public.services s + WHERE ((s.id = commitment_services.service_id) AND (s.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.services s + WHERE ((s.id = commitment_services.service_id) AND (s.owner_id = auth.uid()))))); + + +-- +-- Name: commitment_time_logs; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.commitment_time_logs ENABLE ROW LEVEL SECURITY; + +-- +-- Name: company_profiles; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.company_profiles ENABLE ROW LEVEL SECURITY; + +-- +-- Name: company_profiles company_profiles_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_delete ON public.company_profiles FOR DELETE USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: company_profiles company_profiles_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_insert ON public.company_profiles FOR INSERT WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: company_profiles company_profiles_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_select ON public.company_profiles FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: company_profiles company_profiles_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY company_profiles_update ON public.company_profiles FOR UPDATE USING ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members + WHERE ((tenant_members.tenant_id = company_profiles.tenant_id) AND (tenant_members.user_id = auth.uid()))))); + + +-- +-- Name: commitment_time_logs ctl_delete_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_delete_for_active_member ON public.commitment_time_logs FOR DELETE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: commitment_time_logs ctl_insert_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_insert_for_active_member ON public.commitment_time_logs FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: commitment_time_logs ctl_select_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_select_for_active_member ON public.commitment_time_logs FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: commitment_time_logs ctl_update_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ctl_update_for_active_member ON public.commitment_time_logs FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = commitment_time_logs.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitments dc_delete_custom_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_delete_custom_for_active_member ON public.determined_commitments FOR DELETE TO authenticated USING (((is_native = false) AND (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: determined_commitments dc_insert_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_insert_for_active_member ON public.determined_commitments FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitments dc_select_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_select_for_active_member ON public.determined_commitments FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitments dc_update_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dc_update_for_active_member ON public.determined_commitments FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitments.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_delete_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_delete_for_active_member ON public.determined_commitment_fields FOR DELETE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_insert_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_insert_for_active_member ON public.determined_commitment_fields FOR INSERT TO authenticated WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_select_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_select_for_active_member ON public.determined_commitment_fields FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: determined_commitment_fields dcf_update_for_active_member; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dcf_update_for_active_member ON public.determined_commitment_fields FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = determined_commitment_fields.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text))))); + + +-- +-- Name: agenda_bloqueios delete own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "delete own" ON public.agenda_bloqueios FOR DELETE USING ((owner_id = auth.uid())); + + +-- +-- Name: determined_commitment_fields; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.determined_commitment_fields ENABLE ROW LEVEL SECURITY; + +-- +-- Name: determined_commitments; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.determined_commitments ENABLE ROW LEVEL SECURITY; + +-- +-- Name: dev_user_credentials dev_creds_select_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dev_creds_select_saas_admin ON public.dev_user_credentials FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles p + WHERE ((p.id = auth.uid()) AND (p.role = 'saas_admin'::text))))); + + +-- +-- Name: dev_user_credentials dev_creds_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY dev_creds_write_saas_admin ON public.dev_user_credentials TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles p + WHERE ((p.id = auth.uid()) AND (p.role = 'saas_admin'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.profiles p + WHERE ((p.id = auth.uid()) AND (p.role = 'saas_admin'::text))))); + + +-- +-- Name: dev_user_credentials; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.dev_user_credentials ENABLE ROW LEVEL SECURITY; + +-- +-- Name: email_layout_config; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.email_layout_config ENABLE ROW LEVEL SECURITY; + +-- +-- Name: email_templates_global; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.email_templates_global ENABLE ROW LEVEL SECURITY; + +-- +-- Name: email_templates_tenant; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.email_templates_tenant ENABLE ROW LEVEL SECURITY; + +-- +-- Name: entitlements_invalidation ent_inv_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ent_inv_select_own ON public.entitlements_invalidation FOR SELECT USING (((owner_id = auth.uid()) OR public.is_saas_admin())); + + +-- +-- Name: entitlements_invalidation ent_inv_update_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ent_inv_update_saas ON public.entitlements_invalidation FOR UPDATE USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: entitlements_invalidation ent_inv_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY ent_inv_write_saas ON public.entitlements_invalidation FOR INSERT WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: entitlements_invalidation; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.entitlements_invalidation ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_faq faq_admin_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_admin_write ON public.saas_faq TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = ANY (ARRAY['saas_admin'::text, 'tenant_admin'::text, 'clinic_admin'::text])))))); + + +-- +-- Name: saas_faq faq_auth_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_auth_read ON public.saas_faq FOR SELECT TO authenticated USING ((ativo = true)); + + +-- +-- Name: saas_faq_itens faq_itens_admin_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_itens_admin_write ON public.saas_faq_itens TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = ANY (ARRAY['saas_admin'::text, 'tenant_admin'::text, 'clinic_admin'::text])))))); + + +-- +-- Name: saas_faq_itens faq_itens_auth_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_itens_auth_read ON public.saas_faq_itens FOR SELECT TO authenticated USING (((ativo = true) AND (EXISTS ( SELECT 1 + FROM public.saas_docs d + WHERE ((d.id = saas_faq_itens.doc_id) AND (d.ativo = true)))))); + + +-- +-- Name: saas_faq faq_public_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY faq_public_read ON public.saas_faq FOR SELECT USING (((publico = true) AND (ativo = true))); + + +-- +-- Name: features; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.features ENABLE ROW LEVEL SECURITY; + +-- +-- Name: features features_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY features_read_authenticated ON public.features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: features features_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY features_write_saas_admin ON public.features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: feriados; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.feriados ENABLE ROW LEVEL SECURITY; + +-- +-- Name: feriados feriados_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_delete ON public.feriados FOR DELETE USING ((owner_id = auth.uid())); + + +-- +-- Name: feriados feriados_global_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_global_select ON public.feriados FOR SELECT USING ((tenant_id IS NULL)); + + +-- +-- Name: feriados feriados_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_insert ON public.feriados FOR INSERT WITH CHECK ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_saas_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_saas_delete ON public.feriados FOR DELETE USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_saas_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_saas_insert ON public.feriados FOR INSERT WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_saas_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_saas_select ON public.feriados FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: feriados feriados_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY feriados_select ON public.feriados FOR SELECT USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: financial_categories; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.financial_categories ENABLE ROW LEVEL SECURITY; + +-- +-- Name: financial_categories financial_categories_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_categories_self ON public.financial_categories USING ((auth.uid() = user_id)) WITH CHECK ((auth.uid() = user_id)); + + +-- +-- Name: financial_exceptions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.financial_exceptions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: financial_exceptions financial_exceptions: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "financial_exceptions: owner full access" ON public.financial_exceptions USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: financial_exceptions financial_exceptions: tenant members read clinic rules; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "financial_exceptions: tenant members read clinic rules" ON public.financial_exceptions FOR SELECT USING (((owner_id IS NULL) AND (EXISTS ( SELECT 1 + FROM public.owner_users ou + WHERE ((ou.owner_id = financial_exceptions.tenant_id) AND (ou.user_id = auth.uid())))))); + + +-- +-- Name: financial_records; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.financial_records ENABLE ROW LEVEL SECURITY; + +-- +-- Name: financial_records financial_records_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_records_self ON public.financial_records USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: financial_records financial_records_tenant_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_records_tenant_admin ON public.financial_records FOR SELECT USING (((tenant_id IS NOT NULL) AND public.is_tenant_admin(tenant_id))); + + +-- +-- Name: financial_records financial_records_tenant_member_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY financial_records_tenant_member_read ON public.financial_records FOR SELECT USING (((tenant_id IS NOT NULL) AND (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = financial_records.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: email_templates_global global templates readable by authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "global templates readable by authenticated" ON public.email_templates_global FOR SELECT USING ((auth.role() = 'authenticated'::text)); + + +-- +-- Name: global_notices; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.global_notices ENABLE ROW LEVEL SECURITY; + +-- +-- Name: global_notices global_notices_saas_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY global_notices_saas_all ON public.global_notices TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: global_notices global_notices_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY global_notices_select ON public.global_notices FOR SELECT TO authenticated USING ((is_active = true)); + + +-- +-- Name: agenda_bloqueios insert own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "insert own" ON public.agenda_bloqueios FOR INSERT WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: insurance_plan_services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.insurance_plan_services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: insurance_plan_services insurance_plan_services_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY insurance_plan_services_owner ON public.insurance_plan_services USING ((EXISTS ( SELECT 1 + FROM public.insurance_plans ip + WHERE ((ip.id = insurance_plan_services.insurance_plan_id) AND (ip.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.insurance_plans ip + WHERE ((ip.id = insurance_plan_services.insurance_plan_id) AND (ip.owner_id = auth.uid()))))); + + +-- +-- Name: insurance_plans; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.insurance_plans ENABLE ROW LEVEL SECURITY; + +-- +-- Name: insurance_plans insurance_plans: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "insurance_plans: owner full access" ON public.insurance_plans USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: login_carousel_slides; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.login_carousel_slides ENABLE ROW LEVEL SECURITY; + +-- +-- Name: medicos; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.medicos ENABLE ROW LEVEL SECURITY; + +-- +-- Name: medicos medicos: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "medicos: owner full access" ON public.medicos USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: module_features; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.module_features ENABLE ROW LEVEL SECURITY; + +-- +-- Name: module_features module_features_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY module_features_read_authenticated ON public.module_features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: module_features module_features_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY module_features_write_saas_admin ON public.module_features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: modules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.modules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: modules modules_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY modules_read_authenticated ON public.modules FOR SELECT TO authenticated USING (true); + + +-- +-- Name: modules modules_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY modules_write_saas_admin ON public.modules TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: notice_dismissals; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notice_dismissals ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notice_dismissals notice_dismissals_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notice_dismissals_own ON public.notice_dismissals TO authenticated USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: notification_logs notif_logs_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_logs_owner ON public.notification_logs FOR SELECT USING ((owner_id = auth.uid())); + + +-- +-- Name: notification_preferences notif_prefs_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_prefs_owner ON public.notification_preferences USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: notification_queue notif_queue_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_queue_owner ON public.notification_queue FOR SELECT USING ((owner_id = auth.uid())); + + +-- +-- Name: notification_schedules notif_schedules_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_schedules_owner ON public.notification_schedules USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: notification_templates notif_templates_admin_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_templates_admin_all ON public.notification_templates TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: notification_templates notif_templates_read_global; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_templates_read_global ON public.notification_templates FOR SELECT TO authenticated USING (((deleted_at IS NULL) AND (((tenant_id IS NULL) AND (is_default = true)) OR (owner_id = auth.uid()) OR public.is_tenant_member(tenant_id)))); + + +-- +-- Name: notification_templates notif_templates_write_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notif_templates_write_owner ON public.notification_templates TO authenticated USING (((owner_id = auth.uid()) OR public.is_tenant_member(tenant_id))) WITH CHECK (((owner_id = auth.uid()) OR public.is_tenant_member(tenant_id))); + + +-- +-- Name: notification_channels; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_channels ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_channels notification_channels_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY notification_channels_owner ON public.notification_channels USING (((owner_id = auth.uid()) AND (deleted_at IS NULL))) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: notification_logs; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_logs ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_preferences; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_preferences ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_queue; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_queue ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_schedules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_schedules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notification_templates; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notification_templates ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notifications; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY; + +-- +-- Name: notifications owner only; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "owner only" ON public.notifications USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: owner_users; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.owner_users ENABLE ROW LEVEL SECURITY; + +-- +-- Name: owner_users owner_users: user can read own links; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "owner_users: user can read own links" ON public.owner_users FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: patient_contacts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_contacts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_contacts patient_contacts_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_contacts_select ON public.patient_contacts FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_contacts patient_contacts_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_contacts_write ON public.patient_contacts USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_discounts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_discounts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_discounts patient_discounts: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "patient_discounts: owner full access" ON public.patient_discounts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_group_patient; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_group_patient ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_group_patient patient_group_patient_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_group_patient_owner_all ON public.patient_group_patient TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.patients p + WHERE ((p.id = patient_group_patient.patient_id) AND (p.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.patients p + WHERE ((p.id = patient_group_patient.patient_id) AND (p.owner_id = auth.uid()))))); + + +-- +-- Name: patient_group_patient patient_group_patient_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_group_patient_select ON public.patient_group_patient FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_group_patient patient_group_patient_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_group_patient_write ON public.patient_group_patient USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_groups; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_groups ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_groups patient_groups_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_groups_owner_all ON public.patient_groups TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_groups patient_groups_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_groups_select ON public.patient_groups FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_groups patient_groups_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_groups_write ON public.patient_groups USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_intake_requests; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_intake_requests ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_intake_requests patient_intake_requests_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_intake_requests_owner_all ON public.patient_intake_requests TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_intake_requests patient_intake_requests_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_intake_requests_select ON public.patient_intake_requests FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_intake_requests patient_intake_requests_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_intake_requests_write ON public.patient_intake_requests USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_invites; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_invites ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_invites patient_invites_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_invites_owner_all ON public.patient_invites TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_invites patient_invites_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_invites_select ON public.patient_invites FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_invites patient_invites_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_invites_write ON public.patient_invites USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_patient_tag; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_patient_tag ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_patient_tag patient_patient_tag_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_patient_tag_owner_all ON public.patient_patient_tag TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_patient_tag patient_patient_tag_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_patient_tag_select ON public.patient_patient_tag FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_patient_tag patient_patient_tag_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_patient_tag_write ON public.patient_patient_tag USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_status_history; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_status_history ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_support_contacts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_support_contacts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_tags; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_tags ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patient_tags patient_tags_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_tags_owner_all ON public.patient_tags TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_tags patient_tags_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_tags_select ON public.patient_tags FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_tags patient_tags_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patient_tags_write ON public.patient_tags USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_timeline; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patient_timeline ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patients; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.patients ENABLE ROW LEVEL SECURITY; + +-- +-- Name: patients patients_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_delete ON public.patients FOR DELETE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.delete'::text))); + + +-- +-- Name: patients patients_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_insert ON public.patients FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.create'::text))); + + +-- +-- Name: patients patients_owner_all; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_owner_all ON public.patients TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patients patients_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_select ON public.patients FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patients patients_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY patients_update ON public.patients FOR UPDATE USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: payment_settings; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.payment_settings ENABLE ROW LEVEL SECURITY; + +-- +-- Name: payment_settings payment_settings: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "payment_settings: owner full access" ON public.payment_settings USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: plan_features; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.plan_features ENABLE ROW LEVEL SECURITY; + +-- +-- Name: plan_features plan_features_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plan_features_read_authenticated ON public.plan_features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plan_features plan_features_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plan_features_write_saas_admin ON public.plan_features TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: plans; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.plans ENABLE ROW LEVEL SECURITY; + +-- +-- Name: plans plans_read_authenticated; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plans_read_authenticated ON public.plans FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plans plans_write_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY plans_write_saas_admin ON public.plans TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: professional_pricing; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.professional_pricing ENABLE ROW LEVEL SECURITY; + +-- +-- Name: professional_pricing professional_pricing: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "professional_pricing: owner full access" ON public.professional_pricing USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: profiles; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; + +-- +-- Name: profiles profiles_insert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_insert_own ON public.profiles FOR INSERT WITH CHECK ((id = auth.uid())); + + +-- +-- Name: profiles profiles_read_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_read_saas_admin ON public.profiles FOR SELECT USING (public.is_saas_admin()); + + +-- +-- Name: profiles profiles_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_select_own ON public.profiles FOR SELECT USING ((id = auth.uid())); + + +-- +-- Name: profiles profiles_update_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY profiles_update_own ON public.profiles FOR UPDATE USING ((id = auth.uid())) WITH CHECK ((id = auth.uid())); + + +-- +-- Name: patient_support_contacts psc: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "psc: owner full access" ON public.patient_support_contacts USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: patient_status_history psh_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY psh_insert ON public.patient_status_history FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_status_history psh_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY psh_select ON public.patient_status_history FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: patient_timeline pt_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY pt_insert ON public.patient_timeline FOR INSERT WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.edit'::text))); + + +-- +-- Name: patient_timeline pt_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY pt_select ON public.patient_timeline FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'patients.view'::text))); + + +-- +-- Name: login_carousel_slides public_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY public_read ON public.login_carousel_slides FOR SELECT USING ((ativo = true)); + + +-- +-- Name: features read features (auth); Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "read features (auth)" ON public.features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plan_features read plan_features (auth); Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "read plan_features (auth)" ON public.plan_features FOR SELECT TO authenticated USING (true); + + +-- +-- Name: plans read plans (auth); Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "read plans (auth)" ON public.plans FOR SELECT TO authenticated USING (true); + + +-- +-- Name: recurrence_exceptions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.recurrence_exceptions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: recurrence_exceptions recurrence_exceptions_tenant; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_exceptions_tenant ON public.recurrence_exceptions TO authenticated USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))) WITH CHECK ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: recurrence_rule_services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.recurrence_rule_services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: recurrence_rule_services recurrence_rule_services: clinic read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "recurrence_rule_services: clinic read" ON public.recurrence_rule_services FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND public.is_clinic_tenant(r.tenant_id) AND public.is_tenant_member(r.tenant_id) AND public.tenant_has_feature(r.tenant_id, 'agenda.view'::text))))); + + +-- +-- Name: recurrence_rule_services recurrence_rule_services: clinic write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "recurrence_rule_services: clinic write" ON public.recurrence_rule_services USING ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND public.is_clinic_tenant(r.tenant_id) AND public.is_tenant_member(r.tenant_id) AND public.tenant_has_feature(r.tenant_id, 'agenda.edit'::text))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND public.is_clinic_tenant(r.tenant_id) AND public.is_tenant_member(r.tenant_id) AND public.tenant_has_feature(r.tenant_id, 'agenda.edit'::text))))); + + +-- +-- Name: recurrence_rule_services recurrence_rule_services: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "recurrence_rule_services: owner full access" ON public.recurrence_rule_services TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND (r.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.recurrence_rules r + WHERE ((r.id = recurrence_rule_services.rule_id) AND (r.owner_id = auth.uid()))))); + + +-- +-- Name: recurrence_rules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.recurrence_rules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: recurrence_rules recurrence_rules_clinic_read; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_rules_clinic_read ON public.recurrence_rules FOR SELECT USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.view'::text))); + + +-- +-- Name: recurrence_rules recurrence_rules_clinic_write; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_rules_clinic_write ON public.recurrence_rules USING ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))) WITH CHECK ((public.is_clinic_tenant(tenant_id) AND public.is_tenant_member(tenant_id) AND public.tenant_has_feature(tenant_id, 'agenda.edit'::text))); + + +-- +-- Name: recurrence_rules recurrence_rules_owner; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY recurrence_rules_owner ON public.recurrence_rules TO authenticated USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: subscription_intents_legacy saas_admin can read subscription_intents; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "saas_admin can read subscription_intents" ON public.subscription_intents_legacy FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins a + WHERE (a.user_id = auth.uid())))); + + +-- +-- Name: subscription_intents_legacy saas_admin can update subscription_intents; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "saas_admin can update subscription_intents" ON public.subscription_intents_legacy FOR UPDATE TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins a + WHERE (a.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins a + WHERE (a.user_id = auth.uid())))); + + +-- +-- Name: login_carousel_slides saas_admin_full; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY saas_admin_full ON public.login_carousel_slides USING ((EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text))))); + + +-- +-- Name: saas_docs saas_admin_full_access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY saas_admin_full_access ON public.saas_docs TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid())))); + + +-- +-- Name: saas_admins; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_admins ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_admins saas_admins_select_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY saas_admins_select_self ON public.saas_admins FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: saas_doc_votos; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_doc_votos ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_docs; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_docs ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_faq; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_faq ENABLE ROW LEVEL SECURITY; + +-- +-- Name: saas_faq_itens; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.saas_faq_itens ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_bloqueios select own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "select own" ON public.agenda_bloqueios FOR SELECT USING ((owner_id = auth.uid())); + + +-- +-- Name: twilio_subaccount_usage service_role_manage_usage; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY service_role_manage_usage ON public.twilio_subaccount_usage USING ((auth.role() = 'service_role'::text)); + + +-- +-- Name: services; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.services ENABLE ROW LEVEL SECURITY; + +-- +-- Name: services services: owner full access; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "services: owner full access" ON public.services USING ((owner_id = auth.uid())) WITH CHECK ((owner_id = auth.uid())); + + +-- +-- Name: subscription_events; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.subscription_events ENABLE ROW LEVEL SECURITY; + +-- +-- Name: subscription_events subscription_events_read_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_events_read_saas ON public.subscription_events FOR SELECT USING (public.is_saas_admin()); + + +-- +-- Name: subscription_events subscription_events_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_events_write_saas ON public.subscription_events FOR INSERT WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: subscription_intents_legacy subscription_intents_insert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_intents_insert_own ON public.subscription_intents_legacy FOR INSERT TO authenticated WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: subscription_intents_legacy; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.subscription_intents_legacy ENABLE ROW LEVEL SECURITY; + +-- +-- Name: subscription_intents_legacy subscription_intents_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscription_intents_select_own ON public.subscription_intents_legacy FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: subscriptions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.subscriptions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: subscriptions subscriptions read own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "subscriptions read own" ON public.subscriptions FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: subscriptions subscriptions: read if linked owner_users; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "subscriptions: read if linked owner_users" ON public.subscriptions FOR SELECT TO authenticated USING ((EXISTS ( SELECT 1 + FROM public.owner_users ou + WHERE ((ou.owner_id = subscriptions.user_id) AND (ou.user_id = auth.uid()))))); + + +-- +-- Name: subscriptions subscriptions_insert_own_personal; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_insert_own_personal ON public.subscriptions FOR INSERT TO authenticated WITH CHECK (((user_id = auth.uid()) AND (tenant_id IS NULL))); + + +-- +-- Name: subscriptions subscriptions_no_direct_update; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_no_direct_update ON public.subscriptions FOR UPDATE TO authenticated USING (false) WITH CHECK (false); + + +-- +-- Name: subscriptions subscriptions_read_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_read_own ON public.subscriptions FOR SELECT TO authenticated USING (((user_id = auth.uid()) OR public.is_saas_admin())); + + +-- +-- Name: subscriptions subscriptions_select_for_tenant_members; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_select_for_tenant_members ON public.subscriptions FOR SELECT TO authenticated USING (((tenant_id IS NOT NULL) AND (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = subscriptions.tenant_id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: subscriptions subscriptions_select_own_personal; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_select_own_personal ON public.subscriptions FOR SELECT TO authenticated USING (((user_id = auth.uid()) AND (tenant_id IS NULL))); + + +-- +-- Name: subscriptions subscriptions_update_only_saas_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY subscriptions_update_only_saas_admin ON public.subscriptions FOR UPDATE TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: support_sessions; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.support_sessions ENABLE ROW LEVEL SECURITY; + +-- +-- Name: support_sessions support_sessions_saas_delete; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY support_sessions_saas_delete ON public.support_sessions FOR DELETE USING (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text)))))); + + +-- +-- Name: support_sessions support_sessions_saas_insert; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY support_sessions_saas_insert ON public.support_sessions FOR INSERT WITH CHECK (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text)))))); + + +-- +-- Name: support_sessions support_sessions_saas_select; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY support_sessions_saas_select ON public.support_sessions FOR SELECT USING (((auth.uid() = admin_id) AND (EXISTS ( SELECT 1 + FROM public.profiles + WHERE ((profiles.id = auth.uid()) AND (profiles.role = 'saas_admin'::text)))))); + + +-- +-- Name: email_templates_tenant tenant manages own overrides; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "tenant manages own overrides" ON public.email_templates_tenant USING ((tenant_id = auth.uid())) WITH CHECK ((tenant_id = auth.uid())); + + +-- +-- Name: email_layout_config tenant owns email layout config; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "tenant owns email layout config" ON public.email_layout_config USING ((tenant_id = auth.uid())) WITH CHECK ((tenant_id = auth.uid())); + + +-- +-- Name: tenant_members; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.tenant_members ENABLE ROW LEVEL SECURITY; + +-- +-- Name: tenant_members tenant_members_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_members_write_saas ON public.tenant_members TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: tenant_modules; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.tenant_modules ENABLE ROW LEVEL SECURITY; + +-- +-- Name: tenant_modules tenant_modules_read_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_modules_read_own ON public.tenant_modules FOR SELECT TO authenticated USING (((owner_id = auth.uid()) OR public.is_saas_admin())); + + +-- +-- Name: tenant_modules tenant_modules_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_modules_write_saas ON public.tenant_modules TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: twilio_subaccount_usage tenant_select_own_usage; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenant_select_own_usage ON public.twilio_subaccount_usage FOR SELECT USING ((tenant_id IN ( SELECT tenant_members.tenant_id + FROM public.tenant_members + WHERE (tenant_members.user_id = auth.uid())))); + + +-- +-- Name: tenants; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.tenants ENABLE ROW LEVEL SECURITY; + +-- +-- Name: tenants tenants_read_members; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenants_read_members ON public.tenants FOR SELECT TO authenticated USING ((public.is_saas_admin() OR (EXISTS ( SELECT 1 + FROM public.tenant_members tm + WHERE ((tm.tenant_id = tenants.id) AND (tm.user_id = auth.uid()) AND (tm.status = 'active'::text)))))); + + +-- +-- Name: tenants tenants_write_saas; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tenants_write_saas ON public.tenants TO authenticated USING (public.is_saas_admin()) WITH CHECK (public.is_saas_admin()); + + +-- +-- Name: therapist_payout_records; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.therapist_payout_records ENABLE ROW LEVEL SECURITY; + +-- +-- Name: therapist_payout_records therapist_payout_records_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payout_records_self ON public.therapist_payout_records USING ((EXISTS ( SELECT 1 + FROM public.therapist_payouts tp + WHERE ((tp.id = therapist_payout_records.payout_id) AND (tp.owner_id = auth.uid()))))) WITH CHECK ((EXISTS ( SELECT 1 + FROM public.therapist_payouts tp + WHERE ((tp.id = therapist_payout_records.payout_id) AND (tp.owner_id = auth.uid()))))); + + +-- +-- Name: therapist_payout_records therapist_payout_records_tenant_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payout_records_tenant_admin ON public.therapist_payout_records FOR SELECT USING ((EXISTS ( SELECT 1 + FROM public.therapist_payouts tp + WHERE ((tp.id = therapist_payout_records.payout_id) AND public.is_tenant_admin(tp.tenant_id))))); + + +-- +-- Name: therapist_payouts; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.therapist_payouts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: therapist_payouts therapist_payouts_self; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payouts_self ON public.therapist_payouts USING ((auth.uid() = owner_id)) WITH CHECK ((auth.uid() = owner_id)); + + +-- +-- Name: therapist_payouts therapist_payouts_tenant_admin; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY therapist_payouts_tenant_admin ON public.therapist_payouts FOR SELECT USING (((tenant_id IS NOT NULL) AND public.is_tenant_admin(tenant_id))); + + +-- +-- Name: tenant_members tm_select_admin_all_members; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tm_select_admin_all_members ON public.tenant_members FOR SELECT TO authenticated USING (public.is_tenant_admin(tenant_id)); + + +-- +-- Name: tenant_members tm_select_own_membership; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY tm_select_own_membership ON public.tenant_members FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: twilio_subaccount_usage; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.twilio_subaccount_usage ENABLE ROW LEVEL SECURITY; + +-- +-- Name: agenda_bloqueios update own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY "update own" ON public.agenda_bloqueios FOR UPDATE USING ((owner_id = auth.uid())); + + +-- +-- Name: user_settings; Type: ROW SECURITY; Schema: public; Owner: - +-- + +ALTER TABLE public.user_settings ENABLE ROW LEVEL SECURITY; + +-- +-- Name: user_settings user_settings_insert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY user_settings_insert_own ON public.user_settings FOR INSERT WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: user_settings user_settings_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY user_settings_select_own ON public.user_settings FOR SELECT USING ((user_id = auth.uid())); + + +-- +-- Name: user_settings user_settings_update_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY user_settings_update_own ON public.user_settings FOR UPDATE USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: saas_docs users_read_usuario_docs; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY users_read_usuario_docs ON public.saas_docs FOR SELECT TO authenticated USING (((ativo = true) AND (tipo_acesso = 'usuario'::text))); + + +-- +-- Name: saas_doc_votos votos_select_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY votos_select_own ON public.saas_doc_votos FOR SELECT TO authenticated USING ((user_id = auth.uid())); + + +-- +-- Name: saas_doc_votos votos_upsert_own; Type: POLICY; Schema: public; Owner: - +-- + +CREATE POLICY votos_upsert_own ON public.saas_doc_votos TO authenticated USING ((user_id = auth.uid())) WITH CHECK ((user_id = auth.uid())); + + +-- +-- Name: messages; Type: ROW SECURITY; Schema: realtime; Owner: - +-- + +ALTER TABLE realtime.messages ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects Allow authenticated updates; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "Allow authenticated updates" ON storage.objects FOR UPDATE TO authenticated USING ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); + + +-- +-- Name: objects Allow authenticated uploads; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "Allow authenticated uploads" ON storage.objects FOR INSERT TO authenticated WITH CHECK ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); + + +-- +-- Name: objects Public read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "Public read" ON storage.objects FOR SELECT USING ((bucket_id = ANY (ARRAY['avatars'::text, 'logos'::text]))); + + +-- +-- Name: objects agendador_storage_owner_delete; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_owner_delete ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects agendador_storage_owner_insert; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_owner_insert ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects agendador_storage_owner_update; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_owner_update ON storage.objects FOR UPDATE TO authenticated USING (((bucket_id = 'agendador'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects agendador_storage_public_read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY agendador_storage_public_read ON storage.objects FOR SELECT USING ((bucket_id = 'agendador'::text)); + + +-- +-- Name: objects avatars authenticated upload; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars authenticated upload" ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects avatars owner delete; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars owner delete" ON storage.objects FOR DELETE USING (((bucket_id = 'avatars'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects avatars owner update; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars owner update" ON storage.objects FOR UPDATE USING (((bucket_id = 'avatars'::text) AND ((storage.foldername(name))[1] = (auth.uid())::text))); + + +-- +-- Name: objects avatars public read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY "avatars public read" ON storage.objects FOR SELECT USING ((bucket_id = 'avatars'::text)); + + +-- +-- Name: objects avatars_delete_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_delete_own ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_delete_own_folder; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_delete_own_folder ON storage.objects FOR DELETE USING (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); + + +-- +-- Name: objects avatars_insert_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_insert_own ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_insert_own_folder; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_insert_own_folder ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); + + +-- +-- Name: objects avatars_read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_read ON storage.objects FOR SELECT USING ((bucket_id = 'avatars'::text)); + + +-- +-- Name: objects avatars_select_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_select_own ON storage.objects FOR SELECT TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_update_own; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_update_own ON storage.objects FOR UPDATE TO authenticated USING (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))) WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ ((auth.uid())::text || '/%'::text)))); + + +-- +-- Name: objects avatars_update_own_folder; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY avatars_update_own_folder ON storage.objects FOR UPDATE USING (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))) WITH CHECK (((bucket_id = 'avatars'::text) AND (auth.role() = 'authenticated'::text) AND (name ~~ (('owners/'::text || auth.uid()) || '/%'::text)))); + + +-- +-- Name: buckets; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.buckets ENABLE ROW LEVEL SECURITY; + +-- +-- Name: buckets_analytics; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.buckets_analytics ENABLE ROW LEVEL SECURITY; + +-- +-- Name: buckets_vectors; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.buckets_vectors ENABLE ROW LEVEL SECURITY; + +-- +-- Name: iceberg_namespaces; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.iceberg_namespaces ENABLE ROW LEVEL SECURITY; + +-- +-- Name: iceberg_tables; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.iceberg_tables ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects intake_read_anon; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_read_anon ON storage.objects FOR SELECT TO anon USING (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: objects intake_read_public; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_read_public ON storage.objects FOR SELECT USING (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: objects intake_upload_anon; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_upload_anon ON storage.objects FOR INSERT TO anon WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: objects intake_upload_public; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY intake_upload_public ON storage.objects FOR INSERT WITH CHECK (((bucket_id = 'avatars'::text) AND (name ~~ 'intakes/%'::text))); + + +-- +-- Name: migrations; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.migrations ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects public_read; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY public_read ON storage.objects FOR SELECT USING ((bucket_id = 'saas-docs'::text)); + + +-- +-- Name: s3_multipart_uploads; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.s3_multipart_uploads ENABLE ROW LEVEL SECURITY; + +-- +-- Name: s3_multipart_uploads_parts; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.s3_multipart_uploads_parts ENABLE ROW LEVEL SECURITY; + +-- +-- Name: objects saas_admin_delete; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY saas_admin_delete ON storage.objects FOR DELETE TO authenticated USING (((bucket_id = 'saas-docs'::text) AND (EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid()))))); + + +-- +-- Name: objects saas_admin_upload; Type: POLICY; Schema: storage; Owner: - +-- + +CREATE POLICY saas_admin_upload ON storage.objects FOR INSERT TO authenticated WITH CHECK (((bucket_id = 'saas-docs'::text) AND (EXISTS ( SELECT 1 + FROM public.saas_admins + WHERE (saas_admins.user_id = auth.uid()))))); + + +-- +-- Name: vector_indexes; Type: ROW SECURITY; Schema: storage; Owner: - +-- + +ALTER TABLE storage.vector_indexes ENABLE ROW LEVEL SECURITY; + +-- +-- Name: supabase_realtime; Type: PUBLICATION; Schema: -; Owner: - +-- + +CREATE PUBLICATION supabase_realtime WITH (publish = 'insert, update, delete, truncate'); + + +-- +-- Name: supabase_realtime_messages_publication; Type: PUBLICATION; Schema: -; Owner: - +-- + +CREATE PUBLICATION supabase_realtime_messages_publication WITH (publish = 'insert, update, delete, truncate'); + + +-- +-- Name: supabase_realtime notifications; Type: PUBLICATION TABLE; Schema: public; Owner: - +-- + +ALTER PUBLICATION supabase_realtime ADD TABLE ONLY public.notifications; + + +-- +-- Name: supabase_realtime_messages_publication messages; Type: PUBLICATION TABLE; Schema: realtime; Owner: - +-- + +ALTER PUBLICATION supabase_realtime_messages_publication ADD TABLE ONLY realtime.messages; + + +-- +-- Name: issue_graphql_placeholder; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_graphql_placeholder ON sql_drop + WHEN TAG IN ('DROP EXTENSION') + EXECUTE FUNCTION extensions.set_graphql_placeholder(); + + +-- +-- Name: issue_pg_cron_access; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_pg_cron_access ON ddl_command_end + WHEN TAG IN ('CREATE EXTENSION') + EXECUTE FUNCTION extensions.grant_pg_cron_access(); + + +-- +-- Name: issue_pg_graphql_access; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_pg_graphql_access ON ddl_command_end + WHEN TAG IN ('CREATE FUNCTION') + EXECUTE FUNCTION extensions.grant_pg_graphql_access(); + + +-- +-- Name: issue_pg_net_access; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER issue_pg_net_access ON ddl_command_end + WHEN TAG IN ('CREATE EXTENSION') + EXECUTE FUNCTION extensions.grant_pg_net_access(); + + +-- +-- Name: pgrst_ddl_watch; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER pgrst_ddl_watch ON ddl_command_end + EXECUTE FUNCTION extensions.pgrst_ddl_watch(); + + +-- +-- Name: pgrst_drop_watch; Type: EVENT TRIGGER; Schema: -; Owner: - +-- + +CREATE EVENT TRIGGER pgrst_drop_watch ON sql_drop + EXECUTE FUNCTION extensions.pgrst_drop_watch(); + + +-- +-- PostgreSQL database dump complete +-- + +\unrestrict svMjIKHsYwcTwAX1IxwEB8W23fUXSRnKpOVx0zBmyOtnoKw2CFaSxxP2G5YRkJY + diff --git a/database-novo/generate-dashboard.cjs b/database-novo/generate-dashboard.cjs new file mode 100644 index 0000000..a655c1c --- /dev/null +++ b/database-novo/generate-dashboard.cjs @@ -0,0 +1,457 @@ +#!/usr/bin/env node +// ============================================================================= +// AgenciaPsi — Dashboard Generator +// ============================================================================= +// Uso: +// node generate-dashboard.js → usa backup mais recente +// node generate-dashboard.js 2026-03-27 → usa backup de data específica +// +// Lê de: ./database-novo/backups/YYYY-MM-DD/schema.sql +// Gera: ./dashboard.html (na mesma pasta do script) +// ============================================================================= + +const fs = require('fs'); +const path = require('path'); + +const BACKUPS_DIR = path.join(__dirname, 'backups'); +const OUTPUT_FILE = path.join(__dirname, 'dashboard.html'); + +// --------------------------------------------------------------------------- +// Cores por domínio +// --------------------------------------------------------------------------- +const DOMAIN_COLORS = { + 'SaaS / Planos': '#4f8cff', + 'Tenants / Multi-tenant': '#6ee7b7', + 'Pacientes': '#f472b6', + 'Agenda': '#fb923c', + 'Financeiro': '#a78bfa', + 'Serviços / Commitments': '#34d399', + 'Notificações': '#38bdf8', + 'Email / Comunicação': '#fbbf24', + 'SaaS Admin': '#94a3b8', +}; + +// --------------------------------------------------------------------------- +// Mapeamento domínio → tabelas +// Adicione novas tabelas aqui quando criar migrations +// --------------------------------------------------------------------------- +const DOMAIN_TABLES = { + 'SaaS / Planos': [ + 'plans','plan_features','plan_prices','plan_public','plan_public_bullets', + 'features','modules','module_features','subscriptions','subscription_events', + 'subscription_intents_personal','subscription_intents_tenant','subscription_intents_legacy', + 'addon_products','addon_credits','addon_transactions', + 'tenant_features','tenant_modules','entitlements_invalidation','tenant_feature_exceptions_log', + ], + 'Tenants / Multi-tenant': [ + 'tenants','tenant_members','tenant_invites','owner_users', + 'profiles','company_profiles','billing_contracts','payment_settings','support_sessions', + ], + 'Pacientes': [ + 'patients','patient_groups','patient_group_patient','patient_tags', + 'patient_patient_tag','patient_discounts','patient_invites','patient_intake_requests', + ], + 'Agenda': [ + 'agenda_eventos','agenda_configuracoes','agenda_bloqueios','agenda_excecoes', + 'agenda_online_slots','agenda_regras_semanais','agenda_slots_bloqueados_semanais', + 'agenda_slots_regras','agendador_configuracoes','agendador_solicitacoes', + 'feriados','recurrence_rules','recurrence_exceptions','recurrence_rule_services', + ], + 'Financeiro': [ + 'financial_records','financial_categories','financial_exceptions', + 'therapist_payouts','therapist_payout_records', + 'insurance_plans','insurance_plan_services','professional_pricing', + ], + 'Serviços / Commitments': [ + 'services','determined_commitments','determined_commitment_fields', + 'commitment_services','commitment_time_logs', + ], + 'Notificações': [ + 'notifications','notification_channels','notification_templates', + 'notification_queue','notification_logs','notification_preferences', + 'notification_schedules','twilio_subaccount_usage', + ], + 'Email / Comunicação': [ + 'email_templates_global','email_templates_tenant','email_layout_config', + 'global_notices','notice_dismissals','login_carousel_slides', + ], + 'SaaS Admin': [ + 'saas_admins','saas_docs','saas_doc_votos','saas_faq','saas_faq_itens', + 'user_settings','dev_user_credentials','_db_migrations', + ], +}; + +// --------------------------------------------------------------------------- +// 1. Resolve qual schema.sql usar +// --------------------------------------------------------------------------- +function resolveSchema() { + const arg = process.argv[2]; + + if (!fs.existsSync(BACKUPS_DIR)) { + console.error(`✖ Pasta não encontrada: ${BACKUPS_DIR}`); + console.error(` Certifique-se que o script está na raiz do projeto.`); + process.exit(1); + } + + const available = fs.readdirSync(BACKUPS_DIR) + .filter(f => /^\d{4}-\d{2}-\d{2}$/.test(f)) + .sort() + .reverse(); + + if (available.length === 0) { + console.error('✖ Nenhum backup encontrado em database-novo/backups/'); + console.error(' Rode primeiro: node db.cjs backup'); + process.exit(1); + } + + const date = (arg && /^\d{4}-\d{2}-\d{2}$/.test(arg)) ? arg : available[0]; + + if (!available.includes(date)) { + console.error(`✖ Backup não encontrado para: ${date}`); + console.error(` Disponíveis: ${available.join(', ')}`); + process.exit(1); + } + + const schemaPath = path.join(BACKUPS_DIR, date, 'schema.sql'); + if (!fs.existsSync(schemaPath)) { + console.error(`✖ schema.sql não encontrado em database-novo/backups/${date}/`); + process.exit(1); + } + + return { schemaPath, date, available }; +} + +// --------------------------------------------------------------------------- +// 2. Parse do schema.sql — extrai tabelas, colunas e FKs +// --------------------------------------------------------------------------- +function parseSchema(content) { + const tables = {}; + + // Tabelas public.* + const tableRe = /CREATE TABLE (public\.\S+)\s*\(([\s\S]*?)\);/gm; + let m; + while ((m = tableRe.exec(content)) !== null) { + const name = m[1].replace('public.', ''); + const body = m[2]; + const columns = []; + + for (let line of body.split('\n')) { + line = line.trim().replace(/,$/, ''); + if (!line || line.startsWith('--')) continue; + if (/^(CONSTRAINT|PRIMARY KEY|UNIQUE|CHECK|FOREIGN KEY|EXCLUDE)/i.test(line)) continue; + + const col = line.match( + /^(\w+)\s+([\w\[\]"()\s,]+?)(?:\s+DEFAULT\s+|\s+NOT NULL|\s+NULL|\s+GENERATED|\s+REFERENCES\s|$)/ + ); + if (col) { + columns.push({ + name: col[1], + type: col[2].trim().split('(')[0].trim(), + pk: col[1] === 'id', + }); + } + } + + tables[name] = { columns, fks: [] }; + } + + // FKs via ALTER TABLE ... ADD CONSTRAINT ... FOREIGN KEY + const fkRe = /ALTER TABLE ONLY public\.(\w+)\s+ADD CONSTRAINT \S+ FOREIGN KEY \((\w+)\) REFERENCES public\.(\w+)\((\w+)\)/gm; + while ((m = fkRe.exec(content)) !== null) { + const [, fromTable, fromCol, toTable, toCol] = m; + if (tables[fromTable]) { + tables[fromTable].fks.push({ from_col: fromCol, to_table: toTable, to_col: toCol }); + } + } + + // Views + const viewRe = /CREATE(?:\s+OR REPLACE)?\s+VIEW\s+public\.(\S+)\s+AS/gm; + const views = []; + while ((m = viewRe.exec(content)) !== null) views.push(m[1]); + + return { tables, views }; +} + +// --------------------------------------------------------------------------- +// 3. Monta os domínios +// Tabelas novas que ainda não estão mapeadas vão para "Outros" +// --------------------------------------------------------------------------- +function buildDomains(tables) { + const mapped = new Set(Object.values(DOMAIN_TABLES).flat()); + const others = Object.keys(tables).filter(t => !mapped.has(t)); + + const domains = {}; + for (const [domain, list] of Object.entries(DOMAIN_TABLES)) { + const present = list.filter(t => tables[t]); + if (present.length > 0) domains[domain] = present; + } + if (others.length > 0) { + domains['Outros'] = others; + DOMAIN_COLORS['Outros'] = '#6b7280'; + } + + return domains; +} + +// --------------------------------------------------------------------------- +// 4. Gera o HTML final (standalone, sem dependências externas de JS) +// --------------------------------------------------------------------------- +function generateHTML(tables, views, domains, date, available) { + const totalFKs = Object.values(tables).reduce((a, t) => a + t.fks.length, 0); + const totalCols = Object.values(tables).reduce((a, t) => a + t.columns.length, 0); + const generated = new Date().toLocaleString('pt-BR'); + + // Serializa dados para embutir no HTML + const jsonData = JSON.stringify({ tables, views, domains }); + const jsonColors = JSON.stringify(DOMAIN_COLORS); + + return ` + + + + +AgenciaPsi DB · ${date} + + + +
+
AgênciaPsi DB
+ ${date} · ${generated} + +
+
${Object.keys(tables).length} tabelas
+
${totalFKs} FKs
+
${views.length} views
+
${totalCols} colunas
+
+
+
+ +
+
+ + +`; +} + +// --------------------------------------------------------------------------- +// 5. Execução +// --------------------------------------------------------------------------- +console.log('\n═══ AgenciaPsi — Dashboard Generator ═══\n'); + +const { schemaPath, date, available } = resolveSchema(); +console.log(` → Schema: ${schemaPath}`); +if (available.length > 1) console.log(` → Outros backups: ${available.slice(1).join(', ')}`); + +const content = fs.readFileSync(schemaPath, 'utf8'); +console.log(` → Lendo schema... (${(content.length / 1024).toFixed(0)} KB)`); + +const { tables, views } = parseSchema(content); +const domains = buildDomains(tables); +const totalFKs = Object.values(tables).reduce((a, t) => a + t.fks.length, 0); + +console.log(` → ${Object.keys(tables).length} tabelas · ${totalFKs} FKs · ${views.length} views`); + +// Avisa sobre tabelas novas não mapeadas +if (domains['Outros']) { + console.log(`\n ⚠ Tabelas novas sem domínio definido (aparecerão em "Outros"):`); + domains['Outros'].forEach(t => console.log(` - ${t}`)); + console.log(` → Edite DOMAIN_TABLES no script para mapeá-las.\n`); +} + +const html = generateHTML(tables, views, domains, date, available); +fs.writeFileSync(OUTPUT_FILE, html, 'utf8'); + +console.log(`\n✔ Gerado: ${OUTPUT_FILE}`); +console.log(` Tamanho: ${(fs.statSync(OUTPUT_FILE).size / 1024).toFixed(0)} KB`); +console.log(` Abra no browser: file://${OUTPUT_FILE}\n`); diff --git a/database-novo/migrations/002_setup_wizard1_fields.sql b/database-novo/migrations/002_setup_wizard1_fields.sql new file mode 100644 index 0000000..98b447f --- /dev/null +++ b/database-novo/migrations/002_setup_wizard1_fields.sql @@ -0,0 +1,57 @@ +-- ============================================================ +-- Migration 002 — SetupWizard1: campos Negócio e Atendimento +-- ============================================================ +-- Tabela: tenants (Step 2 — Negócio) +-- Tabela: agenda_configuracoes (Step 3 — Atendimento) +-- ============================================================ + +-- ---------------------------------------------------------- +-- tenants: dados do negócio +-- ---------------------------------------------------------- + +ALTER TABLE public.tenants + ADD COLUMN IF NOT EXISTS business_type text, + ADD COLUMN IF NOT EXISTS logo_url text, + ADD COLUMN IF NOT EXISTS address text, + ADD COLUMN IF NOT EXISTS phone text, + ADD COLUMN IF NOT EXISTS contact_email text, + ADD COLUMN IF NOT EXISTS site_url text, + ADD COLUMN IF NOT EXISTS social_instagram text; + +-- Valores aceitos: consultorio | clinica | instituto | grupo +ALTER TABLE public.tenants + ADD CONSTRAINT tenants_business_type_check + CHECK (business_type IS NULL OR business_type = ANY (ARRAY[ + 'consultorio'::text, + 'clinica'::text, + 'instituto'::text, + 'grupo'::text + ])); + +-- ---------------------------------------------------------- +-- agenda_configuracoes: modo de atendimento +-- ---------------------------------------------------------- + +ALTER TABLE public.agenda_configuracoes + ADD COLUMN IF NOT EXISTS atendimento_mode text DEFAULT 'particular'::text; + +ALTER TABLE public.agenda_configuracoes + ADD CONSTRAINT agenda_configuracoes_atendimento_mode_check + CHECK (atendimento_mode IS NULL OR atendimento_mode = ANY (ARRAY[ + 'particular'::text, + 'convenio'::text, + 'ambos'::text + ])); + +-- ---------------------------------------------------------- +-- Comments +-- ---------------------------------------------------------- + +COMMENT ON COLUMN public.tenants.business_type IS 'Tipo de negócio: consultorio, clinica, instituto, grupo'; +COMMENT ON COLUMN public.tenants.logo_url IS 'URL da logo do negócio (Storage bucket)'; +COMMENT ON COLUMN public.tenants.address IS 'Endereço do negócio (texto livre)'; +COMMENT ON COLUMN public.tenants.phone IS 'Telefone/WhatsApp do negócio'; +COMMENT ON COLUMN public.tenants.contact_email IS 'E-mail público de contato do negócio'; +COMMENT ON COLUMN public.tenants.site_url IS 'Site do negócio'; +COMMENT ON COLUMN public.tenants.social_instagram IS 'Instagram do negócio (sem @)'; +COMMENT ON COLUMN public.agenda_configuracoes.atendimento_mode IS 'Modo de atendimento: particular | convenio | ambos'; diff --git a/database-novo/migrations/003_tenants_address_fields.sql b/database-novo/migrations/003_tenants_address_fields.sql new file mode 100644 index 0000000..76ded63 --- /dev/null +++ b/database-novo/migrations/003_tenants_address_fields.sql @@ -0,0 +1,33 @@ +-- ============================================================ +-- Migration 003 — Tenants: campos de endereço detalhado +-- ============================================================ +-- Substitui o campo address (texto livre) por campos estruturados +-- preenchidos via consulta de CEP (ViaCEP) +-- ============================================================ + +ALTER TABLE public.tenants + ADD COLUMN IF NOT EXISTS cep text, + ADD COLUMN IF NOT EXISTS logradouro text, + ADD COLUMN IF NOT EXISTS numero text, + ADD COLUMN IF NOT EXISTS complemento text, + ADD COLUMN IF NOT EXISTS bairro text, + ADD COLUMN IF NOT EXISTS cidade text, + ADD COLUMN IF NOT EXISTS estado text; + +-- Migra dados existentes do campo address para logradouro +UPDATE public.tenants + SET logradouro = address + WHERE address IS NOT NULL + AND logradouro IS NULL; + +-- ---------------------------------------------------------- +-- Comments +-- ---------------------------------------------------------- + +COMMENT ON COLUMN public.tenants.cep IS 'CEP do endereço do negócio'; +COMMENT ON COLUMN public.tenants.logradouro IS 'Logradouro (rua, avenida, etc.)'; +COMMENT ON COLUMN public.tenants.numero IS 'Número do endereço'; +COMMENT ON COLUMN public.tenants.complemento IS 'Complemento (sala, andar, etc.)'; +COMMENT ON COLUMN public.tenants.bairro IS 'Bairro'; +COMMENT ON COLUMN public.tenants.cidade IS 'Cidade'; +COMMENT ON COLUMN public.tenants.estado IS 'UF (2 letras)'; diff --git a/database-novo/migrations/20260328000001_create_medicos.sql b/database-novo/migrations/20260328000001_create_medicos.sql new file mode 100644 index 0000000..99d9c78 --- /dev/null +++ b/database-novo/migrations/20260328000001_create_medicos.sql @@ -0,0 +1,147 @@ +-- ========================================================================== +-- Agência PSI — Migração: tabela `medicos` +-- ========================================================================== +-- Criado por: Leonardo Nohama +-- Data: 2026 · São Carlos/SP — Brasil +-- +-- Propósito: +-- Armazena médicos e profissionais de referência (psiquiatras, neurologistas, +-- clínicos gerais, etc.) que encaminham pacientes ou fazem parte da rede de +-- suporte clínico do terapeuta. +-- +-- Usado em: +-- - PatientsCadastroPage: campo "Encaminhado por" (FK medico_id) +-- - CadastroRapidoMedico.vue: cadastro rápido dentro do formulário +-- - MedicosCadastroPage.vue: página completa de gestão de médicos +-- +-- Relacionamentos: +-- medicos.owner_id → auth.users(id) +-- medicos.tenant_id → tenants(id) +-- patients.medico_encaminhador_id → medicos(id) (opcional, ver abaixo) +-- +-- RLS: owner_id = auth.uid() — cada profissional vê apenas seus médicos. +-- ========================================================================== + +-- -------------------------------------------------------------------------- +-- 1. Tabela principal +-- -------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS public.medicos ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + + -- Contexto de acesso + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + + -- Identidade profissional + nome text NOT NULL, + crm text, -- Ex: "123456/SP" + especialidade text, -- Ex: "Psiquiatria" + + -- Contatos — telefone_pessoal é sensível (exibido com ícone de olho) + telefone_profissional text, -- Consultório / clínica + telefone_pessoal text, -- WhatsApp / pessoal + email text, + + -- Local de atuação + clinica text, -- Nome da clínica/hospital + cidade text, + estado text DEFAULT 'SP', + + -- Notas internas do terapeuta + observacoes text, + + -- Controle + ativo boolean DEFAULT true NOT NULL, + created_at timestamptz DEFAULT now(), + updated_at timestamptz DEFAULT now(), + + CONSTRAINT medicos_pkey PRIMARY KEY (id), + + -- CRM único por owner (mesmo terapeuta não cadastra o mesmo CRM duas vezes) + CONSTRAINT medicos_crm_owner_unique UNIQUE NULLS NOT DISTINCT (owner_id, crm) +); + +-- -------------------------------------------------------------------------- +-- 2. Índices de performance +-- -------------------------------------------------------------------------- +CREATE INDEX IF NOT EXISTS medicos_owner_idx + ON public.medicos USING btree (owner_id); + +CREATE INDEX IF NOT EXISTS medicos_tenant_idx + ON public.medicos USING btree (tenant_id); + +CREATE INDEX IF NOT EXISTS medicos_nome_idx + ON public.medicos USING btree (nome); + +CREATE INDEX IF NOT EXISTS medicos_especialidade_idx + ON public.medicos USING btree (especialidade); + +-- Busca textual por nome e especialidade +CREATE INDEX IF NOT EXISTS medicos_nome_trgm_idx + ON public.medicos USING gin (nome gin_trgm_ops); + +-- -------------------------------------------------------------------------- +-- 3. Trigger de updated_at +-- -------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION public.set_medicos_updated_at() +RETURNS trigger LANGUAGE plpgsql AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + +CREATE TRIGGER trg_medicos_updated_at + BEFORE UPDATE ON public.medicos + FOR EACH ROW + EXECUTE FUNCTION public.set_medicos_updated_at(); + +-- -------------------------------------------------------------------------- +-- 4. Row Level Security +-- -------------------------------------------------------------------------- +ALTER TABLE public.medicos ENABLE ROW LEVEL SECURITY; + +-- Owner tem acesso total aos seus próprios médicos +CREATE POLICY "medicos: owner full access" + ON public.medicos + USING (owner_id = auth.uid()) + WITH CHECK (owner_id = auth.uid()); + +-- -------------------------------------------------------------------------- +-- 5. Comentários de documentação +-- -------------------------------------------------------------------------- +COMMENT ON TABLE public.medicos IS 'Médicos e profissionais de referência cadastrados pelo terapeuta.'; +COMMENT ON COLUMN public.medicos.owner_id IS 'Terapeuta dono do cadastro (auth.uid()).'; +COMMENT ON COLUMN public.medicos.tenant_id IS 'Tenant do terapeuta.'; +COMMENT ON COLUMN public.medicos.nome IS 'Nome completo do médico/profissional.'; +COMMENT ON COLUMN public.medicos.crm IS 'CRM com UF. Ex: 123456/SP. Único por owner_id.'; +COMMENT ON COLUMN public.medicos.especialidade IS 'Especialidade médica. Ex: Psiquiatria, Neurologia.'; +COMMENT ON COLUMN public.medicos.telefone_profissional IS 'Telefone do consultório ou clínica.'; +COMMENT ON COLUMN public.medicos.telefone_pessoal IS 'Telefone pessoal / WhatsApp. Campo sensível.'; +COMMENT ON COLUMN public.medicos.email IS 'E-mail profissional.'; +COMMENT ON COLUMN public.medicos.clinica IS 'Nome da clínica ou hospital onde atua.'; +COMMENT ON COLUMN public.medicos.cidade IS 'Cidade de atuação.'; +COMMENT ON COLUMN public.medicos.estado IS 'UF de atuação. Default SP.'; +COMMENT ON COLUMN public.medicos.observacoes IS 'Notas internas do terapeuta sobre o médico.'; +COMMENT ON COLUMN public.medicos.ativo IS 'Soft delete: false oculta da listagem.'; + +-- -------------------------------------------------------------------------- +-- 6. Coluna FK opcional em patients +-- (Conecta "Encaminhado por" ao cadastro de médico) +-- Execute apenas se quiser a FK estruturada; caso contrário, +-- o campo encaminhado_por (text) no PatientsCadastroPage já funciona. +-- -------------------------------------------------------------------------- + +-- ALTER TABLE public.patients +-- ADD COLUMN IF NOT EXISTS medico_encaminhador_id uuid +-- REFERENCES public.medicos(id) ON DELETE SET NULL; + +-- CREATE INDEX IF NOT EXISTS patients_medico_encaminhador_idx +-- ON public.patients USING btree (medico_encaminhador_id); + +-- COMMENT ON COLUMN public.patients.medico_encaminhador_id +-- IS 'FK para medicos.id — quem encaminhou o paciente.'; + +-- ========================================================================== +-- FIM DA MIGRAÇÃO +-- ========================================================================== diff --git a/database-novo/migrations/20260328000002_patients_new_columns.sql b/database-novo/migrations/20260328000002_patients_new_columns.sql new file mode 100644 index 0000000..997085e --- /dev/null +++ b/database-novo/migrations/20260328000002_patients_new_columns.sql @@ -0,0 +1,119 @@ +-- ========================================================================== +-- Agência PSI — Migração: novos campos em `patients` +-- ========================================================================== +-- Arquivo: supabase/migrations/20260328000002_patients_new_columns.sql +-- Criado por: Leonardo Nohama · 2026 · São Carlos/SP +-- +-- Adiciona as colunas identificadas na engenharia reversa da tela de detalhe +-- (PatientsDetailPage) que ainda não existiam na tabela `patients`. +-- +-- Também ajusta os CHECK constraints de `status` e `patient_scope` para +-- aceitar os valores usados no novo formulário de cadastro. +-- ========================================================================== + +-- -------------------------------------------------------------------------- +-- 1. Colunas novas +-- -------------------------------------------------------------------------- + +-- Identidade +ALTER TABLE public.patients + ADD COLUMN IF NOT EXISTS pronomes text, + ADD COLUMN IF NOT EXISTS nome_social text, + ADD COLUMN IF NOT EXISTS etnia text; + +-- Contato +ALTER TABLE public.patients + ADD COLUMN IF NOT EXISTS canal_preferido text, + ADD COLUMN IF NOT EXISTS horario_contato text; + +-- Clínico / convênio +-- convenio: nome de exibição (badge azul no header) +-- convenio_id: FK para insurance_plans (opcional — permite vincular ao cadastro) +ALTER TABLE public.patients + ADD COLUMN IF NOT EXISTS convenio text, + ADD COLUMN IF NOT EXISTS convenio_id uuid REFERENCES public.insurance_plans(id) ON DELETE SET NULL; + +-- Origem +ALTER TABLE public.patients + ADD COLUMN IF NOT EXISTS metodo_pagamento_preferido text, + ADD COLUMN IF NOT EXISTS motivo_saida text; + +-- -------------------------------------------------------------------------- +-- 2. Ajuste do CHECK constraint de `status` +-- Valores originais: Ativo | Inativo | Alta | Encaminhado | Arquivado +-- Valores novos: + Em espera +-- -------------------------------------------------------------------------- +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_status_check; + +ALTER TABLE public.patients + ADD CONSTRAINT patients_status_check CHECK ( + status = ANY (ARRAY[ + 'Ativo'::text, + 'Em espera'::text, + 'Inativo'::text, + 'Alta'::text, + 'Encaminhado'::text, + 'Arquivado'::text + ]) + ); + +-- -------------------------------------------------------------------------- +-- 3. Ajuste do CHECK constraint de `patient_scope` +-- Valores originais: clinic | therapist (valores técnicos internos) +-- Valores novos: + Clínica | Particular | Online | Híbrido +-- Estratégia: remover o constraint restritivo e deixar livre (text), +-- pois o controle já é feito no frontend via Select com opções fixas. +-- -------------------------------------------------------------------------- +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_patient_scope_check; + +-- Também remove a constraint de consistência que dependia do scope antigo +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_therapist_scope_consistency; + +-- -------------------------------------------------------------------------- +-- 4. Índices de performance +-- -------------------------------------------------------------------------- +CREATE INDEX IF NOT EXISTS patients_convenio_id_idx + ON public.patients USING btree (convenio_id); + +CREATE INDEX IF NOT EXISTS patients_pronomes_idx + ON public.patients USING btree (pronomes); + +CREATE INDEX IF NOT EXISTS patients_etnia_idx + ON public.patients USING btree (etnia); + +-- -------------------------------------------------------------------------- +-- 5. Comentários +-- -------------------------------------------------------------------------- +COMMENT ON COLUMN public.patients.pronomes + IS 'Pronomes de tratamento. Ex: ela/dela, ele/dele. Exibido no header do perfil.'; + +COMMENT ON COLUMN public.patients.nome_social + IS 'Nome social / como prefere ser chamado(a) no atendimento.'; + +COMMENT ON COLUMN public.patients.etnia + IS 'Etnia / raça autodeclarada. Exibida no card "Dados pessoais".'; + +COMMENT ON COLUMN public.patients.canal_preferido + IS 'Canal preferido de contato. Ex: WhatsApp, Telefone, E-mail.'; + +COMMENT ON COLUMN public.patients.horario_contato + IS 'Horário preferido para contato. Ex: 08h–18h.'; + +COMMENT ON COLUMN public.patients.convenio + IS 'Nome do convênio para exibição (badge azul no header). Derivado de convenio_id.'; + +COMMENT ON COLUMN public.patients.convenio_id + IS 'FK para insurance_plans.id. Vincula o paciente ao convênio cadastrado.'; + +COMMENT ON COLUMN public.patients.metodo_pagamento_preferido + IS 'Método de pagamento preferido. Ex: PIX, Cartão crédito. Exibido no card Origem.'; + +COMMENT ON COLUMN public.patients.motivo_saida + IS 'Motivo de encerramento do acompanhamento. Exibido no card Origem quando preenchido.'; + +-- ========================================================================== +-- FIM DA MIGRAÇÃO +-- ========================================================================== diff --git a/database-novo/migrations/20260328000003_patients_drop_check_constraints.sql b/database-novo/migrations/20260328000003_patients_drop_check_constraints.sql new file mode 100644 index 0000000..975ba69 --- /dev/null +++ b/database-novo/migrations/20260328000003_patients_drop_check_constraints.sql @@ -0,0 +1,70 @@ +-- ========================================================================== +-- Agência PSI — Migração: remove check constraints dos novos campos +-- ========================================================================== +-- Arquivo: supabase/migrations/20260328000003_patients_drop_check_constraints.sql +-- Criado por: Leonardo Nohama · 2026 · São Carlos/SP +-- +-- O banco tinha CHECK constraints nos novos campos que foram adicionados +-- pela migration anterior (ou que já existiam no schema ao vivo). +-- O frontend já controla os valores via Select com opções fixas, +-- então os constraints são desnecessários e serão removidos. +-- ========================================================================== + +-- canal_preferido +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_canal_preferido_check; + +-- horario_contato +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_horario_contato_check; + +-- pronomes +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_pronomes_check; + +-- nome_social +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_nome_social_check; + +-- etnia +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_etnia_check; + +-- convenio +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_convenio_check; + +-- metodo_pagamento_preferido +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_metodo_pagamento_preferido_check; + +-- motivo_saida +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_motivo_saida_check; + +-- status (já ajustado na migration anterior, mas garante) +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_status_check; + +ALTER TABLE public.patients + ADD CONSTRAINT patients_status_check CHECK ( + status = ANY (ARRAY[ + 'Ativo'::text, + 'Em espera'::text, + 'Inativo'::text, + 'Alta'::text, + 'Encaminhado'::text, + 'Arquivado'::text + ]) + ); + +-- patient_scope (já ajustado na migration anterior, mas garante) +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_patient_scope_check; + +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_therapist_scope_consistency; + +-- ========================================================================== +-- FIM DA MIGRAÇÃO +-- ========================================================================== diff --git a/database-novo/migrations/20260328000004_create_patient_support_contacts.sql b/database-novo/migrations/20260328000004_create_patient_support_contacts.sql new file mode 100644 index 0000000..71aa503 --- /dev/null +++ b/database-novo/migrations/20260328000004_create_patient_support_contacts.sql @@ -0,0 +1,56 @@ +-- ========================================================================== +-- Agência PSI — Migração: tabela `patient_support_contacts` +-- ========================================================================== +-- Arquivo: supabase/migrations/20260328000004_create_patient_support_contacts.sql +-- Criado por: Leonardo Nohama · 2026 · São Carlos/SP +-- +-- Contatos da rede de suporte do paciente. +-- Alimenta o card "Contatos & rede de suporte" na tela de detalhe. +-- is_primario = true → badge vermelho "emergência" no perfil. +-- ========================================================================== + +CREATE TABLE IF NOT EXISTS public.patient_support_contacts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + + patient_id uuid NOT NULL REFERENCES public.patients(id) ON DELETE CASCADE, + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + + nome text, + relacao text, -- Ex: mãe, psiquiatra, cônjuge + tipo text, -- emergencia | familiar | profissional_saude | amigo | outro + telefone text, + email text, + is_primario boolean DEFAULT false NOT NULL, + + created_at timestamptz DEFAULT now(), + updated_at timestamptz DEFAULT now(), + + CONSTRAINT patient_support_contacts_pkey PRIMARY KEY (id) +); + +-- Índices +CREATE INDEX IF NOT EXISTS psc_patient_idx ON public.patient_support_contacts USING btree (patient_id); +CREATE INDEX IF NOT EXISTS psc_owner_idx ON public.patient_support_contacts USING btree (owner_id); + +-- Trigger updated_at +CREATE TRIGGER trg_psc_updated_at + BEFORE UPDATE ON public.patient_support_contacts + FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + +-- RLS +ALTER TABLE public.patient_support_contacts ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "psc: owner full access" + ON public.patient_support_contacts + USING (owner_id = auth.uid()) + WITH CHECK (owner_id = auth.uid()); + +-- Comentários +COMMENT ON TABLE public.patient_support_contacts IS 'Rede de suporte do paciente. Exibida no card "Contatos & rede de suporte" do perfil.'; +COMMENT ON COLUMN public.patient_support_contacts.is_primario IS 'true = badge vermelho "emergência" no perfil do paciente.'; +COMMENT ON COLUMN public.patient_support_contacts.tipo IS 'emergencia | familiar | profissional_saude | amigo | outro'; + +-- ========================================================================== +-- FIM DA MIGRAÇÃO +-- ========================================================================== diff --git a/database-novo/migrations/20260329000001_create_documents_tables.sql b/database-novo/migrations/20260329000001_create_documents_tables.sql new file mode 100644 index 0000000..307f0e5 --- /dev/null +++ b/database-novo/migrations/20260329000001_create_documents_tables.sql @@ -0,0 +1,454 @@ +-- ========================================================================== +-- Agencia PSI — Migracao: tabelas de Documentos & Arquivos +-- ========================================================================== +-- Criado por: Leonardo Nohama +-- Data: 2026-03-29 · Sao Carlos/SP — Brasil +-- +-- Proposito: +-- Modulo completo de documentos do paciente. +-- Tabelas: documents, document_access_logs, document_signatures, +-- document_share_links. +-- +-- Relacionamentos: +-- documents.patient_id → patients(id) +-- documents.owner_id → auth.users(id) +-- documents.tenant_id → tenants(id) +-- documents.agenda_evento_id → agenda_eventos(id) (opcional) +-- document_access_logs.documento_id → documents(id) +-- document_signatures.documento_id → documents(id) +-- document_share_links.documento_id → documents(id) +-- +-- RLS: owner_id = auth.uid() para documents, signatures e share_links. +-- access_logs: somente INSERT (imutavel) + SELECT por tenant. +-- ========================================================================== + + +-- -------------------------------------------------------------------------- +-- 1. Tabela principal: documents +-- -------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS public.documents ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + + -- Contexto de acesso + owner_id uuid NOT NULL, + tenant_id uuid NOT NULL, + + -- Vinculo com paciente + patient_id uuid NOT NULL REFERENCES public.patients(id) ON DELETE CASCADE, + + -- Arquivo no Storage + bucket_path text NOT NULL, + storage_bucket text NOT NULL DEFAULT 'documents', + nome_original text NOT NULL, + mime_type text, + tamanho_bytes bigint, + + -- Classificacao + tipo_documento text NOT NULL DEFAULT 'outro', + -- laudo | receita | exame | termo_assinado | relatorio_externo + -- identidade | convenio | declaracao | atestado | recibo | outro + categoria text, + descricao text, + tags text[] DEFAULT '{}', + + -- Vinculo opcional com sessao/nota + agenda_evento_id uuid REFERENCES public.agenda_eventos(id) ON DELETE SET NULL, + session_note_id uuid, + + -- Visibilidade & controle de acesso + visibilidade text NOT NULL DEFAULT 'privado', + -- privado | compartilhado_supervisor | compartilhado_portal + compartilhado_portal boolean DEFAULT false NOT NULL, + compartilhado_supervisor boolean DEFAULT false NOT NULL, + compartilhado_em timestamptz, + expira_compartilhamento timestamptz, + + -- Upload pelo paciente (portal) + enviado_pelo_paciente boolean DEFAULT false NOT NULL, + status_revisao text DEFAULT 'aprovado', + -- pendente | aprovado | rejeitado + revisado_por uuid, + revisado_em timestamptz, + + -- Quem fez upload + uploaded_by uuid NOT NULL, + uploaded_at timestamptz DEFAULT now() NOT NULL, + + -- Soft delete com retencao (LGPD / CFP) + deleted_at timestamptz, + deleted_by uuid, + retencao_ate timestamptz, + + -- Controle + created_at timestamptz DEFAULT now(), + updated_at timestamptz DEFAULT now(), + + CONSTRAINT documents_pkey PRIMARY KEY (id), + + -- Validacoes + CONSTRAINT documents_tipo_check CHECK ( + tipo_documento = ANY (ARRAY[ + 'laudo', 'receita', 'exame', 'termo_assinado', 'relatorio_externo', + 'identidade', 'convenio', 'declaracao', 'atestado', 'recibo', 'outro' + ]) + ), + CONSTRAINT documents_visibilidade_check CHECK ( + visibilidade = ANY (ARRAY['privado', 'compartilhado_supervisor', 'compartilhado_portal']) + ), + CONSTRAINT documents_status_revisao_check CHECK ( + status_revisao = ANY (ARRAY['pendente', 'aprovado', 'rejeitado']) + ) +); + + +-- -------------------------------------------------------------------------- +-- 2. Indices — documents +-- -------------------------------------------------------------------------- +CREATE INDEX IF NOT EXISTS docs_patient_idx + ON public.documents USING btree (patient_id); + +CREATE INDEX IF NOT EXISTS docs_owner_idx + ON public.documents USING btree (owner_id); + +CREATE INDEX IF NOT EXISTS docs_tenant_idx + ON public.documents USING btree (tenant_id); + +CREATE INDEX IF NOT EXISTS docs_tipo_idx + ON public.documents USING btree (patient_id, tipo_documento); + +CREATE INDEX IF NOT EXISTS docs_tags_idx + ON public.documents USING gin (tags); + +CREATE INDEX IF NOT EXISTS docs_uploaded_at_idx + ON public.documents USING btree (patient_id, uploaded_at DESC); + +-- Excluir soft-deleted da listagem padrao +CREATE INDEX IF NOT EXISTS docs_active_idx + ON public.documents USING btree (patient_id, uploaded_at DESC) + WHERE deleted_at IS NULL; + +-- Busca textual no nome do arquivo +CREATE INDEX IF NOT EXISTS docs_nome_trgm_idx + ON public.documents USING gin (nome_original gin_trgm_ops); + + +-- -------------------------------------------------------------------------- +-- 3. Trigger updated_at — documents +-- -------------------------------------------------------------------------- +CREATE TRIGGER trg_documents_updated_at + BEFORE UPDATE ON public.documents + FOR EACH ROW + EXECUTE FUNCTION public.set_updated_at(); + + +-- -------------------------------------------------------------------------- +-- 4. Trigger: registrar na patient_timeline ao adicionar documento +-- -------------------------------------------------------------------------- +CREATE OR REPLACE FUNCTION public.fn_documents_timeline_insert() +RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER AS $$ +BEGIN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, evento_tipo, + titulo, descricao, icone_cor, + link_ref_tipo, link_ref_id, + gerado_por, ocorrido_em + ) VALUES ( + NEW.patient_id, + NEW.tenant_id, + 'documento_adicionado', + 'Documento adicionado: ' || COALESCE(NEW.nome_original, 'arquivo'), + 'Tipo: ' || COALESCE(NEW.tipo_documento, 'outro'), + 'blue', + 'documento', + NEW.id, + NEW.uploaded_by, + NEW.uploaded_at + ); + RETURN NEW; +END; +$$; + +CREATE TRIGGER trg_documents_timeline_insert + AFTER INSERT ON public.documents + FOR EACH ROW + EXECUTE FUNCTION public.fn_documents_timeline_insert(); + + +-- -------------------------------------------------------------------------- +-- 5. RLS — documents +-- -------------------------------------------------------------------------- +ALTER TABLE public.documents ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "documents: owner full access" + ON public.documents + USING (owner_id = auth.uid()) + WITH CHECK (owner_id = auth.uid()); + + +-- -------------------------------------------------------------------------- +-- 6. Comentarios — documents +-- -------------------------------------------------------------------------- +COMMENT ON TABLE public.documents IS 'Documentos e arquivos vinculados a pacientes. Armazenados no Supabase Storage.'; +COMMENT ON COLUMN public.documents.owner_id IS 'Terapeuta dono do documento (auth.uid()).'; +COMMENT ON COLUMN public.documents.tenant_id IS 'Tenant do terapeuta.'; +COMMENT ON COLUMN public.documents.patient_id IS 'Paciente ao qual o documento pertence.'; +COMMENT ON COLUMN public.documents.bucket_path IS 'Caminho do arquivo no Supabase Storage bucket.'; +COMMENT ON COLUMN public.documents.storage_bucket IS 'Nome do bucket no Storage. Default: documents.'; +COMMENT ON COLUMN public.documents.nome_original IS 'Nome original do arquivo enviado.'; +COMMENT ON COLUMN public.documents.mime_type IS 'MIME type do arquivo. Ex: application/pdf, image/jpeg.'; +COMMENT ON COLUMN public.documents.tamanho_bytes IS 'Tamanho do arquivo em bytes.'; +COMMENT ON COLUMN public.documents.tipo_documento IS 'Tipo: laudo|receita|exame|termo_assinado|relatorio_externo|identidade|convenio|declaracao|atestado|recibo|outro.'; +COMMENT ON COLUMN public.documents.categoria IS 'Categoria livre para organizacao adicional.'; +COMMENT ON COLUMN public.documents.tags IS 'Tags livres para busca e filtro. Array de text.'; +COMMENT ON COLUMN public.documents.visibilidade IS 'privado|compartilhado_supervisor|compartilhado_portal.'; +COMMENT ON COLUMN public.documents.compartilhado_portal IS 'true = visivel para o paciente no portal.'; +COMMENT ON COLUMN public.documents.compartilhado_supervisor IS 'true = visivel para o supervisor.'; +COMMENT ON COLUMN public.documents.enviado_pelo_paciente IS 'true = upload feito pelo paciente via portal.'; +COMMENT ON COLUMN public.documents.status_revisao IS 'pendente|aprovado|rejeitado — para uploads do paciente.'; +COMMENT ON COLUMN public.documents.deleted_at IS 'Soft delete: data da exclusao. NULL = ativo.'; +COMMENT ON COLUMN public.documents.retencao_ate IS 'LGPD/CFP: arquivo retido ate esta data mesmo apos soft delete.'; + + +-- ========================================================================== +-- 7. Tabela: document_access_logs (imutavel — auditoria) +-- ========================================================================== +CREATE TABLE IF NOT EXISTS public.document_access_logs ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + + documento_id uuid NOT NULL REFERENCES public.documents(id) ON DELETE CASCADE, + tenant_id uuid NOT NULL, + + -- Acao realizada + acao text NOT NULL, + -- visualizou | baixou | imprimiu | compartilhou | assinou + user_id uuid, + ip inet, + user_agent text, + + acessado_em timestamptz DEFAULT now() NOT NULL, + + CONSTRAINT document_access_logs_pkey PRIMARY KEY (id), + + CONSTRAINT dal_acao_check CHECK ( + acao = ANY (ARRAY['visualizou', 'baixou', 'imprimiu', 'compartilhou', 'assinou']) + ) +); + +-- Indices +CREATE INDEX IF NOT EXISTS dal_documento_idx + ON public.document_access_logs USING btree (documento_id, acessado_em DESC); + +CREATE INDEX IF NOT EXISTS dal_tenant_idx + ON public.document_access_logs USING btree (tenant_id, acessado_em DESC); + +CREATE INDEX IF NOT EXISTS dal_user_idx + ON public.document_access_logs USING btree (user_id, acessado_em DESC); + +-- RLS — somente INSERT (imutavel) + SELECT +ALTER TABLE public.document_access_logs ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "dal: tenant members can insert" + ON public.document_access_logs + FOR INSERT + WITH CHECK (true); + +CREATE POLICY "dal: tenant members can select" + ON public.document_access_logs + FOR SELECT + USING (tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + )); + +-- Comentarios +COMMENT ON TABLE public.document_access_logs IS 'Log imutavel de acessos a documentos. Conformidade CFP e LGPD. Sem UPDATE/DELETE.'; +COMMENT ON COLUMN public.document_access_logs.acao IS 'visualizou|baixou|imprimiu|compartilhou|assinou.'; + + +-- ========================================================================== +-- 8. Tabela: document_signatures (assinatura eletronica) +-- ========================================================================== +CREATE TABLE IF NOT EXISTS public.document_signatures ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + + documento_id uuid NOT NULL REFERENCES public.documents(id) ON DELETE CASCADE, + tenant_id uuid NOT NULL, + + -- Signatario + signatario_tipo text NOT NULL, + -- paciente | responsavel_legal | terapeuta + signatario_id uuid, + signatario_nome text, + signatario_email text, + + -- Ordem e status + ordem smallint DEFAULT 1 NOT NULL, + status text NOT NULL DEFAULT 'pendente', + -- pendente | enviado | assinado | recusado | expirado + + -- Dados da assinatura (preenchidos ao assinar) + ip inet, + user_agent text, + assinado_em timestamptz, + hash_documento text, + + -- Controle + criado_em timestamptz DEFAULT now(), + atualizado_em timestamptz DEFAULT now(), + + CONSTRAINT document_signatures_pkey PRIMARY KEY (id), + + CONSTRAINT ds_signatario_tipo_check CHECK ( + signatario_tipo = ANY (ARRAY['paciente', 'responsavel_legal', 'terapeuta']) + ), + CONSTRAINT ds_status_check CHECK ( + status = ANY (ARRAY['pendente', 'enviado', 'assinado', 'recusado', 'expirado']) + ) +); + +-- Indices +CREATE INDEX IF NOT EXISTS ds_documento_idx + ON public.document_signatures USING btree (documento_id, ordem); + +CREATE INDEX IF NOT EXISTS ds_tenant_idx + ON public.document_signatures USING btree (tenant_id); + +CREATE INDEX IF NOT EXISTS ds_status_idx + ON public.document_signatures USING btree (documento_id, status); + +-- Trigger updated_at +CREATE TRIGGER trg_ds_updated_at + BEFORE UPDATE ON public.document_signatures + FOR EACH ROW + EXECUTE FUNCTION public.set_updated_at(); + +-- Trigger: ao assinar, registrar na patient_timeline +CREATE OR REPLACE FUNCTION public.fn_document_signature_timeline() +RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER AS $$ +DECLARE + v_patient_id uuid; + v_tenant_id uuid; + v_doc_nome text; +BEGIN + IF NEW.status = 'assinado' AND (OLD.status IS NULL OR OLD.status <> 'assinado') THEN + SELECT d.patient_id, d.tenant_id, d.nome_original + INTO v_patient_id, v_tenant_id, v_doc_nome + FROM public.documents d + WHERE d.id = NEW.documento_id; + + IF v_patient_id IS NOT NULL THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, evento_tipo, + titulo, descricao, icone_cor, + link_ref_tipo, link_ref_id, + gerado_por, ocorrido_em + ) VALUES ( + v_patient_id, + v_tenant_id, + 'documento_assinado', + 'Documento assinado: ' || COALESCE(v_doc_nome, 'documento'), + 'Assinado por ' || COALESCE(NEW.signatario_nome, NEW.signatario_tipo), + 'green', + 'documento', + NEW.documento_id, + NEW.signatario_id, + NEW.assinado_em + ); + END IF; + END IF; + RETURN NEW; +END; +$$; + +CREATE TRIGGER trg_ds_timeline + AFTER UPDATE ON public.document_signatures + FOR EACH ROW + EXECUTE FUNCTION public.fn_document_signature_timeline(); + +-- RLS +ALTER TABLE public.document_signatures ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "ds: tenant members access" + ON public.document_signatures + USING (tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + )) + WITH CHECK (tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + )); + +-- Comentarios +COMMENT ON TABLE public.document_signatures IS 'Assinaturas eletronicas de documentos. Cada signatario tem seu registro.'; +COMMENT ON COLUMN public.document_signatures.signatario_tipo IS 'paciente|responsavel_legal|terapeuta.'; +COMMENT ON COLUMN public.document_signatures.status IS 'pendente|enviado|assinado|recusado|expirado.'; +COMMENT ON COLUMN public.document_signatures.hash_documento IS 'Hash SHA-256 do documento no momento da assinatura. Garante integridade.'; +COMMENT ON COLUMN public.document_signatures.ip IS 'IP do signatario no momento da assinatura.'; + + +-- ========================================================================== +-- 9. Tabela: document_share_links (links temporarios) +-- ========================================================================== +CREATE TABLE IF NOT EXISTS public.document_share_links ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + + documento_id uuid NOT NULL REFERENCES public.documents(id) ON DELETE CASCADE, + tenant_id uuid NOT NULL, + + -- Token unico para o link + token text NOT NULL DEFAULT encode(gen_random_bytes(32), 'hex'), + + -- Limites + expira_em timestamptz NOT NULL, + usos_max smallint DEFAULT 5 NOT NULL, + usos smallint DEFAULT 0 NOT NULL, + + -- Quem criou + criado_por uuid NOT NULL, + criado_em timestamptz DEFAULT now(), + + -- Controle + ativo boolean DEFAULT true NOT NULL, + + CONSTRAINT document_share_links_pkey PRIMARY KEY (id), + CONSTRAINT dsl_token_unique UNIQUE (token) +); + +-- Indices +CREATE INDEX IF NOT EXISTS dsl_documento_idx + ON public.document_share_links USING btree (documento_id); + +CREATE INDEX IF NOT EXISTS dsl_token_idx + ON public.document_share_links USING btree (token) + WHERE ativo = true; + +CREATE INDEX IF NOT EXISTS dsl_expira_idx + ON public.document_share_links USING btree (expira_em) + WHERE ativo = true; + +-- RLS +ALTER TABLE public.document_share_links ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "dsl: creator full access" + ON public.document_share_links + USING (criado_por = auth.uid()) + WITH CHECK (criado_por = auth.uid()); + +-- Politica publica de leitura por token (para acesso externo sem login) +CREATE POLICY "dsl: public read by token" + ON public.document_share_links + FOR SELECT + USING (ativo = true AND expira_em > now() AND usos < usos_max); + +-- Comentarios +COMMENT ON TABLE public.document_share_links IS 'Links temporarios assinados para compartilhar documento com profissional externo.'; +COMMENT ON COLUMN public.document_share_links.token IS 'Token unico gerado automaticamente (32 bytes hex).'; +COMMENT ON COLUMN public.document_share_links.expira_em IS 'Data/hora de expiracao do link.'; +COMMENT ON COLUMN public.document_share_links.usos_max IS 'Numero maximo de acessos permitidos.'; +COMMENT ON COLUMN public.document_share_links.usos IS 'Numero de vezes que o link ja foi acessado.'; + + +-- ========================================================================== +-- FIM DA MIGRACAO 005 +-- ========================================================================== diff --git a/database-novo/migrations/20260329000002_create_document_templates.sql b/database-novo/migrations/20260329000002_create_document_templates.sql new file mode 100644 index 0000000..c4403e0 --- /dev/null +++ b/database-novo/migrations/20260329000002_create_document_templates.sql @@ -0,0 +1,260 @@ +-- ========================================================================== +-- Agencia PSI — Migracao: tabelas de Templates de Documentos +-- ========================================================================== +-- Criado por: Leonardo Nohama +-- Data: 2026-03-29 · Sao Carlos/SP — Brasil +-- +-- Proposito: +-- Templates de documentos (declaracao, atestado, recibo, relatorio etc.) +-- e registro de cada documento gerado (instancia PDF). +-- +-- Tabelas: document_templates, document_generated. +-- +-- Relacionamentos: +-- document_templates.tenant_id → tenants(id) +-- document_templates.owner_id → auth.users(id) +-- document_generated.template_id → document_templates(id) +-- document_generated.patient_id → patients(id) +-- document_generated.tenant_id → tenants(id) +-- +-- Templates globais: is_global = true, tenant_id = NULL. +-- Templates do tenant: is_global = false, tenant_id preenchido. +-- ========================================================================== + + +-- -------------------------------------------------------------------------- +-- 1. Tabela: document_templates +-- -------------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS public.document_templates ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + + -- Contexto + tenant_id uuid, + owner_id uuid, + + -- Identificacao + nome_template text NOT NULL, + tipo text NOT NULL DEFAULT 'outro', + -- declaracao_comparecimento | atestado_psicologico + -- relatorio_acompanhamento | recibo_pagamento + -- termo_consentimento | encaminhamento | outro + descricao text, + + -- Corpo do template + corpo_html text NOT NULL DEFAULT '', + cabecalho_html text, + rodape_html text, + + -- Variaveis que o template utiliza + variaveis text[] DEFAULT '{}', + -- Ex: {paciente_nome, paciente_cpf, data_sessao, terapeuta_nome, ...} + + -- Personalizacao visual + logo_url text, + + -- Escopo + is_global boolean DEFAULT false NOT NULL, + -- true = template padrao do sistema (visivel para todos) + -- false = template criado pelo tenant/terapeuta + + -- Controle + ativo boolean DEFAULT true NOT NULL, + created_at timestamptz DEFAULT now(), + updated_at timestamptz DEFAULT now(), + + CONSTRAINT document_templates_pkey PRIMARY KEY (id), + + CONSTRAINT dt_tipo_check CHECK ( + tipo = ANY (ARRAY[ + 'declaracao_comparecimento', 'atestado_psicologico', + 'relatorio_acompanhamento', 'recibo_pagamento', + 'termo_consentimento', 'encaminhamento', + 'contrato_servicos', 'tcle', 'autorizacao_menor', + 'laudo_psicologico', 'parecer_psicologico', + 'termo_sigilo', 'declaracao_inicio_tratamento', + 'termo_alta', 'tcle_online', 'outro' + ]) + ) +); + + +-- -------------------------------------------------------------------------- +-- 2. Indices — document_templates +-- -------------------------------------------------------------------------- +CREATE INDEX IF NOT EXISTS dt_tenant_idx + ON public.document_templates USING btree (tenant_id); + +CREATE INDEX IF NOT EXISTS dt_owner_idx + ON public.document_templates USING btree (owner_id); + +CREATE INDEX IF NOT EXISTS dt_global_idx + ON public.document_templates USING btree (is_global) + WHERE is_global = true; + +CREATE INDEX IF NOT EXISTS dt_tipo_idx + ON public.document_templates USING btree (tipo); + +CREATE INDEX IF NOT EXISTS dt_nome_trgm_idx + ON public.document_templates USING gin (nome_template gin_trgm_ops); + + +-- -------------------------------------------------------------------------- +-- 3. Trigger updated_at +-- -------------------------------------------------------------------------- +CREATE TRIGGER trg_dt_updated_at + BEFORE UPDATE ON public.document_templates + FOR EACH ROW + EXECUTE FUNCTION public.set_updated_at(); + + +-- -------------------------------------------------------------------------- +-- 4. RLS — document_templates +-- -------------------------------------------------------------------------- +ALTER TABLE public.document_templates ENABLE ROW LEVEL SECURITY; + +-- Templates globais: todos podem ler +CREATE POLICY "dt: global templates readable by all" + ON public.document_templates + FOR SELECT + USING (is_global = true); + +-- Templates do tenant: membros do tenant podem ler +CREATE POLICY "dt: tenant members can select" + ON public.document_templates + FOR SELECT + USING ( + is_global = false + AND tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + ) + ); + +-- Owner pode inserir/atualizar/deletar seus templates +CREATE POLICY "dt: owner can insert" + ON public.document_templates + FOR INSERT + WITH CHECK (owner_id = auth.uid() AND is_global = false); + +CREATE POLICY "dt: owner can update" + ON public.document_templates + FOR UPDATE + USING (owner_id = auth.uid() AND is_global = false) + WITH CHECK (owner_id = auth.uid() AND is_global = false); + +CREATE POLICY "dt: owner can delete" + ON public.document_templates + FOR DELETE + USING (owner_id = auth.uid() AND is_global = false); + +-- SaaS admin pode gerenciar templates globais (usa funcao public.is_saas_admin()) +CREATE POLICY "dt: saas admin can insert global" + ON public.document_templates + FOR INSERT + WITH CHECK (is_global = true AND public.is_saas_admin()); + +CREATE POLICY "dt: saas admin can update global" + ON public.document_templates + FOR UPDATE + USING (is_global = true AND public.is_saas_admin()) + WITH CHECK (is_global = true AND public.is_saas_admin()); + +CREATE POLICY "dt: saas admin can delete global" + ON public.document_templates + FOR DELETE + USING (is_global = true AND public.is_saas_admin()); + + +-- -------------------------------------------------------------------------- +-- 5. Comentarios — document_templates +-- -------------------------------------------------------------------------- +COMMENT ON TABLE public.document_templates IS 'Templates de documentos para geracao automatica (declaracao, atestado, recibo etc.).'; +COMMENT ON COLUMN public.document_templates.nome_template IS 'Nome do template. Ex: Declaracao de Comparecimento.'; +COMMENT ON COLUMN public.document_templates.tipo IS 'declaracao_comparecimento|atestado_psicologico|relatorio_acompanhamento|recibo_pagamento|termo_consentimento|encaminhamento|outro.'; +COMMENT ON COLUMN public.document_templates.corpo_html IS 'Corpo do template em HTML com variaveis {{nome_variavel}}.'; +COMMENT ON COLUMN public.document_templates.cabecalho_html IS 'HTML do cabecalho (logo, nome da clinica etc.).'; +COMMENT ON COLUMN public.document_templates.rodape_html IS 'HTML do rodape (CRP, endereco, contato etc.).'; +COMMENT ON COLUMN public.document_templates.variaveis IS 'Array com nomes das variaveis usadas no template. Ex: {paciente_nome, data_sessao}.'; +COMMENT ON COLUMN public.document_templates.is_global IS 'true = template padrao do sistema visivel para todos. false = template do tenant.'; +COMMENT ON COLUMN public.document_templates.logo_url IS 'URL do logo personalizado para o cabecalho do documento.'; + + +-- ========================================================================== +-- 6. Tabela: document_generated (cada PDF gerado) +-- ========================================================================== +CREATE TABLE IF NOT EXISTS public.document_generated ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + + -- Origem + template_id uuid NOT NULL REFERENCES public.document_templates(id) ON DELETE RESTRICT, + patient_id uuid NOT NULL REFERENCES public.patients(id) ON DELETE CASCADE, + tenant_id uuid NOT NULL, + + -- Dados usados no preenchimento (snapshot — permite auditoria futura) + dados_preenchidos jsonb NOT NULL DEFAULT '{}', + + -- PDF gerado + pdf_path text NOT NULL, + storage_bucket text NOT NULL DEFAULT 'generated-docs', + + -- Vinculo opcional com documento pai (se o PDF gerado tambem for registrado em documents) + documento_id uuid REFERENCES public.documents(id) ON DELETE SET NULL, + + -- Quem gerou + gerado_por uuid NOT NULL, + gerado_em timestamptz DEFAULT now() NOT NULL, + + CONSTRAINT document_generated_pkey PRIMARY KEY (id) +); + + +-- -------------------------------------------------------------------------- +-- 7. Indices — document_generated +-- -------------------------------------------------------------------------- +CREATE INDEX IF NOT EXISTS dg_template_idx + ON public.document_generated USING btree (template_id); + +CREATE INDEX IF NOT EXISTS dg_patient_idx + ON public.document_generated USING btree (patient_id, gerado_em DESC); + +CREATE INDEX IF NOT EXISTS dg_tenant_idx + ON public.document_generated USING btree (tenant_id, gerado_em DESC); + +CREATE INDEX IF NOT EXISTS dg_gerado_por_idx + ON public.document_generated USING btree (gerado_por, gerado_em DESC); + + +-- -------------------------------------------------------------------------- +-- 8. RLS — document_generated +-- -------------------------------------------------------------------------- +ALTER TABLE public.document_generated ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "dg: generator full access" + ON public.document_generated + USING (gerado_por = auth.uid()) + WITH CHECK (gerado_por = auth.uid()); + +-- Membros do tenant podem visualizar +CREATE POLICY "dg: tenant members can select" + ON public.document_generated + FOR SELECT + USING (tenant_id IN ( + SELECT tm.tenant_id FROM public.tenant_members tm + WHERE tm.user_id = auth.uid() AND tm.status = 'active' + )); + + +-- -------------------------------------------------------------------------- +-- 9. Comentarios — document_generated +-- -------------------------------------------------------------------------- +COMMENT ON TABLE public.document_generated IS 'Registro de cada documento PDF gerado a partir de um template.'; +COMMENT ON COLUMN public.document_generated.template_id IS 'Template usado para gerar o documento.'; +COMMENT ON COLUMN public.document_generated.dados_preenchidos IS 'Snapshot JSON dos dados usados no preenchimento. Permite auditoria futura.'; +COMMENT ON COLUMN public.document_generated.pdf_path IS 'Caminho do PDF gerado no Supabase Storage bucket.'; +COMMENT ON COLUMN public.document_generated.documento_id IS 'FK opcional para documents — se o PDF gerado tambem foi registrado como documento do paciente.'; +COMMENT ON COLUMN public.document_generated.gerado_por IS 'Usuario que gerou o documento (auth.uid()).'; + + +-- ========================================================================== +-- FIM DA MIGRACAO 006 +-- ========================================================================== diff --git a/database-novo/migrations/20260329000003_create_storage_buckets.sql b/database-novo/migrations/20260329000003_create_storage_buckets.sql new file mode 100644 index 0000000..ccab767 --- /dev/null +++ b/database-novo/migrations/20260329000003_create_storage_buckets.sql @@ -0,0 +1,93 @@ +-- ========================================================================== +-- Agencia PSI — Migracao: Storage Buckets para Documentos +-- ========================================================================== +-- Criado por: Leonardo Nohama +-- Data: 2026-03-29 · Sao Carlos/SP — Brasil +-- +-- Cria os buckets no Supabase Storage para documentos de pacientes +-- e PDFs gerados pelo sistema. +-- ========================================================================== + +-- Bucket: documents (uploads de terapeuta/paciente) +INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types) +VALUES ( + 'documents', + 'documents', + false, + 52428800, -- 50 MB + ARRAY[ + 'application/pdf', + 'image/jpeg', 'image/png', 'image/webp', 'image/gif', + 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.ms-excel', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'text/plain' + ] +) +ON CONFLICT (id) DO NOTHING; + +-- Bucket: generated-docs (PDFs gerados pelo sistema) +INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types) +VALUES ( + 'generated-docs', + 'generated-docs', + false, + 20971520, -- 20 MB + ARRAY['application/pdf'] +) +ON CONFLICT (id) DO NOTHING; + + +-- -------------------------------------------------------------------------- +-- Storage RLS Policies — bucket: documents +-- -------------------------------------------------------------------------- + +-- Upload: usuario autenticado pode fazer upload no path do seu tenant +CREATE POLICY "documents: authenticated upload" + ON storage.objects + FOR INSERT + TO authenticated + WITH CHECK (bucket_id = 'documents'); + +-- Download: usuario autenticado pode ler arquivos do seu tenant +CREATE POLICY "documents: authenticated read" + ON storage.objects + FOR SELECT + TO authenticated + USING (bucket_id = 'documents'); + +-- Delete: usuario autenticado pode deletar seus arquivos +CREATE POLICY "documents: authenticated delete" + ON storage.objects + FOR DELETE + TO authenticated + USING (bucket_id = 'documents'); + + +-- -------------------------------------------------------------------------- +-- Storage RLS Policies — bucket: generated-docs +-- -------------------------------------------------------------------------- + +CREATE POLICY "generated-docs: authenticated upload" + ON storage.objects + FOR INSERT + TO authenticated + WITH CHECK (bucket_id = 'generated-docs'); + +CREATE POLICY "generated-docs: authenticated read" + ON storage.objects + FOR SELECT + TO authenticated + USING (bucket_id = 'generated-docs'); + +CREATE POLICY "generated-docs: authenticated delete" + ON storage.objects + FOR DELETE + TO authenticated + USING (bucket_id = 'generated-docs'); + + +-- ========================================================================== +-- FIM DA MIGRACAO +-- ========================================================================== diff --git a/database-novo/migrations/migration_patients.sql b/database-novo/migrations/migration_patients.sql new file mode 100644 index 0000000..c6d9188 --- /dev/null +++ b/database-novo/migrations/migration_patients.sql @@ -0,0 +1,661 @@ +-- ============================================================================= +-- MIGRATION: patients — melhorias completas +-- Gerado em: 2025-03 +-- Estratégia: cirúrgico — só adiciona, nunca destrói o que existe +-- ============================================================================= + + +-- ----------------------------------------------------------------------------- +-- 1. ALTERAÇÕES NA TABELA patients +-- Novos campos adicionados sem tocar nos existentes +-- ----------------------------------------------------------------------------- + +ALTER TABLE public.patients + -- Identidade & pronomes + ADD COLUMN IF NOT EXISTS nome_social text, + ADD COLUMN IF NOT EXISTS pronomes text, + + -- Dados socioeconômicos (opcionais, clínicamente relevantes) + ADD COLUMN IF NOT EXISTS etnia text, + ADD COLUMN IF NOT EXISTS religiao text, + ADD COLUMN IF NOT EXISTS faixa_renda text, + + -- Preferências de comunicação (alimenta lembretes automáticos) + ADD COLUMN IF NOT EXISTS canal_preferido text DEFAULT 'whatsapp', + ADD COLUMN IF NOT EXISTS horario_contato_inicio time DEFAULT '08:00', + ADD COLUMN IF NOT EXISTS horario_contato_fim time DEFAULT '20:00', + ADD COLUMN IF NOT EXISTS idioma text DEFAULT 'pt-BR', + + -- Origem estruturada (permite filtros e relatórios) + ADD COLUMN IF NOT EXISTS origem text, + + -- Financeiro + ADD COLUMN IF NOT EXISTS metodo_pagamento_preferido text, + + -- Ciclo de vida + ADD COLUMN IF NOT EXISTS motivo_saida text, + ADD COLUMN IF NOT EXISTS data_saida date, + ADD COLUMN IF NOT EXISTS encaminhado_para text, + + -- Risco clínico (flag de atenção visível no topo do cadastro) + ADD COLUMN IF NOT EXISTS risco_elevado boolean DEFAULT false NOT NULL, + ADD COLUMN IF NOT EXISTS risco_nota text, + ADD COLUMN IF NOT EXISTS risco_sinalizado_em timestamp with time zone, + ADD COLUMN IF NOT EXISTS risco_sinalizado_por uuid REFERENCES auth.users(id) ON DELETE SET NULL; + + +-- Constraints de validação para novos campos enum-like +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_canal_preferido_check, + ADD CONSTRAINT patients_canal_preferido_check + CHECK (canal_preferido IS NULL OR canal_preferido = ANY ( + ARRAY['whatsapp','email','sms','telefone'] + )); + +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_metodo_pagamento_check, + ADD CONSTRAINT patients_metodo_pagamento_check + CHECK (metodo_pagamento_preferido IS NULL OR metodo_pagamento_preferido = ANY ( + ARRAY['pix','cartao','dinheiro','deposito','convenio'] + )); + +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_faixa_renda_check, + ADD CONSTRAINT patients_faixa_renda_check + CHECK (faixa_renda IS NULL OR faixa_renda = ANY ( + ARRAY['ate_1sm','1_3sm','3_6sm','6_10sm','acima_10sm','nao_informado'] + )); + +-- Constraint: risco_elevado = true exige nota e sinalizante +ALTER TABLE public.patients + DROP CONSTRAINT IF EXISTS patients_risco_consistency_check, + ADD CONSTRAINT patients_risco_consistency_check + CHECK ( + (risco_elevado = false) + OR (risco_elevado = true AND risco_nota IS NOT NULL AND risco_sinalizado_por IS NOT NULL) + ); + +-- Comments +COMMENT ON COLUMN public.patients.nome_social IS 'Nome social preferido — exibido no lugar do nome completo quando preenchido'; +COMMENT ON COLUMN public.patients.pronomes IS 'Pronomes preferidos: ele/dele, ela/dela, eles/deles, etc.'; +COMMENT ON COLUMN public.patients.etnia IS 'Autodeclaração étnico-racial (opcional)'; +COMMENT ON COLUMN public.patients.religiao IS 'Religião ou espiritualidade (opcional, relevante clinicamente)'; +COMMENT ON COLUMN public.patients.faixa_renda IS 'Faixa de renda em salários mínimos — usado para precificação solidária'; +COMMENT ON COLUMN public.patients.canal_preferido IS 'Canal de comunicação preferido para lembretes e notificações'; +COMMENT ON COLUMN public.patients.horario_contato_inicio IS 'Início da janela de horário preferida para contato'; +COMMENT ON COLUMN public.patients.horario_contato_fim IS 'Fim da janela de horário preferida para contato'; +COMMENT ON COLUMN public.patients.origem IS 'Como o paciente chegou: indicacao, agendador, redes_sociais, encaminhamento, outro'; +COMMENT ON COLUMN public.patients.metodo_pagamento_preferido IS 'Método de pagamento habitual — sugerido ao criar cobrança'; +COMMENT ON COLUMN public.patients.motivo_saida IS 'Motivo da alta, inativação ou encaminhamento'; +COMMENT ON COLUMN public.patients.data_saida IS 'Data em que o paciente foi desligado/encaminhado'; +COMMENT ON COLUMN public.patients.encaminhado_para IS 'Nome ou serviço para onde o paciente foi encaminhado'; +COMMENT ON COLUMN public.patients.risco_elevado IS 'Flag de atenção clínica — exibe alerta no topo do cadastro e prontuário'; +COMMENT ON COLUMN public.patients.risco_nota IS 'Descrição do risco (obrigatória quando risco_elevado = true)'; +COMMENT ON COLUMN public.patients.risco_sinalizado_em IS 'Timestamp em que o risco foi sinalizado'; +COMMENT ON COLUMN public.patients.risco_sinalizado_por IS 'Usuário que sinalizou o risco'; + +-- Índices úteis para filtros frequentes +CREATE INDEX IF NOT EXISTS idx_patients_risco_elevado + ON public.patients (tenant_id, risco_elevado) + WHERE risco_elevado = true; + +CREATE INDEX IF NOT EXISTS idx_patients_status_tenant + ON public.patients (tenant_id, status); + +CREATE INDEX IF NOT EXISTS idx_patients_origem + ON public.patients (tenant_id, origem) + WHERE origem IS NOT NULL; + + +-- ----------------------------------------------------------------------------- +-- 2. TABELA patient_contacts +-- Substitui os campos soltos nome_parente/telefone_parente na tabela principal +-- Os campos antigos ficam intactos (retrocompatibilidade) +-- ----------------------------------------------------------------------------- + +CREATE TABLE IF NOT EXISTS public.patient_contacts ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + patient_id uuid NOT NULL REFERENCES public.patients(id) ON DELETE CASCADE, + tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE, + + -- Identificação + nome text NOT NULL, + tipo text NOT NULL, -- emergencia | responsavel_legal | profissional_saude | outro + relacao text, -- mãe, pai, psiquiatra, médico, cônjuge... + + -- Contato + telefone text, + email text, + cpf text, + + -- Profissional de saúde + especialidade text, -- preenchido quando tipo = profissional_saude + registro_profissional text, -- CRM, CRP, etc. + + -- Flags + is_primario boolean DEFAULT false NOT NULL, -- contato principal de emergência + ativo boolean DEFAULT true NOT NULL, + + -- Auditoria + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + + CONSTRAINT patient_contacts_pkey PRIMARY KEY (id), + CONSTRAINT patient_contacts_tipo_check CHECK (tipo = ANY ( + ARRAY['emergencia','responsavel_legal','profissional_saude','outro'] + )) +); + +COMMENT ON TABLE public.patient_contacts IS 'Contatos vinculados ao paciente: emergência, responsável legal, outros profissionais de saúde'; +COMMENT ON COLUMN public.patient_contacts.tipo IS 'Categoria do contato: emergencia | responsavel_legal | profissional_saude | outro'; +COMMENT ON COLUMN public.patient_contacts.is_primario IS 'Contato de emergência principal — exibido em destaque no cadastro'; + +-- Garante no máximo 1 contato primário por paciente +CREATE UNIQUE INDEX IF NOT EXISTS uq_patient_contacts_primario + ON public.patient_contacts (patient_id) + WHERE is_primario = true AND ativo = true; + +CREATE INDEX IF NOT EXISTS idx_patient_contacts_patient + ON public.patient_contacts (patient_id); + +CREATE INDEX IF NOT EXISTS idx_patient_contacts_tenant + ON public.patient_contacts (tenant_id); + +-- updated_at automático +CREATE TRIGGER trg_patient_contacts_updated_at + BEFORE UPDATE ON public.patient_contacts + FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); + +-- RLS — mesmas regras de patients +ALTER TABLE public.patient_contacts ENABLE ROW LEVEL SECURITY; + +CREATE POLICY patient_contacts_select ON public.patient_contacts + FOR SELECT USING ( + public.is_clinic_tenant(tenant_id) + AND public.is_tenant_member(tenant_id) + AND public.tenant_has_feature(tenant_id, 'patients.view') + ); + +CREATE POLICY patient_contacts_write ON public.patient_contacts + USING ( + public.is_clinic_tenant(tenant_id) + AND public.is_tenant_member(tenant_id) + AND public.tenant_has_feature(tenant_id, 'patients.edit') + ) + WITH CHECK ( + public.is_clinic_tenant(tenant_id) + AND public.is_tenant_member(tenant_id) + AND public.tenant_has_feature(tenant_id, 'patients.edit') + ); + + +-- ----------------------------------------------------------------------------- +-- 3. TABELA patient_status_history +-- Trilha de auditoria de todas as mudanças de status do paciente +-- ----------------------------------------------------------------------------- + +CREATE TABLE IF NOT EXISTS public.patient_status_history ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + patient_id uuid NOT NULL REFERENCES public.patients(id) ON DELETE CASCADE, + tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE, + + status_anterior text, -- NULL na primeira inserção + status_novo text NOT NULL, + motivo text, + encaminhado_para text, -- preenchido quando status = Encaminhado + data_saida date, -- preenchido quando Alta/Encaminhado/Arquivado + + alterado_por uuid REFERENCES auth.users(id) ON DELETE SET NULL, + alterado_em timestamp with time zone DEFAULT now() NOT NULL, + + CONSTRAINT patient_status_history_pkey PRIMARY KEY (id), + CONSTRAINT psh_status_novo_check CHECK (status_novo = ANY ( + ARRAY['Ativo','Inativo','Alta','Encaminhado','Arquivado'] + )) +); + +COMMENT ON TABLE public.patient_status_history IS 'Histórico imutável de todas as mudanças de status do paciente — não editar, apenas inserir'; + +CREATE INDEX IF NOT EXISTS idx_psh_patient + ON public.patient_status_history (patient_id, alterado_em DESC); + +CREATE INDEX IF NOT EXISTS idx_psh_tenant + ON public.patient_status_history (tenant_id, alterado_em DESC); + +-- RLS +ALTER TABLE public.patient_status_history ENABLE ROW LEVEL SECURITY; + +CREATE POLICY psh_select ON public.patient_status_history + FOR SELECT USING ( + public.is_clinic_tenant(tenant_id) + AND public.is_tenant_member(tenant_id) + AND public.tenant_has_feature(tenant_id, 'patients.view') + ); + +CREATE POLICY psh_insert ON public.patient_status_history + FOR INSERT WITH CHECK ( + public.is_clinic_tenant(tenant_id) + AND public.is_tenant_member(tenant_id) + AND public.tenant_has_feature(tenant_id, 'patients.edit') + ); + +-- Trigger: registra automaticamente no histórico quando status muda em patients +CREATE OR REPLACE FUNCTION public.trg_fn_patient_status_history() +RETURNS trigger +LANGUAGE plpgsql SECURITY DEFINER +AS $$ +BEGIN + IF (TG_OP = 'INSERT') OR (OLD.status IS DISTINCT FROM NEW.status) THEN + INSERT INTO public.patient_status_history ( + patient_id, tenant_id, + status_anterior, status_novo, + motivo, encaminhado_para, data_saida, + alterado_por, alterado_em + ) VALUES ( + NEW.id, NEW.tenant_id, + CASE WHEN TG_OP = 'INSERT' THEN NULL ELSE OLD.status END, + NEW.status, + NEW.motivo_saida, + NEW.encaminhado_para, + NEW.data_saida, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + +DROP TRIGGER IF EXISTS trg_patient_status_history ON public.patients; +CREATE TRIGGER trg_patient_status_history + AFTER INSERT OR UPDATE OF status ON public.patients + FOR EACH ROW EXECUTE FUNCTION public.trg_fn_patient_status_history(); + + +-- ----------------------------------------------------------------------------- +-- 4. TABELA patient_timeline +-- Feed cronológico automático de eventos relevantes do paciente +-- ----------------------------------------------------------------------------- + +CREATE TABLE IF NOT EXISTS public.patient_timeline ( + id uuid DEFAULT gen_random_uuid() NOT NULL, + patient_id uuid NOT NULL REFERENCES public.patients(id) ON DELETE CASCADE, + tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE, + + -- Tipo do evento + evento_tipo text NOT NULL, + -- Exemplos: primeira_sessao | sessao_realizada | sessao_cancelada | falta | + -- status_alterado | risco_sinalizado | documento_assinado | + -- escala_respondida | pagamento_vencido | pagamento_recebido | + -- tarefa_combinada | contato_adicionado | prontuario_editado + + titulo text NOT NULL, -- Ex: "Sessão realizada" + descricao text, -- Ex: "Sessão 47 · presencial · 50min" + icone_cor text DEFAULT 'gray', -- green | blue | amber | red | gray + link_ref_tipo text, -- agenda_evento | financial_record | documento | escala + link_ref_id uuid, -- FK genérico — sem constraint formal (polimórfico) + gerado_por uuid REFERENCES auth.users(id) ON DELETE SET NULL, + ocorrido_em timestamp with time zone DEFAULT now() NOT NULL, + + CONSTRAINT patient_timeline_pkey PRIMARY KEY (id), + CONSTRAINT pt_evento_tipo_check CHECK (evento_tipo = ANY (ARRAY[ + 'primeira_sessao','sessao_realizada','sessao_cancelada','falta', + 'status_alterado','risco_sinalizado','risco_removido', + 'documento_assinado','documento_adicionado', + 'escala_respondida','escala_enviada', + 'pagamento_vencido','pagamento_recebido', + 'tarefa_combinada','contato_adicionado', + 'prontuario_editado','nota_adicionada','manual' + ])), + CONSTRAINT pt_icone_cor_check CHECK (icone_cor = ANY ( + ARRAY['green','blue','amber','red','gray','purple'] + )) +); + +COMMENT ON TABLE public.patient_timeline IS 'Feed cronológico de eventos do paciente — alimentado por triggers e inserções manuais'; +COMMENT ON COLUMN public.patient_timeline.link_ref_tipo IS 'Tipo da entidade referenciada (polimórfico): agenda_evento | financial_record | documento | escala'; +COMMENT ON COLUMN public.patient_timeline.link_ref_id IS 'ID da entidade referenciada — sem FK formal para suportar múltiplos tipos'; + +CREATE INDEX IF NOT EXISTS idx_pt_patient_ocorrido + ON public.patient_timeline (patient_id, ocorrido_em DESC); + +CREATE INDEX IF NOT EXISTS idx_pt_tenant + ON public.patient_timeline (tenant_id, ocorrido_em DESC); + +CREATE INDEX IF NOT EXISTS idx_pt_evento_tipo + ON public.patient_timeline (patient_id, evento_tipo); + +-- RLS +ALTER TABLE public.patient_timeline ENABLE ROW LEVEL SECURITY; + +CREATE POLICY pt_select ON public.patient_timeline + FOR SELECT USING ( + public.is_clinic_tenant(tenant_id) + AND public.is_tenant_member(tenant_id) + AND public.tenant_has_feature(tenant_id, 'patients.view') + ); + +CREATE POLICY pt_insert ON public.patient_timeline + FOR INSERT WITH CHECK ( + public.is_clinic_tenant(tenant_id) + AND public.is_tenant_member(tenant_id) + AND public.tenant_has_feature(tenant_id, 'patients.edit') + ); + +-- Trigger: registra na timeline quando risco é sinalizado/removido +CREATE OR REPLACE FUNCTION public.trg_fn_patient_risco_timeline() +RETURNS trigger +LANGUAGE plpgsql SECURITY DEFINER +AS $$ +BEGIN + IF OLD.risco_elevado IS DISTINCT FROM NEW.risco_elevado THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, + evento_tipo, titulo, descricao, icone_cor, + gerado_por, ocorrido_em + ) VALUES ( + NEW.id, NEW.tenant_id, + CASE WHEN NEW.risco_elevado THEN 'risco_sinalizado' ELSE 'risco_removido' END, + CASE WHEN NEW.risco_elevado THEN 'Risco elevado sinalizado' ELSE 'Sinalização de risco removida' END, + NEW.risco_nota, + CASE WHEN NEW.risco_elevado THEN 'red' ELSE 'green' END, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + +DROP TRIGGER IF EXISTS trg_patient_risco_timeline ON public.patients; +CREATE TRIGGER trg_patient_risco_timeline + AFTER UPDATE OF risco_elevado ON public.patients + FOR EACH ROW EXECUTE FUNCTION public.trg_fn_patient_risco_timeline(); + +-- Trigger: registra na timeline quando status muda +CREATE OR REPLACE FUNCTION public.trg_fn_patient_status_timeline() +RETURNS trigger +LANGUAGE plpgsql SECURITY DEFINER +AS $$ +BEGIN + IF (TG_OP = 'INSERT') OR (OLD.status IS DISTINCT FROM NEW.status) THEN + INSERT INTO public.patient_timeline ( + patient_id, tenant_id, + evento_tipo, titulo, descricao, icone_cor, + gerado_por, ocorrido_em + ) VALUES ( + NEW.id, NEW.tenant_id, + 'status_alterado', + 'Status alterado para ' || NEW.status, + CASE + WHEN TG_OP = 'INSERT' THEN 'Paciente cadastrado' + ELSE 'De ' || OLD.status || ' → ' || NEW.status || + CASE WHEN NEW.motivo_saida IS NOT NULL THEN ' · ' || NEW.motivo_saida ELSE '' END + END, + CASE NEW.status + WHEN 'Ativo' THEN 'green' + WHEN 'Alta' THEN 'blue' + WHEN 'Inativo' THEN 'gray' + WHEN 'Encaminhado' THEN 'amber' + WHEN 'Arquivado' THEN 'gray' + ELSE 'gray' + END, + auth.uid(), + now() + ); + END IF; + RETURN NEW; +END; +$$; + +DROP TRIGGER IF EXISTS trg_patient_status_timeline ON public.patients; +CREATE TRIGGER trg_patient_status_timeline + AFTER INSERT OR UPDATE OF status ON public.patients + FOR EACH ROW EXECUTE FUNCTION public.trg_fn_patient_status_timeline(); + + +-- ----------------------------------------------------------------------------- +-- 5. VIEW v_patient_engajamento +-- Score calculado em tempo real — sem armazenar, sem inconsistência +-- ----------------------------------------------------------------------------- + +CREATE OR REPLACE VIEW public.v_patient_engajamento +WITH (security_invoker = on) +AS +WITH sessoes AS ( + SELECT + ae.patient_id, + ae.tenant_id, + COUNT(*) FILTER (WHERE ae.status = 'realizado') AS total_realizadas, + COUNT(*) FILTER (WHERE ae.status IN ('realizado','cancelado','faltou')) AS total_marcadas, + COUNT(*) FILTER (WHERE ae.status = 'faltou') AS total_faltas, + MAX(ae.inicio_em) FILTER (WHERE ae.status = 'realizado') AS ultima_sessao_em, + MIN(ae.inicio_em) FILTER (WHERE ae.status = 'realizado') AS primeira_sessao_em, + COUNT(*) FILTER (WHERE ae.status = 'realizado' + AND ae.inicio_em >= now() - interval '30 days') AS sessoes_ultimo_mes + FROM public.agenda_eventos ae + WHERE ae.patient_id IS NOT NULL + GROUP BY ae.patient_id, ae.tenant_id +), +financeiro AS ( + SELECT + fr.patient_id, + fr.tenant_id, + COALESCE(SUM(fr.final_amount) FILTER (WHERE fr.status = 'paid'), 0) AS total_pago, + COALESCE(AVG(fr.final_amount) FILTER (WHERE fr.status = 'paid'), 0) AS ticket_medio, + COUNT(*) FILTER (WHERE fr.status IN ('pending','overdue') + AND fr.due_date < now()) AS cobr_vencidas, + COUNT(*) FILTER (WHERE fr.status IN ('pending','overdue')) AS cobr_pendentes, + COUNT(*) FILTER (WHERE fr.type = 'receita' AND fr.status = 'paid') AS cobr_pagas + FROM public.financial_records fr + WHERE fr.patient_id IS NOT NULL + AND fr.deleted_at IS NULL + GROUP BY fr.patient_id, fr.tenant_id +) +SELECT + p.id AS patient_id, + p.tenant_id, + p.nome_completo, + p.status, + p.risco_elevado, + + -- Sessões + COALESCE(s.total_realizadas, 0) AS total_sessoes, + COALESCE(s.sessoes_ultimo_mes, 0) AS sessoes_ultimo_mes, + s.primeira_sessao_em, + s.ultima_sessao_em, + EXTRACT(DAY FROM now() - s.ultima_sessao_em)::int AS dias_sem_sessao, + + -- Taxa de comparecimento (%) + CASE + WHEN COALESCE(s.total_marcadas, 0) = 0 THEN NULL + ELSE ROUND((s.total_realizadas::numeric / s.total_marcadas) * 100, 1) + END AS taxa_comparecimento, + + -- Financeiro + COALESCE(f.total_pago, 0) AS ltv_total, + ROUND(COALESCE(f.ticket_medio, 0), 2) AS ticket_medio, + COALESCE(f.cobr_vencidas, 0) AS cobr_vencidas, + COALESCE(f.cobr_pagas, 0) AS cobr_pagas, + + -- Taxa de pagamentos em dia (%) + CASE + WHEN COALESCE(f.cobr_pagas + f.cobr_vencidas, 0) = 0 THEN NULL + ELSE ROUND( + f.cobr_pagas::numeric / (f.cobr_pagas + f.cobr_vencidas) * 100, 1 + ) + END AS taxa_pagamentos_dia, + + -- Score de engajamento composto (0-100) + -- Pesos: comparecimento 50%, pagamentos 30%, recência 20% + ROUND( + LEAST(100, + COALESCE( + ( + -- Comparecimento (50 pts) + CASE WHEN COALESCE(s.total_marcadas, 0) = 0 THEN 50 + ELSE LEAST(50, (s.total_realizadas::numeric / s.total_marcadas) * 50) + END + + + -- Pagamentos em dia (30 pts) + CASE WHEN COALESCE(f.cobr_pagas + f.cobr_vencidas, 0) = 0 THEN 30 + ELSE LEAST(30, f.cobr_pagas::numeric / (f.cobr_pagas + f.cobr_vencidas) * 30) + END + + + -- Recência (20 pts — penaliza quem está há muito tempo sem sessão) + CASE + WHEN s.ultima_sessao_em IS NULL THEN 0 + WHEN EXTRACT(DAY FROM now() - s.ultima_sessao_em) <= 14 THEN 20 + WHEN EXTRACT(DAY FROM now() - s.ultima_sessao_em) <= 30 THEN 15 + WHEN EXTRACT(DAY FROM now() - s.ultima_sessao_em) <= 60 THEN 8 + ELSE 0 + END + ), 0 + ) + ) + , 0) AS engajamento_score, + + -- Duração do tratamento + CASE + WHEN s.primeira_sessao_em IS NULL THEN NULL + ELSE EXTRACT(DAY FROM now() - s.primeira_sessao_em)::int + END AS duracao_tratamento_dias + +FROM public.patients p +LEFT JOIN sessoes s ON s.patient_id = p.id AND s.tenant_id = p.tenant_id +LEFT JOIN financeiro f ON f.patient_id = p.id AND f.tenant_id = p.tenant_id; + +COMMENT ON VIEW public.v_patient_engajamento IS + 'Score de engajamento e métricas consolidadas por paciente. Calculado em tempo real via RLS (security_invoker=on).'; + + +-- ----------------------------------------------------------------------------- +-- 6. VIEW v_patients_risco +-- Lista rápida de pacientes que precisam de atenção imediata +-- ----------------------------------------------------------------------------- + +CREATE OR REPLACE VIEW public.v_patients_risco +WITH (security_invoker = on) +AS +SELECT + p.id, + p.tenant_id, + p.nome_completo, + p.status, + p.risco_elevado, + p.risco_nota, + p.risco_sinalizado_em, + e.dias_sem_sessao, + e.engajamento_score, + e.taxa_comparecimento, + -- Motivo do alerta + CASE + WHEN p.risco_elevado THEN 'risco_sinalizado' + WHEN COALESCE(e.dias_sem_sessao, 999) > 30 + AND p.status = 'Ativo' THEN 'sem_sessao_30d' + WHEN COALESCE(e.taxa_comparecimento, 100) < 60 THEN 'baixo_comparecimento' + WHEN COALESCE(e.cobr_vencidas, 0) > 0 THEN 'cobranca_vencida' + ELSE 'ok' + END AS alerta_tipo +FROM public.patients p +JOIN public.v_patient_engajamento e ON e.patient_id = p.id +WHERE p.status = 'Ativo' + AND ( + p.risco_elevado = true + OR COALESCE(e.dias_sem_sessao, 999) > 30 + OR COALESCE(e.taxa_comparecimento, 100) < 60 + OR COALESCE(e.cobr_vencidas, 0) > 0 + ); + +COMMENT ON VIEW public.v_patients_risco IS + 'Pacientes ativos que precisam de atenção: risco clínico, sem sessão há 30+ dias, baixo comparecimento ou cobrança vencida'; + + +-- ----------------------------------------------------------------------------- +-- 7. Migração de dados: popular patient_contacts com os dados já existentes +-- Roda só uma vez — protegido por WHERE NOT EXISTS +-- ----------------------------------------------------------------------------- + +INSERT INTO public.patient_contacts ( + patient_id, tenant_id, + nome, tipo, relacao, + telefone, is_primario, ativo +) +SELECT + p.id, + p.tenant_id, + p.nome_parente, + 'emergencia', + p.grau_parentesco, + p.telefone_parente, + true, + true +FROM public.patients p +WHERE p.nome_parente IS NOT NULL + AND p.telefone_parente IS NOT NULL + AND NOT EXISTS ( + SELECT 1 FROM public.patient_contacts pc + WHERE pc.patient_id = p.id AND pc.tipo = 'emergencia' + ); + +-- Migra responsável legal quando diferente do parente de emergência +INSERT INTO public.patient_contacts ( + patient_id, tenant_id, + nome, tipo, relacao, + telefone, cpf, + is_primario, ativo +) +SELECT + p.id, + p.tenant_id, + p.nome_responsavel, + 'responsavel_legal', + 'Responsável legal', + p.telefone_responsavel, + p.cpf_responsavel, + false, + true +FROM public.patients p +WHERE p.nome_responsavel IS NOT NULL + AND NOT EXISTS ( + SELECT 1 FROM public.patient_contacts pc + WHERE pc.patient_id = p.id AND pc.tipo = 'responsavel_legal' + ); + + +-- ----------------------------------------------------------------------------- +-- 8. Seed do histórico de status para pacientes já existentes +-- Cria a primeira entrada de histórico com o status atual +-- ----------------------------------------------------------------------------- + +INSERT INTO public.patient_status_history ( + patient_id, tenant_id, + status_anterior, status_novo, + motivo, alterado_em +) +SELECT + p.id, + p.tenant_id, + NULL, + p.status, + 'Status inicial — migração de dados', + COALESCE(p.created_at, now()) +FROM public.patients p +WHERE NOT EXISTS ( + SELECT 1 FROM public.patient_status_history psh + WHERE psh.patient_id = p.id +); + + +-- ============================================================================= +-- FIM DO MIGRATION +-- Resumo do que foi feito: +-- 1. ALTER TABLE patients — 16 novos campos (pronomes, risco, origem, etc.) +-- 2. CREATE TABLE patient_contacts — múltiplos contatos por paciente +-- 3. CREATE TABLE patient_status_history — trilha imutável de mudanças de status +-- 4. CREATE TABLE patient_timeline — feed cronológico de eventos +-- 5. Triggers automáticos — status history, timeline de risco e status +-- 6. VIEW v_patient_engajamento — score 0-100 + métricas calculadas em tempo real +-- 7. VIEW v_patients_risco — lista de pacientes que precisam de atenção +-- 8. Migração de dados — popula patient_contacts e status_history com dados existentes +-- ============================================================================= diff --git a/database-novo/seeds/seed_011_features.sql b/database-novo/seeds/seed_011_features.sql index 696647f..1968d80 100644 --- a/database-novo/seeds/seed_011_features.sql +++ b/database-novo/seeds/seed_011_features.sql @@ -60,7 +60,14 @@ VALUES -- ── Branding / API / Auditoria ── ('f393178c-284d-422f-b096-8793f85428d5', 'custom_branding', 'Custom branding', '2026-03-01 09:59:15.432733+00', 'Personalização de marca', 'Marca Personalizada'), ('d6f54674-ea8b-484b-af0e-99127a510da2', 'api_access', 'API/Integrations access', '2026-03-01 09:59:15.432733+00', 'Integrações/API', 'Acesso à API'), - ('a5593d96-dd95-46bb-bef0-bd379b56ad50', 'audit_log', 'Audit log capability', '2026-03-01 09:59:15.432733+00', 'Auditoria', 'Log de Auditoria') + ('a5593d96-dd95-46bb-bef0-bd379b56ad50', 'audit_log', 'Audit log capability', '2026-03-01 09:59:15.432733+00', 'Auditoria', 'Log de Auditoria'), + + -- ── Documentos ── + ('b1a2c3d4-1111-4aaa-bbbb-000000000001', 'documents.upload', 'Upload e gestão de arquivos do paciente', '2026-03-29 00:00:00.000000+00', 'Upload de documentos', 'Upload de Documentos'), + ('b1a2c3d4-1111-4aaa-bbbb-000000000002', 'documents.templates', 'Geração de documentos a partir de templates', '2026-03-29 00:00:00.000000+00', 'Templates de documentos', 'Templates de Documentos'), + ('b1a2c3d4-1111-4aaa-bbbb-000000000003', 'documents.signatures', 'Assinatura eletrônica de documentos', '2026-03-29 00:00:00.000000+00', 'Assinaturas eletrônicas', 'Assinaturas Eletrônicas'), + ('b1a2c3d4-1111-4aaa-bbbb-000000000004', 'documents.share_links', 'Links temporários para compartilhamento de documentos', '2026-03-29 00:00:00.000000+00', 'Links de compartilhamento', 'Links de Compartilhamento'), + ('b1a2c3d4-1111-4aaa-bbbb-000000000005', 'documents.patient_portal', 'Acesso a documentos pelo portal do paciente', '2026-03-29 00:00:00.000000+00', 'Portal do paciente (documentos)', 'Portal do Paciente (Documentos)') ON CONFLICT (id) DO UPDATE SET key = EXCLUDED.key, @@ -71,7 +78,7 @@ ON CONFLICT (id) DO UPDATE SET DO $$ BEGIN - RAISE NOTICE 'seed_011_features: 26 features inseridas/atualizadas.'; + RAISE NOTICE 'seed_011_features: 31 features inseridas/atualizadas.'; END; $$; diff --git a/database-novo/seeds/seed_012_plan_features.sql b/database-novo/seeds/seed_012_plan_features.sql index 9d04aab..48c0797 100644 --- a/database-novo/seeds/seed_012_plan_features.sql +++ b/database-novo/seeds/seed_012_plan_features.sql @@ -52,7 +52,13 @@ INSERT INTO public.plan_features (plan_id, feature_id, enabled, limits) VALUES -- PRO exclusivo ('a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145', 'f393178c-284d-422f-b096-8793f85428d5', true, NULL), -- custom_branding ('a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145', 'd6f54674-ea8b-484b-af0e-99127a510da2', true, NULL), -- api_access - ('a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145', 'a5593d96-dd95-46bb-bef0-bd379b56ad50', true, NULL); -- audit_log + ('a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145', 'a5593d96-dd95-46bb-bef0-bd379b56ad50', true, NULL), -- audit_log + -- Documentos + ('a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145', 'b1a2c3d4-1111-4aaa-bbbb-000000000001', true, '{"max_storage_mb": 5000}'), -- documents.upload + ('a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145', 'b1a2c3d4-1111-4aaa-bbbb-000000000002', true, NULL), -- documents.templates + ('a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145', 'b1a2c3d4-1111-4aaa-bbbb-000000000003', true, NULL), -- documents.signatures + ('a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145', 'b1a2c3d4-1111-4aaa-bbbb-000000000004', true, NULL), -- documents.share_links + ('a74bc2d4-88c6-4cc6-b004-ef2bcb1b5145', 'b1a2c3d4-1111-4aaa-bbbb-000000000005', true, NULL); -- documents.patient_portal -- ════════════════════════════════════════════════════════════════ @@ -88,7 +94,13 @@ INSERT INTO public.plan_features (plan_id, feature_id, enabled, limits) VALUES ('01a5867f-0705-4714-ac97-a23470949157', '74fc1321-4d17-49c3-b72e-db3a7f4be451', false, NULL), -- rooms (PRO) ('01a5867f-0705-4714-ac97-a23470949157', 'f393178c-284d-422f-b096-8793f85428d5', false, NULL), -- custom_branding (PRO) ('01a5867f-0705-4714-ac97-a23470949157', 'd6f54674-ea8b-484b-af0e-99127a510da2', false, NULL), -- api_access (PRO) - ('01a5867f-0705-4714-ac97-a23470949157', 'a5593d96-dd95-46bb-bef0-bd379b56ad50', false, NULL); -- audit_log (PRO) + ('01a5867f-0705-4714-ac97-a23470949157', 'a5593d96-dd95-46bb-bef0-bd379b56ad50', false, NULL), -- audit_log (PRO) + -- Documentos + ('01a5867f-0705-4714-ac97-a23470949157', 'b1a2c3d4-1111-4aaa-bbbb-000000000001', true, '{"max_storage_mb": 500}'), -- documents.upload (FREE) + ('01a5867f-0705-4714-ac97-a23470949157', 'b1a2c3d4-1111-4aaa-bbbb-000000000002', false, NULL), -- documents.templates (PRO) + ('01a5867f-0705-4714-ac97-a23470949157', 'b1a2c3d4-1111-4aaa-bbbb-000000000003', false, NULL), -- documents.signatures (PRO) + ('01a5867f-0705-4714-ac97-a23470949157', 'b1a2c3d4-1111-4aaa-bbbb-000000000004', false, NULL), -- documents.share_links (PRO) + ('01a5867f-0705-4714-ac97-a23470949157', 'b1a2c3d4-1111-4aaa-bbbb-000000000005', false, NULL); -- documents.patient_portal (PRO) -- ════════════════════════════════════════════════════════════════ @@ -122,7 +134,13 @@ INSERT INTO public.plan_features (plan_id, feature_id, enabled, limits) VALUES -- PRO exclusivo ('82067ba7-16f0-4803-b36f-4c4e8919d4b4', 'f393178c-284d-422f-b096-8793f85428d5', true, NULL), -- custom_branding ('82067ba7-16f0-4803-b36f-4c4e8919d4b4', 'd6f54674-ea8b-484b-af0e-99127a510da2', true, NULL), -- api_access - ('82067ba7-16f0-4803-b36f-4c4e8919d4b4', 'a5593d96-dd95-46bb-bef0-bd379b56ad50', true, NULL); -- audit_log + ('82067ba7-16f0-4803-b36f-4c4e8919d4b4', 'a5593d96-dd95-46bb-bef0-bd379b56ad50', true, NULL), -- audit_log + -- Documentos + ('82067ba7-16f0-4803-b36f-4c4e8919d4b4', 'b1a2c3d4-1111-4aaa-bbbb-000000000001', true, '{"max_storage_mb": 5000}'), -- documents.upload + ('82067ba7-16f0-4803-b36f-4c4e8919d4b4', 'b1a2c3d4-1111-4aaa-bbbb-000000000002', true, NULL), -- documents.templates + ('82067ba7-16f0-4803-b36f-4c4e8919d4b4', 'b1a2c3d4-1111-4aaa-bbbb-000000000003', true, NULL), -- documents.signatures + ('82067ba7-16f0-4803-b36f-4c4e8919d4b4', 'b1a2c3d4-1111-4aaa-bbbb-000000000004', true, NULL), -- documents.share_links + ('82067ba7-16f0-4803-b36f-4c4e8919d4b4', 'b1a2c3d4-1111-4aaa-bbbb-000000000005', true, NULL); -- documents.patient_portal -- ════════════════════════════════════════════════════════════════ @@ -152,7 +170,13 @@ INSERT INTO public.plan_features (plan_id, feature_id, enabled, limits) VALUES -- PRO-only (desabilitado) ('c56fe2a8-2c17-4048-adc7-ff7fbd89461a', 'f393178c-284d-422f-b096-8793f85428d5', false, NULL), -- custom_branding (PRO) ('c56fe2a8-2c17-4048-adc7-ff7fbd89461a', 'd6f54674-ea8b-484b-af0e-99127a510da2', false, NULL), -- api_access (PRO) - ('c56fe2a8-2c17-4048-adc7-ff7fbd89461a', 'a5593d96-dd95-46bb-bef0-bd379b56ad50', false, NULL); -- audit_log (PRO) + ('c56fe2a8-2c17-4048-adc7-ff7fbd89461a', 'a5593d96-dd95-46bb-bef0-bd379b56ad50', false, NULL), -- audit_log (PRO) + -- Documentos + ('c56fe2a8-2c17-4048-adc7-ff7fbd89461a', 'b1a2c3d4-1111-4aaa-bbbb-000000000001', true, '{"max_storage_mb": 500}'), -- documents.upload (FREE) + ('c56fe2a8-2c17-4048-adc7-ff7fbd89461a', 'b1a2c3d4-1111-4aaa-bbbb-000000000002', false, NULL), -- documents.templates (PRO) + ('c56fe2a8-2c17-4048-adc7-ff7fbd89461a', 'b1a2c3d4-1111-4aaa-bbbb-000000000003', false, NULL), -- documents.signatures (PRO) + ('c56fe2a8-2c17-4048-adc7-ff7fbd89461a', 'b1a2c3d4-1111-4aaa-bbbb-000000000004', false, NULL), -- documents.share_links (PRO) + ('c56fe2a8-2c17-4048-adc7-ff7fbd89461a', 'b1a2c3d4-1111-4aaa-bbbb-000000000005', false, NULL); -- documents.patient_portal (PRO) -- ════════════════════════════════════════════════════════════════ diff --git a/database-novo/seeds/seed_015_document_templates.sql b/database-novo/seeds/seed_015_document_templates.sql new file mode 100644 index 0000000..cb5f67e --- /dev/null +++ b/database-novo/seeds/seed_015_document_templates.sql @@ -0,0 +1,230 @@ +-- ========================================================================== +-- Agencia PSI — Seed: Templates globais de documentos +-- ========================================================================== +-- 4 templates padrao do sistema (is_global = true). +-- Disponiveis para todos os tenants como base para personalizacao. +-- ========================================================================== + +INSERT INTO public.document_templates ( + id, tenant_id, owner_id, nome_template, tipo, descricao, + corpo_html, cabecalho_html, rodape_html, + variaveis, is_global, ativo +) VALUES + +-- ──────────────────────────────────────────────────────────── +-- 1. Declaração de Comparecimento +-- ──────────────────────────────────────────────────────────── +( + gen_random_uuid(), NULL, NULL, + 'Declaração de Comparecimento', + 'declaracao_comparecimento', + 'Declara que o paciente compareceu à sessão na data e horário indicados.', + E'

DECLARAÇÃO DE COMPARECIMENTO

\n\n

Declaro, para os devidos fins, que {{paciente_nome}}, portador(a) do CPF nº {{paciente_cpf}}, compareceu a esta clínica/consultório no dia {{data_sessao}}, no horário das {{hora_inicio}} às {{hora_fim}}, para atendimento psicológico.

\n\n

{{cidade_estado}}, {{data_atual_extenso}}.

\n\n
\n
\n {{terapeuta_nome}}
\n Psicólogo(a) — CRP {{terapeuta_crp}}\n
', + E'
\n {{clinica_nome}}
\n {{clinica_endereco}} · {{clinica_telefone}}\n
', + E'
\n {{clinica_nome}} · {{clinica_endereco}} · {{clinica_telefone}}\n
', + ARRAY['paciente_nome','paciente_cpf','data_sessao','hora_inicio','hora_fim','cidade_estado','data_atual_extenso','terapeuta_nome','terapeuta_crp','clinica_nome','clinica_endereco','clinica_telefone'], + true, true +), + +-- ──────────────────────────────────────────────────────────── +-- 2. Atestado Psicológico +-- ──────────────────────────────────────────────────────────── +( + gen_random_uuid(), NULL, NULL, + 'Atestado Psicológico', + 'atestado_psicologico', + 'Atesta acompanhamento psicológico do paciente conforme Resolução CFP.', + E'

ATESTADO PSICOLÓGICO

\n\n

Atesto, para os devidos fins, que {{paciente_nome}}, nascido(a) em {{paciente_data_nascimento}}, encontra-se em acompanhamento psicológico neste consultório/clínica.

\n\n

O presente atestado é emitido com base no atendimento realizado, em conformidade com o Código de Ética Profissional do Psicólogo e as Resoluções do Conselho Federal de Psicologia.

\n\n

{{cidade_estado}}, {{data_atual_extenso}}.

\n\n
\n
\n {{terapeuta_nome}}
\n Psicólogo(a) — CRP {{terapeuta_crp}}\n
', + E'
\n {{clinica_nome}}
\n {{clinica_endereco}}\n
', + E'
\n {{clinica_nome}} · {{clinica_endereco}} · {{clinica_telefone}}\n
', + ARRAY['paciente_nome','paciente_data_nascimento','cidade_estado','data_atual_extenso','terapeuta_nome','terapeuta_crp','clinica_nome','clinica_endereco','clinica_telefone'], + true, true +), + +-- ──────────────────────────────────────────────────────────── +-- 3. Relatório de Acompanhamento +-- ──────────────────────────────────────────────────────────── +( + gen_random_uuid(), NULL, NULL, + 'Relatório de Acompanhamento', + 'relatorio_acompanhamento', + 'Modelo base para relatório de acompanhamento psicológico.', + E'

RELATÓRIO DE ACOMPANHAMENTO PSICOLÓGICO

\n\n\n \n \n \n \n \n
Paciente:{{paciente_nome}}
Data de nascimento:{{paciente_data_nascimento}}
CPF:{{paciente_cpf}}
Profissional:{{terapeuta_nome}} — CRP {{terapeuta_crp}}
Data do relatório:{{data_atual}}
\n\n

1. Demanda inicial

\n

[Descreva a queixa ou demanda que motivou o início do acompanhamento.]

\n\n

2. Procedimentos utilizados

\n

[Descreva os métodos, técnicas e instrumentos utilizados.]

\n\n

3. Evolução observada

\n

[Descreva a evolução do paciente ao longo do acompanhamento.]

\n\n

4. Considerações finais

\n

[Conclusões e recomendações.]

\n\n

{{cidade_estado}}, {{data_atual_extenso}}.

\n\n
\n
\n {{terapeuta_nome}}
\n Psicólogo(a) — CRP {{terapeuta_crp}}\n
', + E'
\n {{clinica_nome}}
\n {{clinica_endereco}} · {{clinica_telefone}}\n
', + E'
\n Este documento é de caráter sigiloso, conforme Art. 9º do Código de Ética do Psicólogo.\n
', + ARRAY['paciente_nome','paciente_data_nascimento','paciente_cpf','terapeuta_nome','terapeuta_crp','data_atual','data_atual_extenso','cidade_estado','clinica_nome','clinica_endereco','clinica_telefone'], + true, true +), + +-- ──────────────────────────────────────────────────────────── +-- 4. Recibo de Pagamento +-- ──────────────────────────────────────────────────────────── +( + gen_random_uuid(), NULL, NULL, + 'Recibo de Pagamento', + 'recibo_pagamento', + 'Recibo para pagamento de sessão de atendimento psicológico.', + E'

RECIBO DE PAGAMENTO

\n\n

Recebi de {{paciente_nome}}, CPF nº {{paciente_cpf}}, a quantia de {{valor}} ({{valor_extenso}}), referente a sessão de atendimento psicológico realizada em {{data_sessao}}.

\n\n

Forma de pagamento: {{forma_pagamento}}

\n\n

{{cidade_estado}}, {{data_atual_extenso}}.

\n\n
\n
\n {{terapeuta_nome}}
\n Psicólogo(a) — CRP {{terapeuta_crp}}\n
', + E'
\n {{clinica_nome}}
\n {{clinica_endereco}}\n
', + E'
\n {{clinica_nome}} · CNPJ: {{clinica_cnpj}} · {{clinica_telefone}}\n
', + ARRAY['paciente_nome','paciente_cpf','valor','valor_extenso','data_sessao','forma_pagamento','cidade_estado','data_atual_extenso','terapeuta_nome','terapeuta_crp','clinica_nome','clinica_endereco','clinica_cnpj','clinica_telefone'], + true, true +) + +, + +-- ──────────────────────────────────────────────────────────── +-- 5. Contrato de Prestação de Serviços Psicológicos +-- ──────────────────────────────────────────────────────────── +( + gen_random_uuid(), NULL, NULL, + 'Contrato de Prestação de Serviços', + 'contrato_servicos', + 'Contrato padrão entre profissional/clínica e paciente para prestação de serviços psicológicos.', + E'

CONTRATO DE PRESTAÇÃO DE SERVIÇOS PSICOLÓGICOS

\n\n

Pelo presente instrumento particular, de um lado {{terapeuta_nome}}, Psicólogo(a), inscrito(a) no CRP sob o nº {{terapeuta_crp}}, CPF nº {{terapeuta_cpf}}, doravante denominado(a) CONTRATADO(A), e de outro lado {{paciente_nome}}, CPF nº {{paciente_cpf}}, doravante denominado(a) CONTRATANTE, têm entre si justo e contratado o seguinte:

\n\n

CLÁUSULA 1ª — DO OBJETO

\n

O presente contrato tem por objeto a prestação de serviços de atendimento psicológico clínico, na modalidade {{modalidade_atendimento}}, pelo(a) CONTRATADO(A) ao CONTRATANTE.

\n\n

CLÁUSULA 2ª — DA PERIODICIDADE

\n

As sessões ocorrerão com frequência {{frequencia_sessoes}}, com duração aproximada de {{duracao_sessao}} minutos, em dia e horário previamente acordados entre as partes.

\n\n

CLÁUSULA 3ª — DOS HONORÁRIOS

\n

O valor de cada sessão será de {{valor}} ({{valor_extenso}}), a ser pago {{forma_pagamento}}.

\n

Em caso de reajuste, o(a) CONTRATADO(A) comunicará o CONTRATANTE com antecedência mínima de 30 (trinta) dias.

\n\n

CLÁUSULA 4ª — DO CANCELAMENTO E FALTAS

\n

O cancelamento ou remarcação de sessões deverá ser comunicado com antecedência mínima de 24 (vinte e quatro) horas. Faltas sem aviso prévio serão cobradas integralmente.

\n\n

CLÁUSULA 5ª — DO SIGILO

\n

O(A) CONTRATADO(A) compromete-se a manter sigilo absoluto sobre todas as informações obtidas durante o atendimento, conforme o Código de Ética Profissional do Psicólogo (Resolução CFP nº 010/2005) e a Lei Geral de Proteção de Dados (Lei nº 13.709/2018).

\n\n

CLÁUSULA 6ª — DA RESCISÃO

\n

O presente contrato poderá ser rescindido por qualquer das partes, a qualquer tempo, mediante comunicação prévia, sem ônus adicionais além das sessões já realizadas.

\n\n

CLÁUSULA 7ª — DO FORO

\n

Para dirimir quaisquer controvérsias oriundas deste contrato, as partes elegem o foro da Comarca de {{cidade_estado}}.

\n\n

E por estarem assim justas e contratadas, as partes assinam o presente instrumento em 2 (duas) vias de igual teor e forma.

\n\n

{{cidade_estado}}, {{data_atual_extenso}}.

\n\n
\n
\n
\n {{terapeuta_nome}}
\n Psicólogo(a) — CRP {{terapeuta_crp}}\n
\n
\n
\n {{paciente_nome}}
\n CPF: {{paciente_cpf}}\n
\n
', + E'
\n {{clinica_nome}}
\n {{clinica_endereco}} · {{clinica_telefone}}\n
', + E'
\n {{clinica_nome}} · CNPJ: {{clinica_cnpj}} · {{clinica_endereco}}\n
', + ARRAY['paciente_nome','paciente_cpf','terapeuta_nome','terapeuta_crp','terapeuta_cpf','modalidade_atendimento','frequencia_sessoes','duracao_sessao','valor','valor_extenso','forma_pagamento','cidade_estado','data_atual_extenso','clinica_nome','clinica_endereco','clinica_telefone','clinica_cnpj'], + true, true +), + +-- ──────────────────────────────────────────────────────────── +-- 6. Termo de Consentimento Livre e Esclarecido (TCLE) +-- ──────────────────────────────────────────────────────────── +( + gen_random_uuid(), NULL, NULL, + 'Termo de Consentimento Livre e Esclarecido', + 'tcle', + 'TCLE para início de acompanhamento psicológico, conforme exigências éticas do CFP.', + E'

TERMO DE CONSENTIMENTO LIVRE E ESCLARECIDO

\n\n

Eu, {{paciente_nome}}, CPF nº {{paciente_cpf}}, declaro que fui devidamente informado(a) e esclarecido(a) pelo(a) psicólogo(a) {{terapeuta_nome}}, CRP {{terapeuta_crp}}, sobre os seguintes pontos:

\n\n

1. Natureza do serviço

\n

O atendimento psicológico consiste em sessões de psicoterapia, com abordagem {{abordagem_terapeutica}}, visando o acolhimento e o tratamento das demandas apresentadas.

\n\n

2. Sigilo profissional

\n

Todas as informações compartilhadas durante as sessões são estritamente confidenciais, conforme o Código de Ética Profissional do Psicólogo (Resolução CFP nº 010/2005), podendo ser quebrado apenas nas hipóteses previstas em lei.

\n\n

3. Registro de informações

\n

O(A) profissional poderá realizar registros das sessões (prontuário psicológico) para fins de acompanhamento clínico, mantidos em sigilo conforme a LGPD (Lei nº 13.709/2018) e as normas do CFP.

\n\n

4. Direitos do paciente

\n
    \n
  • Receber informações sobre o processo terapêutico;
  • \n
  • Interromper o atendimento a qualquer momento;
  • \n
  • Solicitar encaminhamento a outro profissional;
  • \n
  • Ter acesso às informações registradas em seu prontuário.
  • \n
\n\n

5. Limitações

\n

O acompanhamento psicológico não substitui tratamento médico ou psiquiátrico quando necessário. O(A) profissional poderá sugerir encaminhamentos complementares.

\n\n

Declaro que li e compreendi as informações acima e consinto livremente com o início do acompanhamento psicológico.

\n\n

{{cidade_estado}}, {{data_atual_extenso}}.

\n\n
\n
\n
\n {{paciente_nome}}
\n CPF: {{paciente_cpf}}\n
\n
\n
\n {{terapeuta_nome}}
\n Psicólogo(a) — CRP {{terapeuta_crp}}\n
\n
', + E'
\n {{clinica_nome}}
\n {{clinica_endereco}}\n
', + E'
\n Este termo é regido pela Resolução CFP nº 010/2005 e pela Lei nº 13.709/2018 (LGPD).\n
', + ARRAY['paciente_nome','paciente_cpf','terapeuta_nome','terapeuta_crp','abordagem_terapeutica','cidade_estado','data_atual_extenso','clinica_nome','clinica_endereco'], + true, true +), + +-- ──────────────────────────────────────────────────────────── +-- 7. Encaminhamento +-- ──────────────────────────────────────────────────────────── +( + gen_random_uuid(), NULL, NULL, + 'Encaminhamento', + 'encaminhamento', + 'Carta de encaminhamento do paciente para outro profissional ou serviço.', + E'

ENCAMINHAMENTO

\n\n

Ao(À) profissional/serviço de {{especialidade_destino}},

\n\n

Encaminho o(a) paciente {{paciente_nome}}, nascido(a) em {{paciente_data_nascimento}}, para avaliação e acompanhamento em {{especialidade_destino}}.

\n\n

Motivo do encaminhamento:

\n

{{motivo_encaminhamento}}

\n\n

Informações relevantes:

\n

{{informacoes_relevantes}}

\n\n

Coloco-me à disposição para troca de informações que se façam necessárias ao melhor atendimento do(a) paciente, respeitadas as normas de sigilo profissional.

\n\n

{{cidade_estado}}, {{data_atual_extenso}}.

\n\n
\n
\n {{terapeuta_nome}}
\n Psicólogo(a) — CRP {{terapeuta_crp}}
\n {{terapeuta_email}} · {{terapeuta_telefone}}\n
', + E'
\n {{clinica_nome}}
\n {{clinica_endereco}} · {{clinica_telefone}}\n
', + E'
\n {{clinica_nome}} · {{clinica_endereco}} · {{clinica_telefone}}\n
', + ARRAY['paciente_nome','paciente_data_nascimento','especialidade_destino','motivo_encaminhamento','informacoes_relevantes','cidade_estado','data_atual_extenso','terapeuta_nome','terapeuta_crp','terapeuta_email','terapeuta_telefone','clinica_nome','clinica_endereco','clinica_telefone'], + true, true +), + +-- ──────────────────────────────────────────────────────────── +-- 8. Autorização para Atendimento de Menor +-- ──────────────────────────────────────────────────────────── +( + gen_random_uuid(), NULL, NULL, + 'Autorização para Atendimento de Menor', + 'autorizacao_menor', + 'Termo de autorização dos responsáveis legais para atendimento psicológico de criança ou adolescente.', + E'

AUTORIZAÇÃO PARA ATENDIMENTO PSICOLÓGICO DE MENOR

\n\n

Eu, {{responsavel_nome}}, CPF nº {{responsavel_cpf}}, na qualidade de {{grau_parentesco}} e responsável legal do(a) menor {{paciente_nome}}, nascido(a) em {{paciente_data_nascimento}}, AUTORIZO a realização de atendimento psicológico pelo(a) profissional abaixo identificado(a).

\n\n

Profissional responsável

\n

{{terapeuta_nome}}
\nPsicólogo(a) — CRP {{terapeuta_crp}}

\n\n

Declarações

\n

Declaro estar ciente de que:

\n
    \n
  • O atendimento seguirá as normas do Código de Ética Profissional do Psicólogo e do Estatuto da Criança e do Adolescente (ECA);
  • \n
  • As informações do atendimento são sigilosas, sendo compartilhadas com o responsável apenas o necessário para o bem-estar do(a) menor, conforme julgamento técnico do(a) profissional;
  • \n
  • Posso solicitar informações sobre a evolução do tratamento a qualquer momento;
  • \n
  • Posso revogar esta autorização a qualquer tempo, mediante comunicação por escrito.
  • \n
\n\n

{{cidade_estado}}, {{data_atual_extenso}}.

\n\n
\n
\n
\n {{responsavel_nome}}
\n Responsável legal
\n CPF: {{responsavel_cpf}}\n
\n
\n
\n {{terapeuta_nome}}
\n Psicólogo(a) — CRP {{terapeuta_crp}}\n
\n
', + E'
\n {{clinica_nome}}
\n {{clinica_endereco}}\n
', + E'
\n {{clinica_nome}} · {{clinica_endereco}} · {{clinica_telefone}}\n
', + ARRAY['paciente_nome','paciente_data_nascimento','responsavel_nome','responsavel_cpf','grau_parentesco','terapeuta_nome','terapeuta_crp','cidade_estado','data_atual_extenso','clinica_nome','clinica_endereco','clinica_telefone'], + true, true +), + +-- ──────────────────────────────────────────────────────────── +-- 9. Laudo Psicológico +-- ──────────────────────────────────────────────────────────── +( + gen_random_uuid(), NULL, NULL, + 'Laudo Psicológico', + 'laudo_psicologico', + 'Modelo de laudo psicológico conforme Resolução CFP nº 06/2019.', + E'

LAUDO PSICOLÓGICO

\n\n\n \n \n \n \n \n \n \n
Solicitante:{{solicitante}}
Finalidade:{{finalidade}}
Avaliado(a):{{paciente_nome}}
Data de nascimento:{{paciente_data_nascimento}}
CPF:{{paciente_cpf}}
Profissional:{{terapeuta_nome}} — CRP {{terapeuta_crp}}
Data do laudo:{{data_atual}}
\n\n

1. Descrição da demanda

\n

[Apresente a demanda e o motivo da avaliação, conforme solicitação recebida.]

\n\n

2. Procedimentos

\n

[Descreva os recursos e instrumentos técnicos utilizados na avaliação: entrevistas, testes psicológicos (com nome, autor e parecer favorável do SATEPSI quando aplicável), observação, etc.]

\n\n

3. Análise

\n

[Apresente de forma clara e fundamentada os dados obtidos, integrando os resultados dos procedimentos realizados à luz da literatura científica da Psicologia. Não inclua informações que não tenham relação com a demanda.]

\n\n

4. Conclusão

\n

[Apresente o resultado da avaliação, indicando a resposta à demanda inicial. A conclusão deve ser coerente com a análise e os procedimentos utilizados.]

\n\n

Este laudo foi elaborado em conformidade com a Resolução CFP nº 06/2019, que institui regras para a elaboração de documentos escritos produzidos pelo psicólogo no exercício profissional.

\n\n

{{cidade_estado}}, {{data_atual_extenso}}.

\n\n
\n
\n {{terapeuta_nome}}
\n Psicólogo(a) — CRP {{terapeuta_crp}}\n
', + E'
\n {{clinica_nome}}
\n {{clinica_endereco}} · {{clinica_telefone}}\n
', + E'
\n Este documento é de caráter sigiloso, conforme Art. 9º do Código de Ética do Psicólogo e Resolução CFP nº 06/2019.\n
', + ARRAY['paciente_nome','paciente_data_nascimento','paciente_cpf','solicitante','finalidade','terapeuta_nome','terapeuta_crp','data_atual','data_atual_extenso','cidade_estado','clinica_nome','clinica_endereco','clinica_telefone'], + true, true +), + +-- ──────────────────────────────────────────────────────────── +-- 10. Parecer Psicológico +-- ──────────────────────────────────────────────────────────── +( + gen_random_uuid(), NULL, NULL, + 'Parecer Psicológico', + 'parecer_psicologico', + 'Manifestação técnica sobre questão específica no campo da Psicologia, conforme Resolução CFP nº 06/2019.', + E'

PARECER PSICOLÓGICO

\n\n\n \n \n \n \n
Parecer nº:{{numero_parecer}}
Solicitante:{{solicitante}}
Assunto:{{assunto}}
Profissional:{{terapeuta_nome}} — CRP {{terapeuta_crp}}
\n\n

1. Exposição de motivos

\n

[Descreva a questão ou consulta que originou o pedido de parecer, indicando quem solicitou e com qual finalidade.]

\n\n

2. Análise fundamentada

\n

[Apresente a análise técnica, com base em referencial teórico-científico da Psicologia, normas do CFP e legislação pertinente. O parecer deve se restringir ao campo de conhecimento do psicólogo.]

\n\n

3. Conclusão

\n

[Apresente a resposta técnica à questão formulada, de forma objetiva e fundamentada.]

\n\n

Parecer elaborado em conformidade com a Resolução CFP nº 06/2019.

\n\n

{{cidade_estado}}, {{data_atual_extenso}}.

\n\n
\n
\n {{terapeuta_nome}}
\n Psicólogo(a) — CRP {{terapeuta_crp}}\n
', + E'
\n {{clinica_nome}}
\n {{clinica_endereco}}\n
', + E'
\n Este documento é de caráter sigiloso, conforme Resolução CFP nº 06/2019.\n
', + ARRAY['numero_parecer','solicitante','assunto','terapeuta_nome','terapeuta_crp','cidade_estado','data_atual_extenso','clinica_nome','clinica_endereco'], + true, true +), + +-- ──────────────────────────────────────────────────────────── +-- 11. Termo de Sigilo e Confidencialidade +-- ──────────────────────────────────────────────────────────── +( + gen_random_uuid(), NULL, NULL, + 'Termo de Sigilo e Confidencialidade', + 'termo_sigilo', + 'Termo reforçando o compromisso de sigilo entre profissional e paciente.', + E'

TERMO DE SIGILO E CONFIDENCIALIDADE

\n\n

Eu, {{terapeuta_nome}}, Psicólogo(a), CRP {{terapeuta_crp}}, declaro ao(à) paciente {{paciente_nome}}, CPF nº {{paciente_cpf}}, que:

\n\n
    \n
  1. Todas as informações verbais, escritas ou de qualquer natureza, obtidas durante o processo terapêutico, serão mantidas em sigilo absoluto;
  2. \n
  3. O prontuário psicológico é de acesso exclusivo do profissional e do paciente, nos termos da Resolução CFP nº 001/2009;
  4. \n
  5. Os dados pessoais serão tratados conforme a Lei Geral de Proteção de Dados (Lei nº 13.709/2018), sendo utilizados exclusivamente para fins de acompanhamento clínico;
  6. \n
  7. A quebra de sigilo somente ocorrerá nas hipóteses previstas no Código de Ética do Psicólogo:\n
      \n
    • Situações de risco à vida do paciente ou de terceiros;
    • \n
    • Determinação judicial;
    • \n
    • Atendimento de menor — informações necessárias aos responsáveis;
    • \n
    \n
  8. \n
  9. Dados anonimizados poderão ser utilizados para fins de estudo ou pesquisa, mediante consentimento prévio específico.
  10. \n
\n\n

Este termo entra em vigor na data de sua assinatura e permanece válido mesmo após o encerramento do acompanhamento.

\n\n

{{cidade_estado}}, {{data_atual_extenso}}.

\n\n
\n
\n
\n {{terapeuta_nome}}
\n Psicólogo(a) — CRP {{terapeuta_crp}}\n
\n
\n
\n {{paciente_nome}}
\n CPF: {{paciente_cpf}}\n
\n
', + E'
\n {{clinica_nome}}
\n {{clinica_endereco}}\n
', + E'
\n Documento regido pelo Código de Ética Profissional do Psicólogo e pela LGPD (Lei nº 13.709/2018).\n
', + ARRAY['paciente_nome','paciente_cpf','terapeuta_nome','terapeuta_crp','cidade_estado','data_atual_extenso','clinica_nome','clinica_endereco'], + true, true +), + +-- ──────────────────────────────────────────────────────────── +-- 12. Declaração de Início de Tratamento +-- ──────────────────────────────────────────────────────────── +( + gen_random_uuid(), NULL, NULL, + 'Declaração de Início de Tratamento', + 'declaracao_inicio_tratamento', + 'Declara que o paciente iniciou acompanhamento psicológico a partir de determinada data.', + E'

DECLARAÇÃO DE INÍCIO DE TRATAMENTO

\n\n

Declaro, para os devidos fins, que {{paciente_nome}}, portador(a) do CPF nº {{paciente_cpf}}, encontra-se em acompanhamento psicológico neste consultório/clínica desde {{data_inicio_tratamento}}, com frequência {{frequencia_sessoes}}.

\n\n

O presente documento é expedido a pedido do(a) interessado(a) e não contém informações de caráter diagnóstico ou sigiloso.

\n\n

{{cidade_estado}}, {{data_atual_extenso}}.

\n\n
\n
\n {{terapeuta_nome}}
\n Psicólogo(a) — CRP {{terapeuta_crp}}\n
', + E'
\n {{clinica_nome}}
\n {{clinica_endereco}} · {{clinica_telefone}}\n
', + E'
\n {{clinica_nome}} · {{clinica_endereco}} · {{clinica_telefone}}\n
', + ARRAY['paciente_nome','paciente_cpf','data_inicio_tratamento','frequencia_sessoes','cidade_estado','data_atual_extenso','terapeuta_nome','terapeuta_crp','clinica_nome','clinica_endereco','clinica_telefone'], + true, true +), + +-- ──────────────────────────────────────────────────────────── +-- 13. Termo de Alta Terapêutica +-- ──────────────────────────────────────────────────────────── +( + gen_random_uuid(), NULL, NULL, + 'Termo de Alta Terapêutica', + 'termo_alta', + 'Documento formalizando o encerramento do acompanhamento psicológico.', + E'

TERMO DE ALTA TERAPÊUTICA

\n\n

Comunico que o(a) paciente {{paciente_nome}}, CPF nº {{paciente_cpf}}, que esteve em acompanhamento psicológico desde {{data_inicio_tratamento}}, recebe alta terapêutica nesta data.

\n\n

Motivo da alta

\n

{{motivo_alta}}

\n\n

Orientações

\n

{{orientacoes_pos_alta}}

\n\n

O(A) paciente foi informado(a) de que poderá retornar ao acompanhamento a qualquer momento, caso considere necessário.

\n\n

{{cidade_estado}}, {{data_atual_extenso}}.

\n\n
\n
\n
\n {{terapeuta_nome}}
\n Psicólogo(a) — CRP {{terapeuta_crp}}\n
\n
\n
\n {{paciente_nome}}
\n Ciente\n
\n
', + E'
\n {{clinica_nome}}
\n {{clinica_endereco}}\n
', + E'
\n {{clinica_nome}} · {{clinica_endereco}} · {{clinica_telefone}}\n
', + ARRAY['paciente_nome','paciente_cpf','data_inicio_tratamento','motivo_alta','orientacoes_pos_alta','cidade_estado','data_atual_extenso','terapeuta_nome','terapeuta_crp','clinica_nome','clinica_endereco','clinica_telefone'], + true, true +), + +-- ──────────────────────────────────────────────────────────── +-- 14. Termo de Consentimento para Atendimento Online +-- ──────────────────────────────────────────────────────────── +( + gen_random_uuid(), NULL, NULL, + 'Termo de Consentimento para Atendimento Online', + 'tcle_online', + 'Consentimento específico para atendimento psicológico por meios tecnológicos (Resolução CFP nº 11/2018).', + E'

TERMO DE CONSENTIMENTO PARA ATENDIMENTO PSICOLÓGICO ONLINE

\n\n

Eu, {{paciente_nome}}, CPF nº {{paciente_cpf}}, declaro que fui informado(a) e concordo com a realização de atendimento psicológico na modalidade online, por meio de Tecnologias da Informação e Comunicação (TICs), conforme a Resolução CFP nº 11/2018.

\n\n

1. Plataforma utilizada

\n

As sessões serão realizadas por meio de {{plataforma_online}}, devendo ambas as partes garantir ambiente adequado, com privacidade e conexão estável.

\n\n

2. Condições do atendimento

\n
    \n
  • O(A) profissional está cadastrado(a) no e-Psi (plataforma do CFP) para prestação de serviços psicológicos online;
  • \n
  • Todas as regras de sigilo profissional aplicam-se igualmente ao atendimento online;
  • \n
  • A plataforma utilizada oferece criptografia e segurança dos dados;
  • \n
  • Não é permitida a gravação das sessões por nenhuma das partes, salvo acordo prévio por escrito.
  • \n
\n\n

3. Situações excepcionais

\n

Em caso de instabilidade técnica que comprometa a sessão, o(a) profissional e o(a) paciente acordarão a melhor forma de dar continuidade (reagendar, trocar de plataforma, ou realizar sessão presencial).

\n\n

4. Limitações

\n

O(A) profissional reserva-se o direito de indicar atendimento presencial quando avaliar que a modalidade online não é adequada ao caso clínico.

\n\n

Declaro que compreendi as condições acima e consinto com a realização das sessões na modalidade online.

\n\n

{{cidade_estado}}, {{data_atual_extenso}}.

\n\n
\n
\n
\n {{paciente_nome}}
\n CPF: {{paciente_cpf}}\n
\n
\n
\n {{terapeuta_nome}}
\n Psicólogo(a) — CRP {{terapeuta_crp}}\n
\n
', + E'
\n {{clinica_nome}}
\n {{clinica_endereco}}\n
', + E'
\n Atendimento online regulamentado pela Resolução CFP nº 11/2018. Profissional cadastrado(a) no e-Psi.\n
', + ARRAY['paciente_nome','paciente_cpf','plataforma_online','terapeuta_nome','terapeuta_crp','cidade_estado','data_atual_extenso','clinica_nome','clinica_endereco'], + true, true +) + +ON CONFLICT DO NOTHING; + +-- ========================================================================== +-- FIM DO SEED — 14 templates globais +-- ========================================================================== diff --git a/docs/architecture/Documentos e arquivos/documentos_arquivos_top_de_linha.html b/docs/architecture/Documentos e arquivos/documentos_arquivos_top_de_linha.html new file mode 100644 index 0000000..612e33c --- /dev/null +++ b/docs/architecture/Documentos e arquivos/documentos_arquivos_top_de_linha.html @@ -0,0 +1,766 @@ + + + + + +Documentos & Arquivos — Status de Implementacao + + + + + + + + +
+
+
6/6
+
Tabelas
+
+
+
2/2
+
Buckets
+
+
+
7/7
+
Services
+
+
+
3/3
+
Composables
+
+
+
10/10
+
Componentes
+
+
+
14
+
Templates seed
+
+
+ + +
+
Implementado
+
Parcial / Migration pendente
+
Nao implementado
+
+ + +
+
+
1
+
Upload & Organizacao de Arquivos
+
+ +
+
+ +
+
+
Upload de arquivo ao paciente
+ pronto +
+
PDF, imagem, DOCX. Vinculado ao patient_id. Supabase Storage com path estruturado. Drag & drop + seletor. Validacao de tamanho (50MB) e tipo MIME.
+
+ patient_idbucket_pathstorage_bucket + nome_originalmime_typetamanho_bytes + uploaded_byuploaded_at +
+
Documents.service.js → uploadDocument()
+
+ +
+
+
Tipo, categoria & tags
+ pronto +
+
11 tipos (laudo, receita, exame, atestado, declaracao, recibo, etc.). Categoria livre. Tags[] com autocomplete. Filtros na listagem.
+
+ tipo_documentocategoriadescricaotags[] +
+
DB: CHECK constraint + GIN index em tags
+
+ +
+
+
Vinculo com sessao
+ pronto +
+
Arquivo linkado a agenda_eventos (sessao) ou session_note. Colunas nullable — nem todo arquivo tem sessao.
+
+ agenda_evento_idsession_note_id +
+
DB: FK para agenda_eventos (ON DELETE SET NULL)
+
+ +
+
+
Visibilidade & controle de acesso
+ pronto +
+
Privado, compartilhado com supervisor, ou visivel no portal do paciente. Granular por arquivo. Expiracao de compartilhamento.
+
+ visibilidadecompartilhado_portalcompartilhado_supervisor + compartilhado_emexpira_compartilhamento +
+
DB: CHECK (privado | compartilhado_supervisor | compartilhado_portal)
+
+ +
+
+
Soft delete com retencao LGPD
+ pronto +
+
Arquivo "excluido" some da UI mas fica retido por 5 anos (CFP). Colunas de controle + index parcial para listagem ativa.
+
+ deleted_atdeleted_byretencao_ate +
+
DB: idx_documents_active (WHERE deleted_at IS NULL)
+
+ +
+
+
Preview & download
+ pronto +
+
Preview inline de PDF e imagens via dialog. Download com URL assinada (60s). Suporte a storage_bucket dinamico (documents ou generated-docs).
+
DocumentPreviewDialog.vue + getDownloadUrl(path, expires, bucket)
+
+ +
+ + + +
+
+ + +
+
+
2
+
Geracao de Documentos (PDF)
+
+ +
+
+ +
+
+
Templates de documentos
+ pronto +
+
16 tipos de template. 14 templates globais no seed. Corpo HTML com {{variaveis}}. Cabecalho/rodape personalizaveis. Templates por tenant + globais do sistema.
+
+ nome_templatetipocorpo_html + cabecalho_htmlrodape_htmlvariaveis[] + is_globallogo_urlativo +
+
document_templates (DB) + seed_015_document_templates.sql
+
+ +
+
+
Geracao de PDF (client-side)
+ pronto +
+
jsPDF + html2canvas-pro (substituiu pdfmake por incompatibilidade com Vite). Renderiza HTML preenchido em canvas, converte para PDF A4 com paginacao. JPEG 85%, scale 1.5. ~200-400KB por documento.
+
+ buildFullHtml()htmlToPdfBlob()fillTemplate() +
+
pdf.service.js + DocumentGenerate.service.js
+
+ +
+
+
Documento gerado (instancia + listagem)
+ pronto +
+
Cada PDF gerado: salva snapshot em document_generated (dados preenchidos para auditoria) E automaticamente registra na tabela documents (para aparecer na listagem do paciente). Bucket: generated-docs. Nomes sanitizados (sem acentos).
+
+ template_iddados_preenchidospdf_path + gerado_emgerado_por→ documents +
+
saveGeneratedDocument() → document_generated + documents
+
+ +
+
+
Fluxo de geracao (UI)
+ pronto +
+
Dialog 3 etapas: selecionar template → editar variaveis (auto-preenchidas com dados paciente/sessao/terapeuta/clinica) → preview via iframe sandbox → "Salvar documento" (online) ou "So baixar" (local).
+
DocumentGenerateDialog.vue + useDocumentGenerate.js
+
+ +
+
+
Dados da clinica no template
+ parcial +
+
loadClinicData() usa select('*') na tabela tenants. Atualmente so retorna name. Campos phone, contact_email, logradouro, numero, bairro, cidade, estado dependem da migration 003_tenants_address_fields.sql ser aplicada.
+
Migration pendente: 003_tenants_address_fields.sql
+
+ +
+
+
Editor de templates
+ pronto +
+
Editor rich text para corpo HTML. Insercao de variaveis via dropdown. Preview ao vivo. Config de cabecalho/rodape/logo. Gestao de templates globais e por tenant.
+
DocumentTemplateEditor.vue + DocumentTemplatesPage.vue
+
+ +
+ + + +
+
+ + +
+
+
3
+
Assinatura Eletronica
+
+ +
+
+ +
+
+
TCLE & consentimento
+ db pronto +
+
Tabela document_signatures com rastreamento completo: IP, timestamp, hash SHA-256, user_agent. Suporte a 3 tipos de signatario (paciente, responsavel_legal, terapeuta). 5 status possiveis.
+
+ documento_idsignatario_tiposignatario_id + ordemstatusip + hash_documentoassinado_em +
+
document_signatures (DB) + DocumentSignatures.service.js
+
+ +
+
+
UI de assinatura
+ parcial +
+
Componente DocumentSignatureDialog.vue existe. Service DocumentSignatures.service.js existe. Fluxo completo de envio por link e assinatura pelo paciente ainda precisa ser validado end-to-end.
+
DocumentSignatureDialog.vue
+
+ +
+ + + +
+
+ + +
+
+
4
+
Compartilhamento & Portal do Paciente
+
+ +
+
+ +
+
+
Links temporarios de acesso
+ pronto +
+
Token hex 32 bytes, prazo de expiracao, limite de usos. RLS publica por token valido. Link seguro sem necessidade de login.
+
+ tokenexpira_emusos_max + usosativocriado_por +
+
document_share_links (DB) + DocumentShareLinks.service.js
+
+ +
+
+
Documentos compartilhados com paciente
+ db pronto +
+
Terapeuta decide quais arquivos ficam visiveis pro paciente. Campos compartilhado_portal e expira_compartilhamento na tabela documents.
+
+ compartilhado_portalcompartilhado_emexpira_compartilhamento +
+
+ +
+
+
Upload pelo paciente
+ db pronto +
+
Paciente envia exames/laudos pelo portal. Fila de "pendentes de revisao" para o terapeuta aprovar.
+
+ enviado_pelo_pacientestatus_revisao + revisado_porrevisado_em +
+
+ +
+ + + +
+
+ + +
+
+
5
+
Auditoria & Conformidade
+
+ +
+
+ +
+
+
Log de acesso a arquivos
+ pronto +
+
Tabela imutavel (somente INSERT + SELECT, sem UPDATE/DELETE). Cada visualizacao ou download registrado. Conformidade CFP e LGPD. Integrado no composable useDocuments (logAccess automatico).
+
+ documento_idacaouser_id + ipuser_agentacessado_em +
+
document_access_logs (DB) + DocumentAuditLog.service.js
+
+ +
+
+
Timeline do paciente
+ pronto +
+
Triggers automaticos registram na patient_timeline quando: documento uploadado (INSERT em documents) e documento assinado (UPDATE em document_signatures).
+
DB Triggers: trg_documents_timeline_insert + trg_ds_timeline
+
+ +
+ + + +
+
+ + +
+
+
!
+
Pendencias & Migrations Nao Aplicadas
+
+ +
+
+
+
Migration: tenants address fields
+ pendente +
+
003_tenants_address_fields.sql — adiciona cep, logradouro, numero, complemento, bairro, cidade, estado a tabela tenants. Tambem faltam phone e contact_email. Necessario para preencher variaveis clinica_endereco, clinica_telefone nos templates.
+
+ +
+
+
Campo CRP do terapeuta
+ pendente +
+
Variavel terapeuta_crp nos templates retorna vazio. O campo CRP nao existe na tabela profiles nem em tenant_members. Precisa de migration para adicionar coluna crp em profiles.
+
+ +
+
+
Fluxo de assinatura end-to-end
+ validar +
+
Tabela, service e componente existem. Falta validar: envio de link por email/whatsapp, pagina publica de assinatura, registro de IP/hash, notificacao ao terapeuta quando assinado.
+
+ +
+
+
Portal do paciente — visualizacao de docs
+ validar +
+
Campos compartilhado_portal e visibilidade existem no banco. SharedDocumentPage.vue existe. Falta validar se o portal do paciente (CadastroPacienteExterno) exibe corretamente os documentos compartilhados.
+
+
+ +
+ Decisao tecnica — Motor PDF: pdfmake foi substituido por jsPDF + html2canvas-pro. O pdfmake (UMD) trava silenciosamente com Vite (ESM) — createPdf().getBlob()/getBuffer() nunca retornam. html2canvas-pro e um fork open source (MIT) com suporte a cores oklch usadas pelo PrimeVue/Tailwind. +
+
+ + + diff --git a/docs/architecture/Documentos e arquivos/plano_implementacao_documentos.html b/docs/architecture/Documentos e arquivos/plano_implementacao_documentos.html new file mode 100644 index 0000000..ad91437 --- /dev/null +++ b/docs/architecture/Documentos e arquivos/plano_implementacao_documentos.html @@ -0,0 +1,870 @@ + + + + + +Plano de Implementacao — Modulo Documentos & Arquivos + + + + + + + + +
+
+
6
+
Tabelas
+
+
+
2
+
Buckets
+
+
+
7
+
Services
+
+
+
3
+
Composables
+
+
+
~10
+
Componentes
+
+
+
5
+
Feature flags
+
+
+ + +
+
+
1
+
Banco de Dados — Migrations
+
+
Tabelas, RLS policies, indexes, triggers
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MigrationTabela / ObjetoO que fazCampos principais
005_create_documents_tables.sqldocumentsArquivo vinculado a paciente. Path no Supabase Storage, tipo/categoria, visibilidade, tags, soft delete com retencao LGPD. Tabela central do modulo. O campo storage_bucket indica qual bucket do Storage contem o arquivo (documents ou generated-docs), permitindo que PDFs gerados aparecam na mesma listagem. +
+ id + patient_id + tenant_id + owner_id + bucket_path + storage_bucket + nome_original + mime_type + tamanho_bytes + tipo_documento + categoria + descricao + tags[] + visibilidade + compartilhado_portal + compartilhado_supervisor + agenda_evento_id + session_note_id + enviado_pelo_paciente + status_revisao + revisado_por + revisado_em + uploaded_by + uploaded_at + deleted_at + deleted_by + retencao_ate +
+
document_access_logsLog imutavel de quem visualizou ou baixou cada arquivo. Conformidade CFP e LGPD. Sem UPDATE/DELETE — somente INSERT e SELECT. +
+ id + documento_id + acao + user_id + ip + user_agent + acessado_em +
+
document_signaturesAssinaturas eletronicas. Cada signatario (paciente, responsavel, terapeuta) tem seu registro com IP, timestamp e hash do documento. +
+ id + documento_id + signatario_tipo + signatario_id + ordem + status + ip + user_agent + assinado_em + hash_documento +
+
document_share_linksLinks temporarios assinados para compartilhar documento com profissional externo sem conta no sistema. Prazo e limite de usos. +
+ id + documento_id + token + expira_em + usos_max + usos + criado_por + criado_em +
+
006_create_document_templates.sqldocument_templatesTemplates de documentos (declaracao de comparecimento, atestado, recibo etc.). Corpo HTML com variaveis. Templates globais do sistema + personalizados por tenant com logo/cabecalho. +
+ id + tenant_id + nome_template + tipo + corpo_html + variaveis[] + is_global + owner_id + logo_url + cabecalho_html + rodape_html + ativo +
+
document_generatedCada PDF gerado a partir de um template. Guarda os dados usados no preenchimento e o path do PDF resultante no Storage. +
+ id + template_id + patient_id + tenant_id + dados_preenchidos + pdf_path + gerado_em + gerado_por +
+
+
+ + +
+
+
2
+
Supabase Storage — Buckets
+
+ + + + + + + + + + + + + + + + + + + + + +
BucketUsoPath pattern
documentsArquivos enviados por terapeuta ou paciente (PDF, imagem, DOCX, etc.){tenant_id}/{patient_id}/{timestamp}-{filename}
generated-docsPDFs gerados pelo sistema a partir de templates. Referenciado tanto por document_generated (snapshot) quanto por documents (listagem do paciente) via campo storage_bucket.{tenant_id}/{patient_id}/{template_nome_sanitizado}_{timestamp}.pdf
+
+ + +
+
+
3
+
Services — Camada de dados
+
+
src/services/ — seguem o padrao Medicos.service.js (getOwnerId + getActiveTenantId + CRUD)
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ArquivoO que faz
Documents.service.jsCRUD completo de documentos: upload ao Storage + insert no banco, listagem por paciente com filtros (tipo, categoria, tags), soft delete com retencao, restauracao, download com URL assinada
DocumentTemplates.service.jsCRUD de templates: criar/editar templates (globais e por tenant), listar variaveis disponiveis, duplicar template, ativar/desativar
DocumentGenerate.service.jsGerar PDF a partir de template: preencher variaveis com dados do paciente/sessao, renderizar HTML para PDF via pdf.service.js (jsPDF + html2canvas-pro), salvar no bucket generated-docs, registrar em document_generated E automaticamente na tabela documents (para aparecer na listagem do paciente). Nomes de arquivo sanitizados (sem acentos) para compatibilidade com Supabase Storage.
pdf.service.jsServico de geracao de PDF client-side usando jsPDF + html2canvas-pro. Substitui pdfmake que apresenta incompatibilidade com Vite (UMD vs ESM — getBlob/getBuffer travam silenciosamente). Recebe HTML completo, renderiza em canvas oculto (scale 1.5, JPEG 85%), gera PDF A4 com paginacao automatica. Retorna Blob para upload/download.
DocumentSignatures.service.jsCriar solicitacao de assinatura, registrar assinatura (IP, hash, timestamp, user_agent), consultar status de cada signatario, verificar integridade via hash
DocumentShareLinks.service.jsGerar link temporario com token, validar token no acesso, registrar uso, expirar link
DocumentAuditLog.service.jsRegistrar log de acesso (visualizacao/download) e consultar historico de acessos por documento
+
+ + +
+
+
4
+
Composables — Logica reativa
+
+
src/features/documents/composables/
+ + + + + + + + + + + + + + + + + + + + + + +
ArquivoO que faz
useDocuments.jsState reativo: lista de documentos do paciente, loading, filtros ativos (tipo, categoria, tags), operacoes CRUD, refresh automatico apos upload/delete
useDocumentTemplates.jsState reativo: lista de templates disponiveis (globais + tenant), preview com dados ficticios, variaveis extraidas do corpo HTML
useDocumentGenerate.jsLogica de geracao: carregar dados do paciente/sessao, mapear variaveis, chamar servico de geracao, retornar URL do PDF
+
+ + +
+
+
5
+
Paginas & Componentes Vue
+
+
src/features/documents/
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ArquivoTipoO que faz
DocumentsListPage.vuePaginaPagina principal — lista todos os documentos do paciente com DataTable, filtros (tipo, categoria, tags), botoes de upload, preview, download. Hero header sticky com stats rapidos.
DocumentUploadDialog.vueDialogUpload de arquivo — drag & drop ou seletor, campos: tipo do documento, categoria, descricao, tags, vinculo com sessao (opcional), visibilidade. Validacao de tamanho e tipo de arquivo.
DocumentPreviewDialog.vueDialogPreview inline — renderiza PDF/imagem no dialog. Botoes: download, compartilhar, solicitar assinatura, excluir. Exibe metadados (tipo, tags, quem enviou, data).
DocumentTemplatesPage.vuePaginaGestao de templates — lista templates disponiveis (globais + do tenant), criar novo, editar, duplicar, ativar/desativar. Cards com preview do template.
DocumentTemplateEditor.vueComponenteEditor de template — edicao do corpo HTML (editor rich text), insercao de variaveis via dropdown, preview ao vivo com dados ficticios, config de cabecalho/rodape/logo.
DocumentGenerateDialog.vueDialogGerar documento — selecionar template, campos preenchidos automaticamente com dados do paciente/sessao, edicao manual se necessario, preview final via iframe sandbox, botao "Salvar documento" (salva online, sem download automatico). Botao "So baixar" gera PDF local sem salvar no banco.
DocumentSignatureDialog.vueDialogSolicitar assinatura — adicionar signatarios (paciente, responsavel, terapeuta), definir ordem, enviar link por email/whatsapp, acompanhar status de cada signatario.
DocumentShareDialog.vueDialogCompartilhar — gerar link temporario com prazo (24h, 48h, 7d) e limite de usos, copiar link, enviar por email. Exibe links ja criados com status.
components/DocumentCard.vueComponenteCard reutilizavel de documento — thumbnail (icone por tipo ou preview de imagem), nome, tipo, data, tags, menu de acoes (3 dots).
components/DocumentTagsInput.vueComponenteInput de tags livres — chips editaveis com autocomplete baseado em tags ja usadas pelo terapeuta. Criacao de novas tags inline.
+
+ + +
+
+
6
+
Integracao com Prontuario (arquivo existente)
+
+ + + + + + + + + + + + + + +
Arquivo existenteAlteracao
src/features/patients/prontuario/PatientProntuario.vueAdicionar aba/secao "Documentos" que renderiza DocumentsListPage filtrada pelo patient_id atual. Botao rapido de upload direto do prontuario.
+
+ + +
+
+
7
+
Rotas
+
+
Adicionadas em routes.therapist.js e routes.clinic.js
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RotaPaginaDescricao
/therapist/documentsDocumentsListPage.vueLista geral de documentos (todos os pacientes do terapeuta)
/therapist/documents/templatesDocumentTemplatesPage.vueGestao de templates do terapeuta
/therapist/patients/:id/documentsDocumentsListPage.vueDocumentos de um paciente especifico (via props)
/clinic/documents/templatesDocumentTemplatesPage.vueTemplates da clinica (admin configura templates compartilhados)
+
+ + +
+
+
8
+
Menus de Navegacao
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
ArquivoItem adicionadoOnde no menu
therapist.menu.js"Documentos" — icon: pi-file, to: /therapist/documentsGrupo "Pacientes", abaixo de "Tags"
therapist.menu.js"Templates" — icon: pi-file-edit, to: /therapist/documents/templatesSub-item de Documentos
clinic.menu.js"Templates de Documentos" — icon: pi-file-edit, to: /clinic/documents/templatesGrupo "Configuracoes"
+
+ + +
+
+
9
+
SaaS — Feature Flags
+
+
Inseridas em saas_features e vinculadas aos planos via plan_features
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Feature keyDescricaoPlanos
documents.uploadUpload de arquivos a pacientes — funcionalidade baseFree + Pro
documents.templatesTemplates de documentos (declaracao, atestado, recibo etc.)Pro
documents.signaturesAssinatura eletronica (TCLE, consentimentos)Pro
documents.share_linksLinks temporarios para compartilhamento externoPro
documents.patient_portalPaciente visualiza e envia documentos pelo portalPro
+
+ + +
+
+
10
+
Seed Data — Templates Padrao
+
+ + + + + + + + + + + + + + +
ArquivoO que insere
seed_015_document_templates.sql + 4 templates globais (is_global = true) com corpo HTML e variaveis mapeadas: +
+ Declaracao de Comparecimento + Atestado Psicologico + Relatorio de Acompanhamento + Recibo de Pagamento +
+
+ +
+ Variaveis dos templates: {{paciente_nome}}, {{paciente_cpf}}, {{data_sessao}}, {{hora_inicio}}, {{hora_fim}}, {{terapeuta_nome}}, {{terapeuta_crp}}, {{clinica_nome}}, {{clinica_endereco}}, {{valor}}, {{data_atual}}, entre outras. Cada template define quais variaveis utiliza no campo variaveis[]. +
+ +
+ Decisao tecnica — Motor PDF: pdfmake foi substituido por jsPDF + html2canvas-pro. O pdfmake (UMD) trava silenciosamente com Vite (ESM) — createPdf().getBlob()/getBuffer() nunca retornam, mesmo com optimizeDeps configurado. A solucao final usa html2canvas-pro (fork com suporte a cores oklch do PrimeVue/Tailwind) para renderizar o HTML preenchido em canvas, e jsPDF para converter em PDF A4 com paginacao. Resultado: ~200-400KB por documento (JPEG 85%, scale 1.5). +
+
+ + +
+
+
!
+
Ordem de Execucao Sugerida
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FaseO queDepende de
1Migrations (tabelas, RLS, triggers, indexes)
2Buckets no Supabase StorageFase 1
3Services (camada de dados)Fase 1 + 2
4Composables (logica reativa)Fase 3
5Componentes e Paginas VueFase 4
6Rotas, menus, feature flagsFase 5
7Integracao com ProntuarioFase 5
8Seed data (templates padrao)Fase 1
+
+ + + diff --git a/docs/architecture/Pacientes/cadastro_pacientes_levantamento.html b/docs/architecture/Pacientes/cadastro_pacientes_levantamento.html new file mode 100644 index 0000000..e369ea3 --- /dev/null +++ b/docs/architecture/Pacientes/cadastro_pacientes_levantamento.html @@ -0,0 +1,372 @@ + + + +
+ +
+
você já tem
+
tem parcialmente
+
faltando
+
diferencial de mercado
+
+ +
+
1 · Identificação & dados pessoais
+
+ +
+
+
Dados básicos de identificação
núcleo do cadastro
+ completo +
+
Nome, email, telefone, data de nascimento, CPF, RG, gênero, naturalidade, estado civil, escolaridade e profissão.
+
+ nome_completoemail_principaltelefonedata_nascimentocpfrggeneroestado_civilescolaridadeprofissao +
+
+ +
+
+
Gênero & pronomes
campo genero existe, pronomes não
+ parcial +
+
Você tem o campo genero como texto livre. Faltam pronomes preferidos (ele/ela/eles) — padrão nos sistemas modernos de saúde mental, especialmente para público LGBTQIA+.
+
+ genero + pronomes + nome_social +
+
+ +
+
+
Endereço completo
CEP, cidade, estado, complemento
+ completo +
+
CEP, endereço, número, bairro, complemento, cidade, estado e país. Estrutura adequada.
+
+ cependereconumerobairrocidadeestadopais +
+
+ +
+
+
Dados socioeconômicos
renda e contexto social
+ faltando +
+
Faixa de renda, religião/espiritualidade, etnia. Campos opcionais mas relevantes clinicamente e para política de precificação solidária. SimplePractice e Psicologia Viva coletam isso.
+
+ faixa_renda + etnia + religiao +
+
+ +
+
+ +
+
2 · Contatos & rede de suporte
+
+ +
+
+
Contato de emergência
só um contato, sem estrutura
+ parcial +
+
Você tem nome_parente, grau_parentesco e telefone_parente como campos soltos na tabela. Falta suporte a múltiplos contatos e campo de email do contato.
+
+ nome_parentegrau_parentescotelefone_parente + email_contatomultiplos_contatoscontato_primario +
+
Ideal: tabela separada patient_contacts com N contatos por paciente.
+
+ +
+
+
Responsável legal
para menores de idade
+ completo +
+
Nome, CPF, telefone do responsável e flag de cobrança no responsável. Cobre bem o caso de pacientes menores.
+
+ nome_responsaveltelefone_responsavelcpf_responsavelcobranca_no_responsavel +
+
+ +
+
+
Outros profissionais de saúde
psiquiatra, médico, nutricionista
+ faltando +
+
Nome e contato do psiquiatra, médico ou outros profissionais que acompanham o paciente. Essencial para coordenação de cuidados. Presente no SimplePractice e TheraNest.
+
+ nome_profissionalespecialidadetelefone_profissionalemail_profissional +
+
+ +
+
+
Preferências de comunicação
como o paciente quer ser contatado
+ diferencial +
+
Canal preferido (WhatsApp, email, SMS), horário preferido para contato, idioma preferido. Alimenta diretamente os lembretes automáticos com as preferências do paciente.
+
+ canal_preferidohorario_contatoidioma +
+
+ +
+
+ +
+
3 · Origem & encaminhamento
+
+ +
+
+
Como chegou ao terapeuta
campos existem mas são texto livre
+ parcial +
+
Você tem onde_nos_conheceu e encaminhado_por como texto livre. Ideal ser enum + texto opcional para permitir filtros e relatórios de origem.
+
+ onde_nos_conheceuencaminhado_por + origem_enumagendador_publico_ref +
+
+ +
+
+
Motivo de inatividade ou alta
por que o paciente saiu
+ faltando +
+
Quando paciente vai para "Alta", "Inativo" ou "Encaminhado" — qual o motivo? Alta terapêutica, abandono, encaminhamento, mudança de cidade. Essencial para relatórios e qualidade clínica.
+
+ motivo_saidadata_saidaencaminhado_para +
+
+ +
+
+ +
+
4 · Status & ciclo de vida do paciente
+
+ +
+
+
Status do paciente
Ativo, Inativo, Alta, Encaminhado, Arquivado
+ completo +
+
Enum bem definido com os 5 status mais relevantes. Constraint no banco garante integridade.
+
+ AtivoInativoAltaEncaminhadoArquivado +
+
+ +
+
+
Histórico de mudanças de status
trilha de auditoria do ciclo de vida
+ faltando +
+
Quando o status mudou, quem mudou e por quê. Permite ver o histórico completo: "Ativo → Inativo (01/03) → Ativo (15/04)". Exigência de auditoria clínica.
+
+ status_anteriorstatus_novomotivoalterado_poralterado_em +
+
+ +
+
+
Escopo do paciente (clínica vs. terapeuta)
patient_scope bem modelado
+ completo +
+
Distinção entre paciente da clínica (qualquer terapeuta pode atender) e paciente particular do terapeuta. Com constraint de consistência.
+
+ patient_scopetherapist_member_idresponsible_member_id +
+
+ +
+
+
Alerta & flag de risco
sinalização visível no topo do cadastro
+ diferencial +
+
Flag booleano de risco elevado com nota associada. Exibe alerta vermelho no topo do cadastro e do prontuário. Terapeuta sinaliza pacientes que precisam de atenção especial (ideação, crise recente).
+
+ risco_elevadonota_riscosinalizado_emsinalizado_por +
+
+ +
+
+ +
+
5 · Organização & segmentação
+
+ +
+
+
Tags de paciente
patient_tags + patient_patient_tag
+ completo +
+
Tags com nome e cor, por tenant, com many-to-many. Bem estruturado.
+
+ patient_tagspatient_patient_tagcoris_padrao +
+
+ +
+
+
Grupos de pacientes
patient_groups com many-to-many
+ completo +
+
Grupos com nome, cor, descrição, status ativo e flag de sistema. Relação many-to-many com patient_group_patient.
+
+ patient_groupspatient_group_patientis_system +
+
+ +
+
+
Cor de identificação
identification_color na agenda
+ completo +
+
Cor atribuída ao paciente para visualização rápida na agenda. Diferencial visual que poucos sistemas brasileiros têm.
+
+ identification_color +
+
+ +
+
+
Score de engajamento
calculado automaticamente
+ diferencial +
+
Score calculado por view/função baseado em: frequência de sessões, taxa de comparecimento, dias desde última sessão, pagamentos em dia. Exibido como indicador no card do paciente. Ajuda a identificar quem precisa de atenção.
+
+ engajamento_scoretaxa_comparecimentodias_sem_sessao +
+
+ +
+
+ +
+
6 · Financeiro vinculado ao paciente
+
+ +
+
+
Descontos individuais
patient_discounts bem modelado
+ completo +
+
Desconto percentual ou fixo por paciente, com período de validade e motivo. Bem estruturado com active_from e active_to.
+
+ discount_pctdiscount_flatactive_fromactive_toreason +
+
+ +
+
+
Limite de sessões por período
controle de plano ou convênio
+ faltando +
+
Pacientes de convênio frequentemente têm limite de sessões autorizadas por mês. Campo para registrar o limite e controlar o consumo — alerta quando está próximo do teto.
+
+ limite_sessoes_messessoes_usadasperiodo_referencia +
+
+ +
+
+
Método de pagamento preferido
como esse paciente costuma pagar
+ faltando +
+
PIX, cartão, dinheiro, convênio. Aparece como sugestão padrão ao registrar cobrança. Evita perguntar toda vez como o paciente paga.
+
+ metodo_pagamento_preferidodados_pagamento_obs +
+
+ +
+
+
LTV & métricas financeiras do paciente
calculado por view
+ diferencial +
+
Total pago desde o início, ticket médio por sessão, total de sessões realizadas. Calculado por view em cima de financial_records — sem armazenar, sem inconsistência.
+
+ v_patient_ltvtotal_pagoticket_mediototal_sessoes +
+
+ +
+
+ +
+
7 · Observações & notas internas
+
+ +
+
+
Observações gerais
dois campos de texto soltos
+ parcial +
+
Você tem observacoes e notas_internas como campos de texto livre. Funciona, mas sem distinção clara de propósito ou histórico de edições.
+
+ observacoesnotas_internas + historico_edicoeseditado_por +
+
+ +
+
+
Linha do tempo do paciente
feed cronológico de tudo que aconteceu
+ diferencial +
+
Feed automático com eventos relevantes: "Primeira sessão", "Mudança de status", "Documento assinado", "Escala respondida", "Pagamento em atraso". Visível no topo do cadastro como timeline. SimplePractice tem isso.
+
+ patient_timelineevento_tipodescricaoocorrido_em +
+
+ +
+
+ +
diff --git a/docs/architecture/Pacientes/pacientes_status_implementacao.html b/docs/architecture/Pacientes/pacientes_status_implementacao.html new file mode 100644 index 0000000..5580b10 --- /dev/null +++ b/docs/architecture/Pacientes/pacientes_status_implementacao.html @@ -0,0 +1,964 @@ + + + + + +Pacientes — Status de Implementacao + + + + + + + + +
+
+
5
+
Tabelas core
+
+
+
4
+
Tabelas aux
+
+
+
2
+
Views
+
+
+
3
+
Services
+
+
+
10
+
Componentes
+
+
+
8+8
+
Rotas (T+C)
+
+
+
50+
+
Colunas patients
+
+
+
5
+
Triggers
+
+
+ + +
+
Implementado (DB + frontend)
+
Parcial / migration pendente
+
Planejado / nao implementado
+
+ + +
+
+
1
+
Cadastro & Dados Pessoais
+
+ +
+
+ +
+
+
Identidade & dados pessoais
+ pronto +
+
Formulario completo com 6 secoes em accordion. Nome completo, nome social, pronomes, data nascimento, genero, estado civil, CPF (validacao checksum), RG, naturalidade, etnia, profissao, escolaridade. Avatar com upload ao Storage.
+
+ nome_completonome_socialpronomes + data_nascimentogeneroestado_civil + cpfrgetnia + profissaoescolaridadeavatar_url +
+
PatientsCadastroPage.vue → secao "Identidade"
+
+ +
+
+
Contato & preferencias
+ pronto +
+
Telefone principal e alternativo, email principal e alternativo. Canal preferido de contato (WhatsApp, Telefone, E-mail, SMS). Horario preferido para contato com janela inicio/fim. Idioma.
+
+ telefonetelefone_alternativoemail_principal + email_alternativocanal_preferido + horario_contato_iniciohorario_contato_fimidioma +
+
DB: CHECK canal_preferido IN (whatsapp, email, sms, telefone)
+
+ +
+
+
Endereco com auto-preenchimento
+ pronto +
+
CEP com consulta ViaCEP automatica (onBlur). Preenche logradouro, bairro, cidade, estado. Complemento e numero manuais. Pais default Brasil.
+
+ cependereconumero + bairrocomplementocidade + estadopais +
+
PatientsCadastroPage.vue → secao "Endereco" + ViaCEP API
+
+ +
+
+
Responsavel legal
+ pronto +
+
Para menores ou cobranca em terceiro. Nome, CPF (validacao), telefone, observacao. Flag de cobranca no responsavel.
+
+ nome_responsavelcpf_responsavel + telefone_responsavelobservacao_responsavel + cobranca_no_responsavel +
+
PatientsCadastroPage.vue → secao "Responsavel"
+
+ +
+
+
Cadastro rapido & link externo
+ pronto +
+
3 modos de criacao: Cadastro rapido (nome, email, telefone), Cadastro completo (formulario full), Link externo (paciente preenche). Convite via token com validade.
+
ComponentCadastroRapido.vue + PatientCreatePopover.vue + PatientsExternalLinkPage.vue
+
+ +
+
+
Dados socioeconomicos
+ pronto +
+
Religiao, faixa de renda (ate_1sm, 1_3sm, 3_6sm, 6_10sm, acima_10sm, nao_informado), origem (indicacao, agendador, redes_sociais, encaminhamento).
+
+ religiaofaixa_rendaorigem + onde_nos_conheceuencaminhado_por +
+
DB: CHECK constraints com valores permitidos
+
+ +
+ + + +
+
+ + +
+
+
2
+
Listagem, Busca & Organizacao
+
+ +
+
+ +
+
+
Lista de pacientes com filtros
+ pronto +
+
DataTable com busca por nome/email/telefone (debounce 250ms). Filtros: status, grupo, tag, data de criacao. Colunas dinamicas com visibilidade configuravel. Vista tabela (desktop) e cards (mobile). Vista agrupada por grupo.
+
+ searchstatusgroupId + tagIdcreatedFromcreatedTo +
+
PatientsListPage.vue (1457 linhas)
+
+ +
+
+
Grupos de pacientes
+ pronto +
+
CRUD completo de grupos com cor. Associacao paciente ↔ grupo via junction table. Contagem de pacientes por grupo. Pagina de gestao dedicada.
+
+ patient_groupspatient_group_patient +
+
GruposPacientesPage.vue + GruposPacientes.service.js
+
+ +
+
+
Tags de pacientes
+ pronto +
+
CRUD de tags com cor e nome. Multi-select no cadastro. Autocomplete. Contagem de pacientes por tag. Tags padrao do sistema.
+
+ patient_tagspatient_patient_tag +
+
TagsPage.vue + patientTags.service.js
+
+ +
+
+
Medicos & referencias
+ pronto +
+
Cadastro de medicos que encaminham pacientes. CRM, especialidade (13 opcoes), contatos. Contagem de pacientes por medico. Soft delete. Busca de pacientes referidos.
+
+ medicosnomecrm + especialidadeencaminhado_por +
+
MedicosPage.vue + Medicos.service.js
+
+ +
+
+
Lista de espera
+ placeholder +
+
Tab "Lista de espera" existe na PatientsListPage mas e um placeholder. Comentario no codigo: "Quando voce quiser, podemos ligar isso a uma tabela (ex: patient_waitlist)". Nao tem tabela no banco.
+
PatientsListPage.vue → tab placeholder
+
+ +
+
+
Cadastros recebidos (intake)
+ pronto +
+
Formularios de cadastro externo submetidos por pacientes prospectivos. Status: new, converted, rejected. Pagina de gestao dedicada com badge no menu.
+
+ patient_intake_requestsstatusconverted_patient_id +
+
CadastrosRecebidosPage.vue
+
+ +
+ + + +
+
+ + +
+
+
3
+
Prontuario do Paciente
+
+ +
+
+ +
+
+
Perfil completo (tab 1)
+ pronto +
+
Sidebar com avatar, badges (status, convenio, scope), tags e metricas (sessoes, comparecimento %, LTV, dias sem sessao). Corpo com 6 sub-secoes em accordion: dados pessoais, contato, endereco, dados adicionais, responsavel, anotacoes.
+
PatientProntuario.vue → tab "Perfil" (1167 linhas total)
+
+ +
+
+
Prontuario clinico (tab 2)
+ estrutura +
+
Tab existe no componente mas conteudo clinico (notas de sessao, evolucao, plano terapeutico) ainda precisa ser detalhado. Placeholder no modal.
+
+ +
+
+
Agenda do paciente (tab 3)
+ estrutura +
+
Tab de sessoes/agenda existe. Lista de agenda_eventos carregada. Falta validar se a UI mostra corretamente os agendamentos futuros e historico completo.
+
+ +
+
+
Financeiro do paciente (tab 4)
+ estrutura +
+
Tab existe. patient_discounts funciona (desconto percentual ou valor fixo, periodo de validade). Detalhes de cobrancas e pagamentos por paciente a validar.
+
+ patient_discountsdiscount_pctdiscount_flat +
+
+ +
+
+
Documentos do paciente (tab 5)
+ pronto +
+
DocumentsListPage embarcada no prontuario com prop embedded. Upload, preview, download, geracao de PDF, compartilhamento. Integrado com modulo de documentos completo.
+
DocumentsListPage.vue (embedded)
+
+ +
+
+
Flag de risco clinico
+ pronto +
+
Banner vermelho no prontuario quando risco_elevado = true. Obriga risco_nota e risco_sinalizado_por (CHECK constraint). Trigger registra na patient_timeline. View v_patients_risco para dashboard.
+
+ risco_elevadorisco_nota + risco_sinalizado_emrisco_sinalizado_por +
+
DB: trg_patient_risco_timeline + v_patients_risco
+
+ +
+ + + +
+
+ + +
+
+
4
+
Rede de Suporte & Contatos
+
+ +
+
+ +
+
+
Contatos de suporte (legado)
+ pronto +
+
patient_support_contacts: nome, relacao, tipo (emergencia, familiar, profissional_saude, amigo, outro), telefone, email, flag is_primario. CRUD no cadastro do paciente.
+
+ nomerelacaotipo + telefoneemailis_primario +
+
PatientsCadastroPage.vue → secao "Rede de Suporte"
+
+ +
+
+
Contatos estruturados (novo)
+ db pronto +
+
patient_contacts: tabela mais completa que substitui campos legado (nome_parente, telefone_parente). Inclui CPF, especialidade, registro profissional (CRM/CRP). Unique constraint para contato primario. Migrada com dados legados. Frontend ainda usa patient_support_contacts.
+
+ nometipocpf + especialidaderegistro_profissional + is_primarioativo +
+
DB: patient_contacts (migration_patients.sql) — frontend pendente
+
+ +
+ + + +
+
+ + +
+
+
5
+
Status, Timeline & Engajamento
+
+ +
+
+ +
+
+
Gestao de status
+ pronto +
+
6 status: Ativo, Em espera, Inativo, Alta, Encaminhado, Arquivado. Campos de saida: motivo_saida, data_saida, encaminhado_para. Historico automatico via trigger.
+
+ statusmotivo_saidadata_saida + encaminhado_para +
+
DB: patient_status_history (trigger automatico)
+
+ +
+
+
Historico de status
+ db pronto +
+
Tabela imutavel patient_status_history. Registra automaticamente: status anterior, novo, motivo, encaminhamento, data saida, quem alterou. Trigger trg_patient_status_history.
+
+ status_anteriorstatus_novomotivo + alterado_poralterado_em +
+
+ +
+
+
Timeline do paciente
+ db pronto +
+
patient_timeline: feed cronologico com 18 tipos de evento. Auto-populada por triggers (status, risco, documentos, assinaturas). Cores por tipo. Referencia polimorfica para links.
+
+ evento_tipotitulodescricao + icone_corlink_ref_tipolink_ref_id +
+
DB: 18 event types + 3 triggers auto-insert
+
+ +
+
+
View de engajamento
+ db pronto +
+
v_patient_engajamento: score 0-100 calculado em real-time. Metricas: total sessoes, sessoes ultimo mes, taxa comparecimento, LTV, ticket medio, cobrancas vencidas/pagas, taxa pagamentos em dia, duracao tratamento. Formula: 50% frequencia + 30% financeiro + 20% recencia.
+
DB: VIEW v_patient_engajamento (security_invoker = on)
+
+ +
+
+
View de pacientes em risco
+ db pronto +
+
v_patients_risco: lista pacientes que precisam de atencao. Criterios: risco_elevado, sem sessao ha 30+ dias, comparecimento <60%, cobranca vencida. Alertas categorizados.
+
DB: VIEW v_patients_risco
+
+ +
+
+
UI de timeline no prontuario
+ pendente +
+
A tabela patient_timeline e as views estao prontas no banco. Falta o componente frontend para exibir a timeline visualmente no prontuario do paciente (feed cronologico com icones e cores).
+
+ +
+ + + +
+
+ + +
+
+
6
+
Financeiro & Convenios
+
+ +
+
+ +
+
+
Convenio / plano de saude
+ pronto +
+
Associacao paciente → insurance_plans. Campo convenio (texto livre) + convenio_id (FK). Cadastro rapido de convenio via dialog. Exibido no prontuario como badge.
+
+ convenioconvenio_idinsurance_plans +
+
CadastroRapidoConvenio.vue + PatientsCadastroPage.vue
+
+ +
+
+
Metodo de pagamento preferido
+ pronto +
+
PIX, cartao, dinheiro, deposito, convenio. CHECK constraint no banco. Selecionavel no cadastro.
+
+ metodo_pagamento_preferido +
+
DB: CHECK (pix, cartao, dinheiro, deposito, convenio)
+
+ +
+
+
Descontos por paciente
+ pronto +
+
patient_discounts: desconto percentual ou valor fixo, motivo, periodo de validade (active_from, active_to). Gerenciavel pela listagem de pacientes.
+
+ discount_pctdiscount_flatreason + active_fromactive_to +
+
+ +
+ + +
+
+ + +
+
+
!
+
Pendencias & Itens a Implementar
+
+ +
+ +
+
+
Prontuario clinico (tab 2) — notas de sessao e evolucao
+ estrutura +
+
Tab existe no modal mas sem conteudo clinico detalhado. Precisa: notas de sessao vinculadas a agenda_eventos, evolucao terapeutica, plano de tratamento, hipoteses diagnosticas.
+
+ +
+
+
UI da timeline no prontuario
+ db pronto +
+
Tabela patient_timeline com 18 event types e triggers automaticos existe. Falta componente frontend para exibir feed cronologico no prontuario (icones, cores, links para entidades referenciadas).
+
+ +
+
+
Dashboard de engajamento e risco
+ db pronto +
+
Views v_patient_engajamento e v_patients_risco existem no banco. Score de engajamento (0-100) calculado em real-time. Falta UI para exibir dashboard de risco e engajamento geral dos pacientes.
+
+ +
+
+
Migrar frontend para patient_contacts
+ db pronto +
+
Tabela patient_contacts (mais completa: CPF, especialidade, registro profissional) existe e foi populada com dados legados. Frontend ainda usa patient_support_contacts (mais simples). Migrar UI para usar a nova tabela.
+
+ +
+
+
Lista de espera (patient_waitlist)
+ nao existe +
+
Tab placeholder na PatientsListPage. Nao existe tabela no banco. Precisa: criacao da tabela, service, UI com gestao de fila (posicao, prioridade, data de entrada, notificacao quando vaga abrir).
+
+ +
+
+
Botao "+ Sessao" no prontuario
+ placeholder +
+
Botao existe no header do prontuario mas sem click handler. Precisa abrir dialog de agendamento rapido pre-preenchido com o paciente atual.
+
+ +
+
+
Tabs Agenda e Financeiro no prontuario
+ estrutura +
+
Tabs existem no modal. Dados de agenda_eventos e patient_discounts carregam. Falta validar se a UI mostra corretamente: agendamentos futuros, historico completo, cobrancas, pagamentos, recibos.
+
+ +
+
+
Migrations nao aplicadas
+ verificar +
+
Verificar se as migrations estao aplicadas no banco local: 20260328000002 (new columns), 20260328000003 (drop constraints), 20260328000004 (support_contacts), migration_patients.sql (timeline, contacts, views, risk). Algumas dependem de tabelas que podem nao existir ainda (insurance_plans, etc).
+
+ +
+ +
+ Multi-tenant: Todas as queries filtram por owner_id (terapeuta individual) ou tenant_id (clinica). RLS no banco garante isolamento. Feature flags (patients.view, patients.create, patients.edit, patients.delete) controlam acesso por plano. Rotas admin usam meta tenantFeature: 'patients'. +
+ +
+ Escopo dual: patient_scope = 'clinic' (paciente da clinica, sem therapist_member_id) ou 'therapist' (paciente particular, com therapist_member_id obrigatorio). CHECK constraint garante consistencia. +
+
+ + + diff --git a/index.html b/index.html index 2975cdc..2b9450e 100644 --- a/index.html +++ b/index.html @@ -1,17 +1,21 @@ - + - - - - Sakai Vue - + + + Agência PSI + + + + + + -
- +
+ \ No newline at end of file diff --git a/mvp-assessment.html b/mvp-assessment.html new file mode 100644 index 0000000..fcb6da8 --- /dev/null +++ b/mvp-assessment.html @@ -0,0 +1,445 @@ + + + + + +AgenciaPsi — Avaliação MVP + + + + + +

AgenciaPsi — Avaliação MVP

+

Snapshot de 25 de março de 2026  ·  ~487 componentes Vue  ·  v5.0.0

+ + +
+
+
78%
+
Pronto para MVP
+
+
+
487
+
Componentes Vue
+
+
+
10
+
Módulos prontos
+
+
+
2
+
Gaps críticos
+
+
+ + +
+ +
+

Completude por Módulo

+
+
+
Autenticação & Permissões100%
+
+
+
+
Agenda / Calendário95%
+
+
+
+
Pacientes (CRUD + Prontuário)90%
+
+
+
+
Financeiro85%
+
+
+
+
Agendador Público90%
+
+
+
+
Configurações (15 págs)88%
+
+
+
+
WhatsApp / SMS / Email75%
+
+
+
+
SaaS Admin80%
+
+
+
+
Dashboard da Clínica35%
+
+
+
+
Relatórios40%
+
+
+
+
Gateway de Pagamento10%
+
+
+
+
Testes Automatizados15%
+
+
+
+
+ +
+
+

Status Geral

+
+ +
+
78%
+
MVP Ready
+
+
+
+
+

Distribuição de Módulos

+ +
+
+ +
+ + +
+ +
+

Funcionalidades — Checklist

+
+ +
Autenticação (login, reset, sessão)Pronto
+
Calendário + RecorrênciasPronto
+
CRUD de Pacientes + ProntuárioPronto
+
Dashboard FinanceiroPronto
+
Agendador Público (/agendar/:slug)Pronto
+
15 Páginas de ConfiguraçõesPronto
+
WhatsApp + SMS (Twilio)Pronto
+
Templates de Email (Jodit)Pronto
+
Multi-tenantPronto
+
Dark mode + 3 temasPronto
+
SaaS Admin (planos, assinaturas)Pronto
+ +
Dashboard da ClínicaParcial
+
RelatóriosParcial
+
Onboarding / Setup WizardParcial
+
Validação de formulários (CPF/CNPJ)Parcial
+
Mobile responsivenessNão testado
+ +
Gateway de Pagamento (Stripe)Faltando
+
Testes E2EFaltando
+
Export PDF / ExcelFaltando
+
Google Calendar syncFaltando
+
+
+ +
+

Radar de Módulos

+ +
+ +
+ + +
+

Roadmap para Lançamento

+
+ +
+
+ + Fase 1 — Antes do MVP + (1–2 semanas) +
+
+
+
+
Gateway de Pagamento
Integrar Stripe ou PagSeguro. Cobrança real de assinaturas e sessões avulsas.
+
+
+
+
Testes dos Fluxos Críticos
Login → criar sessão → agendador público → cobrança.
+
+
+
+
Dashboard da Clínica
Expandir com KPIs reais (espelhar dashboard do terapeuta).
+
+
+
+
Mobile — Teste em Produção
Tailwind implementado mas nunca testado em dispositivos reais.
+
+
+
+
Validação de Formulários
CPF, CNPJ, telefone no cadastro de pacientes.
+
+
+
+ +
+
+ + Fase 2 — Pós-MVP + (1 mês) +
+
+
+
+
Relatórios PDF / Excel
Export de lançamentos, sessões, e KPIs para relatórios mensais.
+
+
+
+
Google Calendar Sync
Sincronização bidirecional com o Google Calendar.
+
+
+
+
Analytics / Tracking
Rastrear adoção de features e comportamento de usuários.
+
+
+
+
Webhooks Twilio (entrada)
Receber e processar mensagens WhatsApp de pacientes.
+
+
+
+ +
+
+ + Fase 3 — Expansão + (2–3 meses) +
+
+
+
+
App Mobile
React Native ou Flutter para terapeuta e paciente.
+
+
+
+
Videochamada Integrada
Sessões online sem sair da plataforma.
+
+
+
+
API REST Pública
Integrações de terceiros (CRMs, plataformas de saúde).
+
+
+
+
IA / Prontuário Inteligente
Sugestões de diagnóstico e análise de sessões.
+
+
+
+ +
+
+ + +
+

Cobertura de Rotas por Área

+ +
+ + + + diff --git a/package-lock.json b/package-lock.json index 0a88ddf..96576b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,12 @@ "@supabase/supabase-js": "^2.95.3", "chart.js": "3.3.2", "date-fns": "^4.1.0", + "html-to-pdfmake": "^2.5.33", + "html2canvas-pro": "^2.0.2", + "html2pdf.js": "^0.14.0", "jodit": "^4.11.15", + "jspdf": "^4.2.1", + "pdfmake": "^0.3.7", "pinia": "^3.0.4", "primeicons": "^7.0.0", "primevue": "^4.5.4", @@ -91,6 +96,14 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/types": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", @@ -755,6 +768,28 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1620,6 +1655,14 @@ "node": ">=20.0.0" } }, + "node_modules/@swc/helpers": { + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.20.tgz", + "integrity": "sha512-2egEBHUMasdypIzrprsu8g+OEVd7Vp2MM3a2eVlM/cyFYto0nGz5BX5BTgh/ShZZI9ed+ozEq+Ngt+rgmUs8tw==", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@tailwindcss/node": { "version": "4.1.18", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", @@ -1907,11 +1950,28 @@ "undici-types": "~7.16.0" } }, + "node_modules/@types/pako": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==" + }, "node_modules/@types/phoenix": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz", "integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==" }, + "node_modules/@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "optional": true + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "optional": true + }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -2313,6 +2373,22 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/baseline-browser-mapping": { "version": "2.9.19", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", @@ -2370,6 +2446,33 @@ "node": ">=8" } }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/brotli/node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/browserslist": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", @@ -2447,6 +2550,25 @@ } ] }, + "node_modules/canvg": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", + "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", @@ -2533,6 +2655,14 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2577,6 +2707,17 @@ "url": "https://github.com/sponsors/mesqueeb" } }, + "node_modules/core-js": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", + "hasInstallScript": true, + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2591,6 +2732,14 @@ "node": ">= 8" } }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -2689,6 +2838,11 @@ "node": ">=8" } }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==" + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2701,6 +2855,14 @@ "node": ">=6.0.0" } }, + "node_modules/dompurify": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz", + "integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.286", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", @@ -3037,8 +3199,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-diff": { "version": "1.3.0", @@ -3085,6 +3246,16 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-png": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz", + "integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==", + "dependencies": { + "@types/pako": "^2.0.3", + "iobuffer": "^5.3.2", + "pako": "^2.1.0" + } + }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -3114,8 +3285,7 @@ "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", - "dev": true + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" }, "node_modules/file-entry-cache": { "version": "6.0.1", @@ -3177,6 +3347,22 @@ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true }, + "node_modules/fontkit": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", + "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "dependencies": { + "@swc/helpers": "^0.5.12", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "dfa": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "restructure": "^3.0.0", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.4.0", + "unicode-trie": "^2.0.0" + } + }, "node_modules/fraction.js": { "version": "5.3.4", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", @@ -3319,6 +3505,45 @@ "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==" }, + "node_modules/html-to-pdfmake": { + "version": "2.5.33", + "resolved": "https://registry.npmjs.org/html-to-pdfmake/-/html-to-pdfmake-2.5.33.tgz", + "integrity": "sha512-AyJwwpoLiI80Ha7yYJ+voTria72TXzu/CwuPPoSwRFned2vhIthXvfxAKj/S7fPFnj7Dexp9hrtuGumJ39+MYQ==" + }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/html2canvas-pro": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html2canvas-pro/-/html2canvas-pro-2.0.2.tgz", + "integrity": "sha512-9G/t0XgCZWonLwL0JwI7su6NdbOPUY7Ur4Ihpp8+XMaW9ibA2nDXF181Jr6tm94k8lX6sthpaXB3XqEnsMd5Cw==", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/html2pdf.js": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/html2pdf.js/-/html2pdf.js-0.14.0.tgz", + "integrity": "sha512-yvNJgE/8yru2UeGflkPdjW8YEY+nDH5X7/2WG4uiuSCwYiCp8PZ8EKNiTAa6HxJ1NjC51fZSIEq6xld5CADKBQ==", + "dependencies": { + "dompurify": "^3.3.1", + "html2canvas": "^1.0.0", + "jspdf": "^4.0.0" + } + }, "node_modules/iceberg-js": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", @@ -3384,6 +3609,11 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/iobuffer": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", + "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==" + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3526,6 +3756,11 @@ "resolved": "https://registry.npmjs.org/jodit/-/jodit-4.11.15.tgz", "integrity": "sha512-cT/dlFsK9mCmACb85TB9MmfHXyzsIyZYKY2VO2FLUdO+Xz7wIjBhu06wMCM7nkBdsxpWFGncHEP0KKd4mPx6Ng==" }, + "node_modules/js-md5": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.8.3.tgz", + "integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==" + }, "node_modules/js-tokens": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", @@ -3574,6 +3809,22 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jspdf": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-4.2.1.tgz", + "integrity": "sha512-YyAXyvnmjTbR4bHQRLzex3CuINCDlQnBqoSYyjJwTP2x9jDLuKDzy7aKUl0hgx3uhcl7xzg32agn5vlie6HIlQ==", + "dependencies": { + "@babel/runtime": "^7.28.6", + "fast-png": "^6.2.0", + "fflate": "^0.8.1" + }, + "optionalDependencies": { + "canvg": "^3.0.11", + "core-js": "^3.6.0", + "dompurify": "^3.3.1", + "html2canvas": "^1.0.0-rc.5" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -3845,6 +4096,15 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/linebreak": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", + "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", + "dependencies": { + "base64-js": "0.0.8", + "unicode-trie": "^2.0.0" + } + }, "node_modules/local-pkg": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", @@ -4133,6 +4393,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "node_modules/parchment": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz", @@ -4183,11 +4448,43 @@ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true }, + "node_modules/pdfkit": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.18.0.tgz", + "integrity": "sha512-NvUwSDZ0eYEzqAiWwVQkRkjYUkZ48kcsHuCO31ykqPPIVkwoSDjDGiwIgHHNtsiwls3z3P/zy4q00hl2chg2Ug==", + "dependencies": { + "@noble/ciphers": "^1.0.0", + "@noble/hashes": "^1.6.0", + "fontkit": "^2.0.4", + "js-md5": "^0.8.3", + "linebreak": "^1.1.0", + "png-js": "^1.0.0" + } + }, + "node_modules/pdfmake": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/pdfmake/-/pdfmake-0.3.7.tgz", + "integrity": "sha512-SwTFcaH3kCJBlPFWi/YB34zRg6lpCxq90tkZ9GxfSi9/v4Tk96cv4IvOstA+CC40rdW1OzQIuNhD2DLD1RDVgA==", + "dependencies": { + "linebreak": "^1.1.0", + "pdfkit": "^0.18.0", + "xmldoc": "^2.0.3" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==" }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "optional": true + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -4249,6 +4546,11 @@ "pathe": "^2.0.1" } }, + "node_modules/png-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", + "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==" + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -4444,6 +4746,15 @@ "node": ">= 12.0.0" } }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "optional": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -4457,6 +4768,12 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "optional": true + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -4466,6 +4783,11 @@ "node": ">=4" } }, + "node_modules/restructure": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", + "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==" + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -4481,6 +4803,15 @@ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -4626,6 +4957,14 @@ "@parcel/watcher": "^2.4.1" } }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/scule": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz", @@ -4716,6 +5055,15 @@ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true }, + "node_modules/stackblur-canvas": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, "node_modules/std-env": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", @@ -4825,6 +5173,15 @@ "node": ">=8" } }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/synckit": { "version": "0.11.12", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", @@ -4866,12 +5223,25 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -4973,6 +5343,48 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==" }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-properties/node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unicode-trie/node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==" + }, "node_modules/unimport": { "version": "3.14.6", "resolved": "https://registry.npmjs.org/unimport/-/unimport-3.14.6.tgz", @@ -5296,6 +5708,14 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, "node_modules/v-offline": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/v-offline/-/v-offline-3.5.1.tgz", @@ -6187,6 +6607,17 @@ "node": ">=12" } }, + "node_modules/xmldoc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-2.0.3.tgz", + "integrity": "sha512-6gRk4NY/Jvg67xn7OzJuxLRsGgiXBaPUQplVJ/9l99uIugxh4FTOewYz5ic8WScj7Xx/2WvhENiQKwkK9RpE4w==", + "dependencies": { + "sax": "^1.4.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -6260,6 +6691,11 @@ "@babel/types": "^7.29.0" } }, + "@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==" + }, "@babel/types": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", @@ -6626,6 +7062,16 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==" + }, + "@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -7097,6 +7543,14 @@ "@supabase/storage-js": "2.95.3" } }, + "@swc/helpers": { + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.20.tgz", + "integrity": "sha512-2egEBHUMasdypIzrprsu8g+OEVd7Vp2MM3a2eVlM/cyFYto0nGz5BX5BTgh/ShZZI9ed+ozEq+Ngt+rgmUs8tw==", + "requires": { + "tslib": "^2.8.0" + } + }, "@tailwindcss/node": { "version": "4.1.18", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", @@ -7265,11 +7719,28 @@ "undici-types": "~7.16.0" } }, + "@types/pako": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", + "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==" + }, "@types/phoenix": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz", "integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==" }, + "@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "optional": true + }, + "@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "optional": true + }, "@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -7580,6 +8051,16 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==" + }, + "base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==" + }, "baseline-browser-mapping": { "version": "2.9.19", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", @@ -7622,6 +8103,21 @@ "fill-range": "^7.1.1" } }, + "brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "requires": { + "base64-js": "^1.1.2" + }, + "dependencies": { + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + } + } + }, "browserslist": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", @@ -7656,6 +8152,22 @@ "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", "dev": true }, + "canvg": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", + "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", + "optional": true, + "requires": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + } + }, "chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", @@ -7714,6 +8226,11 @@ } } }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==" + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -7749,6 +8266,12 @@ "is-what": "^5.2.0" } }, + "core-js": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", + "optional": true + }, "cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -7760,6 +8283,14 @@ "which": "^2.0.1" } }, + "css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "requires": { + "utrie": "^1.0.2" + } + }, "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -7819,6 +8350,11 @@ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true }, + "dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==" + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -7828,6 +8364,14 @@ "esutils": "^2.0.2" } }, + "dompurify": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz", + "integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==", + "requires": { + "@types/trusted-types": "^2.0.7" + } + }, "electron-to-chromium": { "version": "1.5.286", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", @@ -8065,8 +8609,7 @@ "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-diff": { "version": "1.3.0", @@ -8109,6 +8652,16 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "fast-png": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz", + "integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==", + "requires": { + "@types/pako": "^2.0.3", + "iobuffer": "^5.3.2", + "pako": "^2.1.0" + } + }, "fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -8128,8 +8681,7 @@ "fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", - "dev": true + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" }, "file-entry-cache": { "version": "6.0.1", @@ -8176,6 +8728,22 @@ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true }, + "fontkit": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", + "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "requires": { + "@swc/helpers": "^0.5.12", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "dfa": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "restructure": "^3.0.0", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.4.0", + "unicode-trie": "^2.0.0" + } + }, "fraction.js": { "version": "5.3.4", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", @@ -8273,6 +8841,39 @@ "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==" }, + "html-to-pdfmake": { + "version": "2.5.33", + "resolved": "https://registry.npmjs.org/html-to-pdfmake/-/html-to-pdfmake-2.5.33.tgz", + "integrity": "sha512-AyJwwpoLiI80Ha7yYJ+voTria72TXzu/CwuPPoSwRFned2vhIthXvfxAKj/S7fPFnj7Dexp9hrtuGumJ39+MYQ==" + }, + "html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "requires": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + } + }, + "html2canvas-pro": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html2canvas-pro/-/html2canvas-pro-2.0.2.tgz", + "integrity": "sha512-9G/t0XgCZWonLwL0JwI7su6NdbOPUY7Ur4Ihpp8+XMaW9ibA2nDXF181Jr6tm94k8lX6sthpaXB3XqEnsMd5Cw==", + "requires": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + } + }, + "html2pdf.js": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/html2pdf.js/-/html2pdf.js-0.14.0.tgz", + "integrity": "sha512-yvNJgE/8yru2UeGflkPdjW8YEY+nDH5X7/2WG4uiuSCwYiCp8PZ8EKNiTAa6HxJ1NjC51fZSIEq6xld5CADKBQ==", + "requires": { + "dompurify": "^3.3.1", + "html2canvas": "^1.0.0", + "jspdf": "^4.0.0" + } + }, "iceberg-js": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", @@ -8322,6 +8923,11 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "iobuffer": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", + "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==" + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -8410,6 +9016,11 @@ "resolved": "https://registry.npmjs.org/jodit/-/jodit-4.11.15.tgz", "integrity": "sha512-cT/dlFsK9mCmACb85TB9MmfHXyzsIyZYKY2VO2FLUdO+Xz7wIjBhu06wMCM7nkBdsxpWFGncHEP0KKd4mPx6Ng==" }, + "js-md5": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.8.3.tgz", + "integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==" + }, "js-tokens": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", @@ -8453,6 +9064,20 @@ "universalify": "^2.0.0" } }, + "jspdf": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-4.2.1.tgz", + "integrity": "sha512-YyAXyvnmjTbR4bHQRLzex3CuINCDlQnBqoSYyjJwTP2x9jDLuKDzy7aKUl0hgx3uhcl7xzg32agn5vlie6HIlQ==", + "requires": { + "@babel/runtime": "^7.28.6", + "canvg": "^3.0.11", + "core-js": "^3.6.0", + "dompurify": "^3.3.1", + "fast-png": "^6.2.0", + "fflate": "^0.8.1", + "html2canvas": "^1.0.0-rc.5" + } + }, "keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -8569,6 +9194,15 @@ "dev": true, "optional": true }, + "linebreak": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", + "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", + "requires": { + "base64-js": "0.0.8", + "unicode-trie": "^2.0.0" + } + }, "local-pkg": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", @@ -8785,6 +9419,11 @@ "p-limit": "^3.0.2" } }, + "pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "parchment": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz", @@ -8823,11 +9462,40 @@ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true }, + "pdfkit": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.18.0.tgz", + "integrity": "sha512-NvUwSDZ0eYEzqAiWwVQkRkjYUkZ48kcsHuCO31ykqPPIVkwoSDjDGiwIgHHNtsiwls3z3P/zy4q00hl2chg2Ug==", + "requires": { + "@noble/ciphers": "^1.0.0", + "@noble/hashes": "^1.6.0", + "fontkit": "^2.0.4", + "js-md5": "^0.8.3", + "linebreak": "^1.1.0", + "png-js": "^1.0.0" + } + }, + "pdfmake": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/pdfmake/-/pdfmake-0.3.7.tgz", + "integrity": "sha512-SwTFcaH3kCJBlPFWi/YB34zRg6lpCxq90tkZ9GxfSi9/v4Tk96cv4IvOstA+CC40rdW1OzQIuNhD2DLD1RDVgA==", + "requires": { + "linebreak": "^1.1.0", + "pdfkit": "^0.18.0", + "xmldoc": "^2.0.3" + } + }, "perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==" }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "optional": true + }, "picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -8873,6 +9541,11 @@ "pathe": "^2.0.1" } }, + "png-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", + "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==" + }, "postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -8987,18 +9660,38 @@ "lodash.isequal": "^4.5.0" } }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "optional": true, + "requires": { + "performance-now": "^2.1.0" + } + }, "readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "optional": true + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "restructure": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", + "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==" + }, "reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -9010,6 +9703,12 @@ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" }, + "rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "optional": true + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -9093,6 +9792,11 @@ "source-map-js": ">=0.6.2 <2.0.0" } }, + "sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==" + }, "scule": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz", @@ -9159,6 +9863,12 @@ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true }, + "stackblur-canvas": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", + "optional": true + }, "std-env": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", @@ -9234,6 +9944,12 @@ "has-flag": "^4.0.0" } }, + "svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "optional": true + }, "synckit": { "version": "0.11.12", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", @@ -9260,12 +9976,25 @@ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true }, + "text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "requires": { + "utrie": "^1.0.2" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" + }, "tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -9340,6 +10069,38 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==" }, + "unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "requires": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + }, + "dependencies": { + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + } + } + }, + "unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "requires": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + }, + "dependencies": { + "pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==" + } + } + }, "unimport": { "version": "3.14.6", "resolved": "https://registry.npmjs.org/unimport/-/unimport-3.14.6.tgz", @@ -9564,6 +10325,14 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "requires": { + "base64-arraybuffer": "^1.0.2" + } + }, "v-offline": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/v-offline/-/v-offline-3.5.1.tgz", @@ -9983,6 +10752,14 @@ "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", "dev": true }, + "xmldoc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-2.0.3.tgz", + "integrity": "sha512-6gRk4NY/Jvg67xn7OzJuxLRsGgiXBaPUQplVJ/9l99uIugxh4FTOewYz5ic8WScj7Xx/2WvhENiQKwkK9RpE4w==", + "requires": { + "sax": "^1.4.3" + } + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 1ab31a1..398d6d7 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,12 @@ "@supabase/supabase-js": "^2.95.3", "chart.js": "3.3.2", "date-fns": "^4.1.0", + "html-to-pdfmake": "^2.5.33", + "html2canvas-pro": "^2.0.2", + "html2pdf.js": "^0.14.0", "jodit": "^4.11.15", + "jspdf": "^4.2.1", + "pdfmake": "^0.3.7", "pinia": "^3.0.4", "primeicons": "^7.0.0", "primevue": "^4.5.4", diff --git a/src/App.vue b/src/App.vue index dcf1bc5..fb36cf5 100644 --- a/src/App.vue +++ b/src/App.vue @@ -33,6 +33,10 @@ function isTenantArea(path = '') { } // ── Setup Wizard redirect ──────────────────────────────────────── +// Cache por sessão: uma vez confirmado, não verifica de novo +let _setupClearedUid = null; +let _setupClearedIsClinic = null; + async function checkSetupWizard() { if (!isTenantArea(route.path)) return; if (route.path.includes('/setup')) return; @@ -40,19 +44,33 @@ async function checkSetupWizard() { const uid = tenantStore.user?.id; if (!uid) return; - const { data } = await supabase.from('agenda_configuracoes').select('setup_concluido, setup_clinica_concluido').eq('owner_id', uid).maybeSingle(); - - if (!data) return; - - const activeMembership = tenantStore.memberships?.find((m) => m.id === tenantStore.activeTenantId); + const activeMembership = tenantStore.memberships?.find((m) => m.tenant_id === tenantStore.activeTenantId); const kind = activeMembership?.kind ?? tenantStore.activeRole ?? ''; const isClinic = kind.startsWith('clinic'); - const setupDone = isClinic ? data.setup_clinica_concluido : data.setup_concluido; - if (!setupDone) { - const dest = isClinic ? '/admin/setup' : '/therapist/setup'; - router.push(dest); + // Se já confirmamos que este uid passou o setup, não verifica de novo + if (_setupClearedUid === uid && _setupClearedIsClinic === isClinic) return; + + const { data } = await supabase + .from('agenda_configuracoes') + .select('setup_concluido, setup_clinica_concluido, atendimento_mode') + .eq('owner_id', uid) + .maybeSingle(); + + if (!data) return; // sem linha = setup nunca iniciado, não redireciona + + // Considera completo se qualquer flag de conclusão estiver setada + const setupDone = data.setup_concluido || data.setup_clinica_concluido || !!data.atendimento_mode; + + if (setupDone) { + // Grava cache: não verifica mais nesta sessão + _setupClearedUid = uid; + _setupClearedIsClinic = isClinic; + return; } + + const dest = isClinic ? '/admin/setup' : '/therapist/setup'; + router.push(dest); } onMounted(() => { diff --git a/src/assets/styles.scss b/src/assets/styles.scss index f28a4d5..178081f 100644 --- a/src/assets/styles.scss +++ b/src/assets/styles.scss @@ -273,3 +273,23 @@ .app-dark .p-datatable tr.row-new-highlight td { background-color: color-mix(in srgb, var(--primary-color) 20%, transparent) !important; } + +/* ── Agenda Preview ────────────────────────── */ +.app-dark .fc-scrollgrid-section-sticky > * { + background: var(--surface-hover); +} + +.app-dark .fc-theme-standard td, +.app-dark .fc-theme-standard th { + border: 1px solid var(--surface-border); +} + +.app-dark .fc-theme-standard .fc-scrollgrid { + border: 1px solid var(--surface-border); +} + +.app-dark .fc-timegrid-event-harness-inset .fc-timegrid-event, +.app-dark .fc-timegrid-event.fc-event-mirror, +.fc-timegrid-more-link { + box-shadow: 0 0 0 1px #000000; +} diff --git a/src/components/CadastroRapidoConvenio.vue b/src/components/CadastroRapidoConvenio.vue new file mode 100644 index 0000000..ef10929 --- /dev/null +++ b/src/components/CadastroRapidoConvenio.vue @@ -0,0 +1,351 @@ + + + + diff --git a/src/components/CadastroRapidoMedico.vue b/src/components/CadastroRapidoMedico.vue new file mode 100644 index 0000000..4273894 --- /dev/null +++ b/src/components/CadastroRapidoMedico.vue @@ -0,0 +1,646 @@ + + + +