diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml index 105ce2d..dd4c951 100644 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -1,5 +1,6 @@ + diff --git a/alembic/versions/shared/223ec99140a9_nome_usuario.py b/alembic/versions/shared/223ec99140a9_nome_usuario.py new file mode 100644 index 0000000..b675120 --- /dev/null +++ b/alembic/versions/shared/223ec99140a9_nome_usuario.py @@ -0,0 +1,31 @@ +"""nome-usuario + +Revision ID: 223ec99140a9 +Revises: 2bc15d1e557d +Create Date: 2024-12-28 05:55:10.230627 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = '223ec99140a9' +down_revision: Union[str, None] = '2bc15d1e557d' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/alembic/versions/shared/2bc15d1e557d_initial_migration_shared.py b/alembic/versions/shared/2bc15d1e557d_initial_migration_shared.py new file mode 100644 index 0000000..a061e99 --- /dev/null +++ b/alembic/versions/shared/2bc15d1e557d_initial_migration_shared.py @@ -0,0 +1,80 @@ +"""Initial Migration Shared + +Revision ID: 2bc15d1e557d +Revises: +Create Date: 2024-11-30 17:40:09.765054 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = '2bc15d1e557d' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('inquilinos', + sa.Column('nome', sa.String(length=100), nullable=False), + sa.Column('cpf_cnpj', sa.String(length=14), nullable=False), + sa.Column('pessoa_celular', sa.String(length=20), nullable=True), + sa.Column('uuid', sa.UUID(), nullable=False), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_inquilinos')), + sa.UniqueConstraint('cpf_cnpj', name=op.f('uq_ct_inquilinos_cpf_cnpj')) + ) + op.create_table('rbac_papeis', + sa.Column('nome', sa.String(length=50), nullable=False), + sa.Column('uuid', sa.UUID(), nullable=False), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_rbac_papeis')), + sa.UniqueConstraint('nome', name=op.f('uq_ct_rbac_papeis_nome')) + ) + op.create_table('rbac_permissoes', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('nome', sa.String(length=50), nullable=False), + sa.PrimaryKeyConstraint('id', name=op.f('pk_ct_rbac_permissoes')), + sa.UniqueConstraint('nome', name=op.f('uq_ct_rbac_permissoes_nome')) + ) + op.create_table('rbac_papel_permissoes', + sa.Column('papel_uuid', sa.UUID(), nullable=False), + sa.Column('permissao_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['papel_uuid'], ['shared.rbac_papeis.uuid'], name=op.f('fk_ct_rbac_papel_permissoes_papel_uuid_rbac_papeis')), + sa.ForeignKeyConstraint(['permissao_id'], ['shared.rbac_permissoes.id'], name=op.f('fk_ct_rbac_papel_permissoes_permissao_id_rbac_permissoes')), + sa.PrimaryKeyConstraint('papel_uuid', 'permissao_id', name=op.f('pk_ct_rbac_papel_permissoes')) + ) + op.create_table('rbac_usuarios', + sa.Column('fk_inquilino_uuid', sa.UUID(), nullable=False), + sa.Column('id', fastapi_users_db_sqlalchemy.generics.GUID(), nullable=False), + sa.Column('email', sa.String(length=320), nullable=False), + sa.Column('hashed_password', sa.String(length=1024), nullable=False), + sa.Column('is_active', sa.Boolean(), nullable=False), + sa.Column('is_superuser', sa.Boolean(), nullable=False), + sa.Column('is_verified', sa.Boolean(), nullable=False), + sa.ForeignKeyConstraint(['fk_inquilino_uuid'], ['shared.inquilinos.uuid'], name=op.f('fk_ct_rbac_usuarios_fk_inquilino_uuid_inquilinos'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id', name=op.f('pk_ct_rbac_usuarios')) + ) + op.create_table('rbac_papeis_usuario', + sa.Column('user_uuid', sa.UUID(), nullable=False), + sa.Column('papel_uuid', sa.UUID(), nullable=False), + sa.ForeignKeyConstraint(['papel_uuid'], ['shared.rbac_papeis.uuid'], name=op.f('fk_ct_rbac_papeis_usuario_papel_uuid_rbac_papeis')), + sa.ForeignKeyConstraint(['user_uuid'], ['shared.rbac_usuarios.id'], name=op.f('fk_ct_rbac_papeis_usuario_user_uuid_rbac_usuarios')), + sa.PrimaryKeyConstraint('user_uuid', 'papel_uuid', name=op.f('pk_ct_rbac_papeis_usuario')) + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('rbac_papeis_usuario') + op.drop_table('rbac_usuarios') + op.drop_table('rbac_papel_permissoes') + op.drop_table('rbac_permissoes') + op.drop_table('rbac_papeis') + op.drop_table('inquilinos') + # ### end Alembic comman \ No newline at end of file diff --git a/alembic/versions/shared/8b5eaa353af9_nome_completo.py b/alembic/versions/shared/8b5eaa353af9_nome_completo.py new file mode 100644 index 0000000..d77fff8 --- /dev/null +++ b/alembic/versions/shared/8b5eaa353af9_nome_completo.py @@ -0,0 +1,31 @@ +"""nome completo + +Revision ID: 8b5eaa353af9 +Revises: 223ec99140a9 +Create Date: 2024-12-29 04:23:00.693428 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = '8b5eaa353af9' +down_revision: Union[str, None] = '223ec99140a9' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('rbac_usuarios', sa.Column('nome_completo', sa.String(length=100), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('rbac_usuarios', 'nome_completo') + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/03f34e0286e2_transacao_comercial.py b/alembic/versions/tenants/03f34e0286e2_transacao_comercial.py new file mode 100644 index 0000000..6187749 --- /dev/null +++ b/alembic/versions/tenants/03f34e0286e2_transacao_comercial.py @@ -0,0 +1,38 @@ +"""Transacao Comercial + +Revision ID: 03f34e0286e2 +Revises: cef79ed4a795 +Create Date: 2025-03-10 12:31:08.864786 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + +# revision identifiers, used by Alembic. +revision: str = '03f34e0286e2' +down_revision: Union[str, None] = 'cef79ed4a795' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + +# Defina o nome do tipo enum e os valores +enum_name = "comercialtransacaocomercialenum" +enum_values = ["PAGAMENTO", "RECEBIMENTO", "AMBOS"] + + +def upgrade() -> None: + sa.Enum(*enum_values, name=enum_name).create(op.get_bind()) + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('comercial_relacoes_comercial', sa.Column('transacao_comercial', + sa.Enum('PAGAMENTO', 'RECEBIMENTO', 'AMBOS', + name='comercialtransacaocomercialenum', + inherit_schema=True), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('comercial_relacoes_comercial', 'transacao_comercial') + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/15bf111b7392_ajuste_campos_itens_equipamentos.py b/alembic/versions/tenants/15bf111b7392_ajuste_campos_itens_equipamentos.py new file mode 100644 index 0000000..8a88d99 --- /dev/null +++ b/alembic/versions/tenants/15bf111b7392_ajuste_campos_itens_equipamentos.py @@ -0,0 +1,33 @@ +"""Ajuste Campos Itens Equipamentos + +Revision ID: 15bf111b7392 +Revises: 1f0b163b5dc0 +Create Date: 2025-04-20 16:56:06.263869 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + +# revision identifiers, used by Alembic. +revision: str = '15bf111b7392' +down_revision: Union[str, None] = '1f0b163b5dc0' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('estoque_itens_equipamentos', 'itens_equipamentos_ativo') + # ### end Alembic commands ### + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_manutencao', + existing_type=sa.Boolean(), server_default=sa.text('false'), existing_nullable=False) + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('estoque_itens_equipamentos', + sa.Column('itens_equipamentos_ativo', sa.BOOLEAN(), autoincrement=False, nullable=False)) + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/17c67edadd6a_correção_campo_permitir_campo_nulo.py b/alembic/versions/tenants/17c67edadd6a_correção_campo_permitir_campo_nulo.py new file mode 100644 index 0000000..12d1cea --- /dev/null +++ b/alembic/versions/tenants/17c67edadd6a_correção_campo_permitir_campo_nulo.py @@ -0,0 +1,287 @@ +"""Correção campo permitir campo nulo + +Revision ID: 17c67edadd6a +Revises: f7d6f6b09b0b +Create Date: 2024-12-14 17:51:50.439445 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = '17c67edadd6a' +down_revision: Union[str, None] = 'f7d6f6b09b0b' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('comercial_enderecos', 'endereco_pessoa_descricao', + existing_type=sa.VARCHAR(length=50), + nullable=True) + op.alter_column('comercial_enderecos', 'endereco_pessoa_numero', + existing_type=sa.VARCHAR(length=8), + nullable=True) + op.alter_column('comercial_enderecos', 'endereco_pessoa_complemento', + existing_type=sa.VARCHAR(length=50), + nullable=True) + op.alter_column('comercial_enderecos', 'endereco_pessoa_cep', + existing_type=sa.VARCHAR(length=8), + nullable=True) + op.alter_column('comercial_fisicas', 'fisica_cpf', + existing_type=sa.VARCHAR(length=11), + nullable=True) + op.alter_column('comercial_fisicas', 'fisica_rg', + existing_type=sa.VARCHAR(length=20), + nullable=True) + op.alter_column('comercial_fisicas', 'fisica_genero', + existing_type=sa.VARCHAR(length=2), + nullable=True) + op.alter_column('comercial_fisicas', 'fisica_nome', + existing_type=sa.VARCHAR(length=100), + nullable=True) + op.alter_column('comercial_juridicas', 'juridica_cnpj', + existing_type=sa.VARCHAR(length=14), + nullable=True) + op.alter_column('comercial_juridicas', 'juridica_email_fiscal', + existing_type=sa.VARCHAR(length=100), + nullable=True) + op.alter_column('comercial_juridicas', 'juridica_insc_est', + existing_type=sa.VARCHAR(length=50), + nullable=True) + op.alter_column('comercial_juridicas', 'juridica_ins_mun', + existing_type=sa.VARCHAR(length=50), + nullable=True) + op.alter_column('comercial_juridicas', 'juridica_razao_social', + existing_type=sa.VARCHAR(length=200), + nullable=True) + op.alter_column('comercial_juridicas', 'juridica_representante', + existing_type=sa.VARCHAR(length=100), + nullable=True) + op.alter_column('comercial_pessoas', 'pessoa_telefone', + existing_type=sa.VARCHAR(length=20), + nullable=True) + op.alter_column('comercial_pessoas', 'pessoa_celular', + existing_type=sa.VARCHAR(length=20), + nullable=True) + op.alter_column('comercial_pessoas', 'pessoa_tipo', + existing_type=sa.VARCHAR(length=1), + nullable=True) + op.alter_column('comercial_pessoas', 'pessoa_email', + existing_type=sa.VARCHAR(length=100), + nullable=True) + op.alter_column('estoque_equipamentos', 'equipamento_nome', + existing_type=sa.VARCHAR(length=50), + nullable=True) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_ns', + existing_type=sa.VARCHAR(length=50), + nullable=True) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_patrimonio', + existing_type=sa.VARCHAR(length=10), + nullable=True) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_data_compra', + existing_type=sa.DATE(), + nullable=True) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_prazo_garantia', + existing_type=sa.DATE(), + nullable=True) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_voltagem', + existing_type=sa.VARCHAR(length=1), + nullable=True) + op.alter_column('estoque_manutencoes_equipamentos', 'manutencao_equipamento_data_entrada', + existing_type=sa.DATE(), + nullable=True) + op.alter_column('estoque_manutencoes_equipamentos', 'manutencao_equipamento_defeito_apresentado', + existing_type=sa.VARCHAR(length=100), + nullable=True) + op.alter_column('financeiro_categorias', 'categorias_nome', + existing_type=sa.VARCHAR(length=20), + nullable=True) + op.alter_column('financeiro_centros_custo', 'centros_custo_nome', + existing_type=sa.VARCHAR(length=20), + nullable=True) + op.alter_column('financeiro_centros_custo', 'centros_custo_descricao', + existing_type=sa.VARCHAR(length=100), + nullable=True) + op.alter_column('financeiro_contas', 'contas_data_emissao', + existing_type=sa.DATE(), + nullable=True) + op.alter_column('financeiro_contas', 'contas_data_vencimento', + existing_type=sa.DATE(), + nullable=True) + op.alter_column('financeiro_contas', 'contas_descricao', + existing_type=sa.VARCHAR(length=200), + nullable=True) + op.alter_column('financeiro_contas_corrente', 'contas_corrente_nome_conta', + existing_type=sa.VARCHAR(length=50), + nullable=True) + op.alter_column('financeiro_contas_corrente', 'contas_corrente_descricao', + existing_type=sa.VARCHAR(length=100), + nullable=True) + op.alter_column('financeiro_formas_pagamento', 'formas_pagamento_descricao', + existing_type=sa.VARCHAR(length=20), + nullable=True) + op.alter_column('financeiro_movimentacoes_conta', 'movimentacoes_conta_descricao', + existing_type=sa.VARCHAR(length=200), + nullable=True) + op.alter_column('financeiro_status', 'statuss_nome_status', + existing_type=sa.VARCHAR(length=20), + nullable=True) + op.alter_column('financeiro_status', 'statuss_descricao', + existing_type=sa.VARCHAR(length=200), + nullable=True) + op.alter_column('historico_alteracoes', 'tabela', + existing_type=sa.VARCHAR(length=100), + nullable=True) + op.alter_column('historico_alteracoes', 'action', + existing_type=sa.VARCHAR(length=10), + nullable=True) + op.alter_column('historico_update', 'coluna', + existing_type=sa.VARCHAR(length=100), + nullable=True) + op.alter_column('historico_update', 'valor_antigo', + existing_type=sa.VARCHAR(length=200), + nullable=True) + op.alter_column('historico_update', 'valor_novo', + existing_type=sa.VARCHAR(length=200), + nullable=True) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('historico_update', 'valor_novo', + existing_type=sa.VARCHAR(length=200), + nullable=False) + op.alter_column('historico_update', 'valor_antigo', + existing_type=sa.VARCHAR(length=200), + nullable=False) + op.alter_column('historico_update', 'coluna', + existing_type=sa.VARCHAR(length=100), + nullable=False) + op.alter_column('historico_alteracoes', 'action', + existing_type=sa.VARCHAR(length=10), + nullable=False) + op.alter_column('historico_alteracoes', 'tabela', + existing_type=sa.VARCHAR(length=100), + nullable=False) + op.alter_column('financeiro_status', 'statuss_descricao', + existing_type=sa.VARCHAR(length=200), + nullable=False) + op.alter_column('financeiro_status', 'statuss_nome_status', + existing_type=sa.VARCHAR(length=20), + nullable=False) + op.alter_column('financeiro_movimentacoes_conta', 'movimentacoes_conta_descricao', + existing_type=sa.VARCHAR(length=200), + nullable=False) + op.alter_column('financeiro_formas_pagamento', 'formas_pagamento_descricao', + existing_type=sa.VARCHAR(length=20), + nullable=False) + op.alter_column('financeiro_contas_corrente', 'contas_corrente_descricao', + existing_type=sa.VARCHAR(length=100), + nullable=False) + op.alter_column('financeiro_contas_corrente', 'contas_corrente_nome_conta', + existing_type=sa.VARCHAR(length=50), + nullable=False) + op.alter_column('financeiro_contas', 'contas_descricao', + existing_type=sa.VARCHAR(length=200), + nullable=False) + op.alter_column('financeiro_contas', 'contas_data_vencimento', + existing_type=sa.DATE(), + nullable=False) + op.alter_column('financeiro_contas', 'contas_data_emissao', + existing_type=sa.DATE(), + nullable=False) + op.alter_column('financeiro_centros_custo', 'centros_custo_descricao', + existing_type=sa.VARCHAR(length=100), + nullable=False) + op.alter_column('financeiro_centros_custo', 'centros_custo_nome', + existing_type=sa.VARCHAR(length=20), + nullable=False) + op.alter_column('financeiro_categorias', 'categorias_nome', + existing_type=sa.VARCHAR(length=20), + nullable=False) + op.alter_column('estoque_manutencoes_equipamentos', 'manutencao_equipamento_defeito_apresentado', + existing_type=sa.VARCHAR(length=100), + nullable=False) + op.alter_column('estoque_manutencoes_equipamentos', 'manutencao_equipamento_data_entrada', + existing_type=sa.DATE(), + nullable=False) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_voltagem', + existing_type=sa.VARCHAR(length=1), + nullable=False) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_prazo_garantia', + existing_type=sa.DATE(), + nullable=False) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_data_compra', + existing_type=sa.DATE(), + nullable=False) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_patrimonio', + existing_type=sa.VARCHAR(length=10), + nullable=False) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_ns', + existing_type=sa.VARCHAR(length=50), + nullable=False) + op.alter_column('estoque_equipamentos', 'equipamento_nome', + existing_type=sa.VARCHAR(length=50), + nullable=False) + op.alter_column('comercial_pessoas', 'pessoa_email', + existing_type=sa.VARCHAR(length=100), + nullable=False) + op.alter_column('comercial_pessoas', 'pessoa_tipo', + existing_type=sa.VARCHAR(length=1), + nullable=False) + op.alter_column('comercial_pessoas', 'pessoa_celular', + existing_type=sa.VARCHAR(length=20), + nullable=False) + op.alter_column('comercial_pessoas', 'pessoa_telefone', + existing_type=sa.VARCHAR(length=20), + nullable=False) + op.alter_column('comercial_juridicas', 'juridica_representante', + existing_type=sa.VARCHAR(length=100), + nullable=False) + op.alter_column('comercial_juridicas', 'juridica_razao_social', + existing_type=sa.VARCHAR(length=200), + nullable=False) + op.alter_column('comercial_juridicas', 'juridica_ins_mun', + existing_type=sa.VARCHAR(length=50), + nullable=False) + op.alter_column('comercial_juridicas', 'juridica_insc_est', + existing_type=sa.VARCHAR(length=50), + nullable=False) + op.alter_column('comercial_juridicas', 'juridica_email_fiscal', + existing_type=sa.VARCHAR(length=100), + nullable=False) + op.alter_column('comercial_juridicas', 'juridica_cnpj', + existing_type=sa.VARCHAR(length=14), + nullable=False) + op.alter_column('comercial_fisicas', 'fisica_nome', + existing_type=sa.VARCHAR(length=100), + nullable=False) + op.alter_column('comercial_fisicas', 'fisica_genero', + existing_type=sa.VARCHAR(length=2), + nullable=False) + op.alter_column('comercial_fisicas', 'fisica_rg', + existing_type=sa.VARCHAR(length=20), + nullable=False) + op.alter_column('comercial_fisicas', 'fisica_cpf', + existing_type=sa.VARCHAR(length=11), + nullable=False) + op.alter_column('comercial_enderecos', 'endereco_pessoa_cep', + existing_type=sa.VARCHAR(length=8), + nullable=False) + op.alter_column('comercial_enderecos', 'endereco_pessoa_complemento', + existing_type=sa.VARCHAR(length=50), + nullable=False) + op.alter_column('comercial_enderecos', 'endereco_pessoa_numero', + existing_type=sa.VARCHAR(length=8), + nullable=False) + op.alter_column('comercial_enderecos', 'endereco_pessoa_descricao', + existing_type=sa.VARCHAR(length=50), + nullable=False) + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/1f0b163b5dc0_remocao_campo_ativo_fora_padrao.py b/alembic/versions/tenants/1f0b163b5dc0_remocao_campo_ativo_fora_padrao.py new file mode 100644 index 0000000..a2419a5 --- /dev/null +++ b/alembic/versions/tenants/1f0b163b5dc0_remocao_campo_ativo_fora_padrao.py @@ -0,0 +1,35 @@ +"""Remocao Campo ativo fora padrao + +Revision ID: 1f0b163b5dc0 +Revises: 5ffe665c5be9 +Create Date: 2025-03-17 08:40:40.599043 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = '1f0b163b5dc0' +down_revision: Union[str, None] = '5ffe665c5be9' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('estoque_equipamentos', 'equipamento_ativo') + op.drop_column('estoque_setores', 'setor_ativo') + op.drop_column('estoque_tipos_equipamentos', 'tipo_equipamento_ativo') + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('estoque_tipos_equipamentos', sa.Column('tipo_equipamento_ativo', sa.BOOLEAN(), server_default=sa.text('true'), autoincrement=False, nullable=False)) + op.add_column('estoque_setores', sa.Column('setor_ativo', sa.BOOLEAN(), server_default=sa.text('true'), autoincrement=False, nullable=False)) + op.add_column('estoque_equipamentos', sa.Column('equipamento_ativo', sa.BOOLEAN(), server_default=sa.text('true'), autoincrement=False, nullable=False)) + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/3aee83e24ef5_boleano_manutenção_ajuste.py b/alembic/versions/tenants/3aee83e24ef5_boleano_manutenção_ajuste.py new file mode 100644 index 0000000..ee8356d --- /dev/null +++ b/alembic/versions/tenants/3aee83e24ef5_boleano_manutenção_ajuste.py @@ -0,0 +1,41 @@ +"""Boleano Manutenção Ajuste + +Revision ID: 3aee83e24ef5 +Revises: f893530f5281 +Create Date: 2025-01-27 18:25:44.336991 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = '3aee83e24ef5' +down_revision: Union[str, None] = 'f893530f5281' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_ativo', + existing_type=sa.BOOLEAN(), + nullable=False) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_manutencao', + existing_type=sa.BOOLEAN(), + nullable=False) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_manutencao', + existing_type=sa.BOOLEAN(), + nullable=True) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_ativo', + existing_type=sa.BOOLEAN(), + nullable=True) + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/405e3f59e8af_colunas_extras_tabela_associativa.py b/alembic/versions/tenants/405e3f59e8af_colunas_extras_tabela_associativa.py new file mode 100644 index 0000000..922fb87 --- /dev/null +++ b/alembic/versions/tenants/405e3f59e8af_colunas_extras_tabela_associativa.py @@ -0,0 +1,43 @@ +"""Colunas extras Tabela Associativa + +Revision ID: 405e3f59e8af +Revises: 997336d58696 +Create Date: 2025-02-04 06:59:12.882748 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = '405e3f59e8af' +down_revision: Union[str, None] = '997336d58696' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('estoque_manutencao_itens_equipamentos', sa.Column('manutencao_equipamento_defeito_apresentado', sa.String(length=100), nullable=True)) + op.add_column('estoque_manutencao_itens_equipamentos', sa.Column('manutencao_equipamento_correcao_realizada', sa.Text(), nullable=True)) + op.add_column('estoque_manutencao_itens_equipamentos', sa.Column('manutencao_equipamento_data_retorno', sa.Date(), nullable=True)) + op.drop_constraint('fk_ct_estoque_manutencao_itens_equipamentos_fk_manutenc_35f1', 'estoque_manutencao_itens_equipamentos', type_='foreignkey') + op.drop_constraint('fk_ct_estoque_manutencao_itens_equipamentos_fk_itens_eq_ccb9', 'estoque_manutencao_itens_equipamentos', type_='foreignkey') + op.create_foreign_key(op.f('fk_ct_estoque_manutencao_itens_equipamentos_fk_itens_equipamentos_uuid_estoque_itens_equipamentos'), 'estoque_manutencao_itens_equipamentos', 'estoque_itens_equipamentos', ['fk_itens_equipamentos_uuid'], ['uuid'], ondelete='CASCADE') + op.create_foreign_key(op.f('fk_ct_estoque_manutencao_itens_equipamentos_fk_manutencao_uuid_estoque_manutencoes_equipamentos'), 'estoque_manutencao_itens_equipamentos', 'estoque_manutencoes_equipamentos', ['fk_manutencao_uuid'], ['uuid'], ondelete='CASCADE') + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(op.f('fk_ct_estoque_manutencao_itens_equipamentos_fk_manutencao_uuid_estoque_manutencoes_equipamentos'), 'estoque_manutencao_itens_equipamentos', type_='foreignkey') + op.drop_constraint(op.f('fk_ct_estoque_manutencao_itens_equipamentos_fk_itens_equipamentos_uuid_estoque_itens_equipamentos'), 'estoque_manutencao_itens_equipamentos', type_='foreignkey') + op.create_foreign_key('fk_ct_estoque_manutencao_itens_equipamentos_fk_itens_eq_ccb9', 'estoque_manutencao_itens_equipamentos', 'estoque_itens_equipamentos', ['fk_itens_equipamentos_uuid'], ['uuid']) + op.create_foreign_key('fk_ct_estoque_manutencao_itens_equipamentos_fk_manutenc_35f1', 'estoque_manutencao_itens_equipamentos', 'estoque_manutencoes_equipamentos', ['fk_manutencao_uuid'], ['uuid']) + op.drop_column('estoque_manutencao_itens_equipamentos', 'manutencao_equipamento_data_retorno') + op.drop_column('estoque_manutencao_itens_equipamentos', 'manutencao_equipamento_correcao_realizada') + op.drop_column('estoque_manutencao_itens_equipamentos', 'manutencao_equipamento_defeito_apresentado') + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/52048623d0cc_initial_migration_tenant.py b/alembic/versions/tenants/52048623d0cc_initial_migration_tenant.py new file mode 100644 index 0000000..2a509d1 --- /dev/null +++ b/alembic/versions/tenants/52048623d0cc_initial_migration_tenant.py @@ -0,0 +1,305 @@ +"""Initial Migration Tenant + +Revision ID: 52048623d0cc +Revises: +Create Date: 2024-11-30 17:40:30.077753 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = '52048623d0cc' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('comercial_pessoas', + sa.Column('pessoa_status', sa.Boolean(), nullable=True), + sa.Column('pessoa_telefone', sa.String(length=20), nullable=False), + sa.Column('pessoa_celular', sa.String(length=20), nullable=False), + sa.Column('pessoa_tipo', sa.String(length=1), nullable=False), + sa.Column('pessoa_email', sa.String(length=100), nullable=False), + sa.Column('pessoa_local_evento', sa.Boolean(), nullable=True), + sa.Column('uuid', sa.UUID(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=True), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_comercial_pessoas')) + ) + op.create_index(op.f('ix_ct_comercial_pessoas_updated_at'), 'comercial_pessoas', ['updated_at'], unique=False) + op.create_table('comercial_relacoes_comercial', + sa.Column('descricao_relacao_comercial', sa.String(length=30), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=True), + sa.Column('uuid', sa.UUID(), nullable=False), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_comercial_relacoes_comercial')) + ) + op.create_index(op.f('ix_ct_comercial_relacoes_comercial_updated_at'), 'comercial_relacoes_comercial', ['updated_at'], unique=False) + op.create_table('comercial_tipos_endereco', + sa.Column('tipo_endereco_descricao', sa.String(length=30), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=True), + sa.Column('uuid', sa.UUID(), nullable=False), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_comercial_tipos_endereco')) + ) + op.create_index(op.f('ix_ct_comercial_tipos_endereco_updated_at'), 'comercial_tipos_endereco', ['updated_at'], unique=False) + op.create_table('estoque_setores', + sa.Column('setor_nome', sa.String(length=30), nullable=False), + sa.Column('uuid', sa.UUID(), nullable=False), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_estoque_setores')) + ) + op.create_table('financeiro_categorias', + sa.Column('categorias_nome', sa.String(length=20), nullable=False), + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.PrimaryKeyConstraint('id', name=op.f('pk_ct_financeiro_categorias')) + ) + op.create_table('financeiro_centros_custo', + sa.Column('centros_custo_nome', sa.String(length=20), nullable=False), + sa.Column('centros_custo_descricao', sa.String(length=100), nullable=False), + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.PrimaryKeyConstraint('id', name=op.f('pk_ct_financeiro_centros_custo')) + ) + op.create_table('financeiro_contas_corrente', + sa.Column('contas_corrente_nome_conta', sa.String(length=50), nullable=False), + sa.Column('contas_corrente_saldo_inicial', sa.Numeric(precision=10, scale=2), nullable=False), + sa.Column('contas_corrente_data_criacao', sa.Date(), nullable=False), + sa.Column('contas_corrente_descricao', sa.String(length=100), nullable=False), + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.PrimaryKeyConstraint('id', name=op.f('pk_ct_financeiro_contas_corrente')) + ) + op.create_index(op.f('ix_ct_financeiro_contas_corrente_contas_corrente_data_criacao'), 'financeiro_contas_corrente', ['contas_corrente_data_criacao'], unique=False) + op.create_table('financeiro_formas_pagamento', + sa.Column('formas_pagamento_descricao', sa.String(length=20), nullable=False), + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.PrimaryKeyConstraint('id', name=op.f('pk_ct_financeiro_formas_pagamento')) + ) + op.create_table('financeiro_status', + sa.Column('statuss_nome_status', sa.String(length=20), nullable=False), + sa.Column('statuss_descricao', sa.String(length=200), nullable=False), + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.PrimaryKeyConstraint('id', name=op.f('pk_ct_financeiro_status')) + ) + op.create_table('historico_alteracoes', + sa.Column('tabela', sa.String(length=100), nullable=False), + sa.Column('data_modificacao', sa.DateTime(), nullable=False), + sa.Column('action', sa.String(length=10), nullable=False), + sa.Column('usuario_id', sa.String(length=36), nullable=True), + sa.Column('registro_id', sa.String(length=36), nullable=False), + sa.Column('uuid', sa.UUID(), nullable=False), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_historico_alteracoes')) + ) + op.create_table('comercial_enderecos', + sa.Column('endereco_pessoa_status', sa.Boolean(), nullable=True), + sa.Column('endereco_pessoa_descricao', sa.String(length=50), nullable=False), + sa.Column('endereco_pessoa_numero', sa.String(length=8), nullable=False), + sa.Column('endereco_pessoa_complemento', sa.String(length=50), nullable=False), + sa.Column('endereco_pessoa_cep', sa.String(length=8), nullable=False), + sa.Column('fk_pessoa_uuid', sa.UUID(), nullable=False), + sa.Column('fk_tipo_endereco_uuid', sa.UUID(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=True), + sa.Column('uuid', sa.UUID(), nullable=False), + sa.ForeignKeyConstraint(['fk_pessoa_uuid'], ['comercial_pessoas.uuid'], name=op.f('fk_ct_comercial_enderecos_fk_pessoa_uuid_comercial_pessoas'), ondelete='CASCADE'), + sa.ForeignKeyConstraint(['fk_tipo_endereco_uuid'], ['comercial_tipos_endereco.uuid'], name=op.f('fk_ct_comercial_enderecos_fk_tipo_endereco_uuid_comercial_tipos_endereco'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_comercial_enderecos')) + ) + op.create_index(op.f('ix_ct_comercial_enderecos_updated_at'), 'comercial_enderecos', ['updated_at'], unique=False) + op.create_table('comercial_fisicas', + sa.Column('uuid', sa.UUID(), nullable=False), + sa.Column('fisica_cpf', sa.String(length=11), nullable=False), + sa.Column('fisica_rg', sa.String(length=20), nullable=False), + sa.Column('fisica_genero', sa.String(length=2), nullable=False), + sa.Column('fisica_nome', sa.String(length=100), nullable=False), + sa.ForeignKeyConstraint(['uuid'], ['comercial_pessoas.uuid'], name=op.f('fk_ct_comercial_fisicas_uuid_comercial_pessoas'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_comercial_fisicas')) + ) + op.create_table('comercial_juridicas', + sa.Column('uuid', sa.UUID(), nullable=False), + sa.Column('juridica_cnpj', sa.String(length=14), nullable=False), + sa.Column('juridica_email_fiscal', sa.String(length=100), nullable=False), + sa.Column('juridica_insc_est', sa.String(length=50), nullable=False), + sa.Column('juridica_ins_mun', sa.String(length=50), nullable=False), + sa.Column('juridica_razao_social', sa.String(length=200), nullable=False), + sa.Column('juridica_representante', sa.String(length=100), nullable=False), + sa.ForeignKeyConstraint(['uuid'], ['comercial_pessoas.uuid'], name=op.f('fk_ct_comercial_juridicas_uuid_comercial_pessoas'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_comercial_juridicas')) + ) + op.create_table('comercial_relacionamento_pessoa_empresa', + sa.Column('relacao_comercial_uuid', sa.UUID(), nullable=True), + sa.Column('pessoa_uuid', sa.UUID(), nullable=True), + sa.ForeignKeyConstraint(['pessoa_uuid'], ['comercial_pessoas.uuid'], name=op.f('fk_ct_comercial_relacionamento_pessoa_empresa_pessoa_uuid_comercial_pessoas'), ondelete='CASCADE'), + sa.ForeignKeyConstraint(['relacao_comercial_uuid'], ['comercial_relacoes_comercial.uuid'], name=op.f('fk_ct_comercial_relacionamento_pessoa_empresa_relacao_comercial_uuid_comercial_relacoes_comercial'), ondelete='CASCADE') + ) + op.create_table('estoque_tipos_equipamentos', + sa.Column('tipo_equipamento_nome', sa.String(length=50), nullable=False), + sa.Column('fk_setor_uuid', sa.UUID(), nullable=False), + sa.Column('uuid', sa.UUID(), nullable=False), + sa.ForeignKeyConstraint(['fk_setor_uuid'], ['estoque_setores.uuid'], name=op.f('fk_ct_estoque_tipos_equipamentos_fk_setor_uuid_estoque_setores'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_estoque_tipos_equipamentos')) + ) + op.create_table('financeiro_contas', + sa.Column('contas_tipo_conta', sa.Enum('PAGAR', 'RECEBER', name='financeirotipocontaenum', inherit_schema=True), nullable=False), + sa.Column('contas_data_emissao', sa.Date(), nullable=False), + sa.Column('contas_data_vencimento', sa.Date(), nullable=False), + sa.Column('contas_valor_total', sa.Numeric(precision=10, scale=2), nullable=False), + sa.Column('contas_valor_juros', sa.Numeric(precision=10, scale=2), nullable=False), + sa.Column('contas_valor_multa', sa.Numeric(precision=10, scale=2), nullable=False), + sa.Column('contas_valor_desconto', sa.Numeric(precision=10, scale=2), nullable=False), + sa.Column('contas_descricao', sa.String(length=200), nullable=False), + sa.Column('fk_pessoas_uuid', sa.UUID(), nullable=False), + sa.Column('fk_statuss_id', sa.Integer(), nullable=False), + sa.Column('fk_categorias_id', sa.Integer(), nullable=False), + sa.Column('fk_centros_custo_id', sa.Integer(), nullable=False), + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.ForeignKeyConstraint(['fk_categorias_id'], ['financeiro_categorias.id'], name=op.f('fk_ct_financeiro_contas_fk_categorias_id_financeiro_categorias'), ondelete='CASCADE'), + sa.ForeignKeyConstraint(['fk_centros_custo_id'], ['financeiro_centros_custo.id'], name=op.f('fk_ct_financeiro_contas_fk_centros_custo_id_financeiro_centros_custo'), ondelete='CASCADE'), + sa.ForeignKeyConstraint(['fk_pessoas_uuid'], ['comercial_pessoas.uuid'], name=op.f('fk_ct_financeiro_contas_fk_pessoas_uuid_comercial_pessoas'), ondelete='CASCADE'), + sa.ForeignKeyConstraint(['fk_statuss_id'], ['financeiro_status.id'], name=op.f('fk_ct_financeiro_contas_fk_statuss_id_financeiro_status'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id', name=op.f('pk_ct_financeiro_contas')) + ) + op.create_table('historico_delete', + sa.Column('fk_historico_alteracoes_uuid', sa.UUID(), nullable=False), + sa.Column('registro_deletado', sa.Text(), nullable=False), + sa.Column('uuid', sa.UUID(), nullable=False), + sa.ForeignKeyConstraint(['fk_historico_alteracoes_uuid'], ['historico_alteracoes.uuid'], name=op.f('fk_ct_historico_delete_fk_historico_alteracoes_uuid_historico_alteracoes'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_historico_delete')) + ) + op.create_table('historico_update', + sa.Column('fk_historico_alteracoes_uuid', sa.UUID(), nullable=False), + sa.Column('coluna', sa.String(length=100), nullable=False), + sa.Column('valor_antigo', sa.String(length=200), nullable=False), + sa.Column('valor_novo', sa.String(length=200), nullable=False), + sa.Column('uuid', sa.UUID(), nullable=False), + sa.ForeignKeyConstraint(['fk_historico_alteracoes_uuid'], ['historico_alteracoes.uuid'], name=op.f('fk_ct_historico_update_fk_historico_alteracoes_uuid_historico_alteracoes'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_historico_update')) + ) + op.create_table('estoque_equipamentos', + sa.Column('equipamento_nome', sa.String(length=50), nullable=False), + sa.Column('equipamento_informacoes', sa.Text(), nullable=False), + sa.Column('fk_tipo_equipamento_uuid', sa.UUID(), nullable=False), + sa.Column('uuid', sa.UUID(), nullable=False), + sa.ForeignKeyConstraint(['fk_tipo_equipamento_uuid'], ['estoque_tipos_equipamentos.uuid'], name=op.f('fk_ct_estoque_equipamentos_fk_tipo_equipamento_uuid_estoque_tipos_equipamentos'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_estoque_equipamentos')) + ) + op.create_table('financeiro_parcelas', + sa.Column('parcelas_numero_parcela', sa.Integer(), nullable=False), + sa.Column('parcelas_valor_parcela', sa.Numeric(precision=10, scale=2), nullable=False), + sa.Column('parcelas_valor_juros', sa.Numeric(precision=10, scale=2), nullable=False), + sa.Column('parcelas_valor_multa', sa.Numeric(precision=10, scale=2), nullable=False), + sa.Column('parcelas_valor_desconto', sa.Numeric(precision=10, scale=2), nullable=False), + sa.Column('parcelas_data_vencimento', sa.Date(), nullable=False), + sa.Column('fk_contas_id', sa.Integer(), nullable=False), + sa.Column('fk_statuss_id', sa.Integer(), nullable=False), + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.ForeignKeyConstraint(['fk_contas_id'], ['financeiro_contas.id'], name=op.f('fk_ct_financeiro_parcelas_fk_contas_id_financeiro_contas'), ondelete='CASCADE'), + sa.ForeignKeyConstraint(['fk_statuss_id'], ['financeiro_status.id'], name=op.f('fk_ct_financeiro_parcelas_fk_statuss_id_financeiro_status'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id', name=op.f('pk_ct_financeiro_parcelas')) + ) + op.create_index(op.f('ix_ct_financeiro_parcelas_parcelas_data_vencimento'), 'financeiro_parcelas', ['parcelas_data_vencimento'], unique=False) + op.create_table('estoque_itens_equipamentos', + sa.Column('itens_equipamentos_ns', sa.String(length=50), nullable=False), + sa.Column('itens_equipamentos_patrimonio', sa.String(length=10), nullable=False), + sa.Column('itens_equipamentos_data_compra', sa.Date(), nullable=False), + sa.Column('itens_equipamentos_prazo_garantia', sa.Date(), nullable=False), + sa.Column('itens_equipamentos_voltagem', sa.String(length=1), nullable=False), + sa.Column('itens_equipamentos_valor_aquisicao', sa.Numeric(precision=10, scale=2), nullable=False), + sa.Column('itens_equipamentos_rfid_uid', sa.String(length=39), nullable=False), + sa.Column('fk_equipamento_uuid', sa.UUID(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=True), + sa.Column('uuid', sa.UUID(), nullable=False), + sa.ForeignKeyConstraint(['fk_equipamento_uuid'], ['estoque_equipamentos.uuid'], name=op.f('fk_ct_estoque_itens_equipamentos_fk_equipamento_uuid_estoque_equipamentos'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_estoque_itens_equipamentos')) + ) + op.create_index(op.f('ix_ct_estoque_itens_equipamentos_updated_at'), 'estoque_itens_equipamentos', ['updated_at'], unique=False) + op.create_table('financeiro_pagamentos', + sa.Column('data_pagamento', sa.Date(), nullable=False), + sa.Column('valor_pago', sa.Numeric(precision=10, scale=2), nullable=False), + sa.Column('observacao', sa.Text(), nullable=False), + sa.Column('fk_parcelas_id', sa.Integer(), nullable=False), + sa.Column('fk_contas_corrente_id', sa.Integer(), nullable=False), + sa.Column('fk_formas_pagamento_id', sa.Integer(), nullable=False), + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.ForeignKeyConstraint(['fk_contas_corrente_id'], ['financeiro_contas_corrente.id'], name=op.f('fk_ct_financeiro_pagamentos_fk_contas_corrente_id_financeiro_contas_corrente'), ondelete='CASCADE'), + sa.ForeignKeyConstraint(['fk_formas_pagamento_id'], ['financeiro_formas_pagamento.id'], name=op.f('fk_ct_financeiro_pagamentos_fk_formas_pagamento_id_financeiro_formas_pagamento'), ondelete='CASCADE'), + sa.ForeignKeyConstraint(['fk_parcelas_id'], ['financeiro_parcelas.id'], name=op.f('fk_ct_financeiro_pagamentos_fk_parcelas_id_financeiro_parcelas'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id', name=op.f('pk_ct_financeiro_pagamentos')) + ) + op.create_index(op.f('ix_ct_financeiro_pagamentos_data_pagamento'), 'financeiro_pagamentos', ['data_pagamento'], unique=False) + op.create_table('estoque_manutencoes_equipamentos', + sa.Column('manutencao_equipamento_data_entrada', sa.Date(), nullable=False), + sa.Column('manutencao_equipamento_data_retorno', sa.Date(), nullable=True), + sa.Column('manutencao_equipamento_defeito_apresentado', sa.String(length=100), nullable=False), + sa.Column('manutencao_equipamento_correcao_realizada', sa.Text(), nullable=False), + sa.Column('fk_itens_equipamentos_uuid', sa.UUID(), nullable=False), + sa.Column('uuid', sa.UUID(), nullable=False), + sa.ForeignKeyConstraint(['fk_itens_equipamentos_uuid'], ['estoque_itens_equipamentos.uuid'], name=op.f('fk_ct_estoque_manutencoes_equipamentos_fk_itens_equipamentos_uuid_estoque_itens_equipamentos'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_estoque_manutencoes_equipamentos')) + ) + op.create_table('financeiro_movimentacoes_conta', + sa.Column('movimentacoes_conta_tipo_movimentacao', sa.Enum('CREDITO', 'DEBITO', name='financeirotipomovimentacaoenum', inherit_schema=True), nullable=False), + sa.Column('movimentacoes_conta_valor_movimentacao', sa.Numeric(precision=10, scale=2), nullable=False), + sa.Column('movimentacoes_conta_data_movimentacao', sa.Date(), nullable=False), + sa.Column('movimentacoes_conta_descricao', sa.String(length=200), nullable=False), + sa.Column('fk_contas_corrente_id', sa.Integer(), nullable=False), + sa.Column('fk_pagamentos_id', sa.Integer(), nullable=False), + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.ForeignKeyConstraint(['fk_contas_corrente_id'], ['financeiro_contas_corrente.id'], name=op.f('fk_ct_financeiro_movimentacoes_conta_fk_contas_corrente_id_financeiro_contas_corrente'), ondelete='CASCADE'), + sa.ForeignKeyConstraint(['fk_pagamentos_id'], ['financeiro_pagamentos.id'], name=op.f('fk_ct_financeiro_movimentacoes_conta_fk_pagamentos_id_financeiro_pagamentos'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id', name=op.f('pk_ct_financeiro_movimentacoes_conta')) + ) + op.create_index(op.f('ix_ct_financeiro_movimentacoes_conta_movimentacoes_conta_data_movimentacao'), 'financeiro_movimentacoes_conta', ['movimentacoes_conta_data_movimentacao'], unique=False) + op.create_table('financeiro_conta_manutencao_equipamentos', + sa.Column('manutencao_uuid', sa.UUID(), nullable=False), + sa.Column('conta_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['conta_id'], ['financeiro_contas.id'], name=op.f('fk_ct_financeiro_conta_manutencao_equipamentos_conta_id_financeiro_contas')), + sa.ForeignKeyConstraint(['manutencao_uuid'], ['estoque_manutencoes_equipamentos.uuid'], name=op.f('fk_ct_financeiro_conta_manutencao_equipamentos_manutencao_uuid_estoque_manutencoes_equipamentos')), + sa.PrimaryKeyConstraint('manutencao_uuid', 'conta_id', name=op.f('pk_ct_financeiro_conta_manutencao_equipamentos')) + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('financeiro_conta_manutencao_equipamentos') + op.drop_index(op.f('ix_ct_financeiro_movimentacoes_conta_movimentacoes_conta_data_movimentacao'), table_name='financeiro_movimentacoes_conta') + op.drop_table('financeiro_movimentacoes_conta') + op.drop_table('estoque_manutencoes_equipamentos') + op.drop_index(op.f('ix_ct_financeiro_pagamentos_data_pagamento'), table_name='financeiro_pagamentos') + op.drop_table('financeiro_pagamentos') + op.drop_index(op.f('ix_ct_estoque_itens_equipamentos_updated_at'), table_name='estoque_itens_equipamentos') + op.drop_table('estoque_itens_equipamentos') + op.drop_index(op.f('ix_ct_financeiro_parcelas_parcelas_data_vencimento'), table_name='financeiro_parcelas') + op.drop_table('financeiro_parcelas') + op.drop_table('estoque_equipamentos') + op.drop_table('historico_update') + op.drop_table('historico_delete') + op.drop_table('financeiro_contas') + op.drop_table('estoque_tipos_equipamentos') + op.drop_table('comercial_relacionamento_pessoa_empresa') + op.drop_table('comercial_juridicas') + op.drop_table('comercial_fisicas') + op.drop_index(op.f('ix_ct_comercial_enderecos_updated_at'), table_name='comercial_enderecos') + op.drop_table('comercial_enderecos') + op.drop_table('historico_alteracoes') + op.drop_table('financeiro_status') + op.drop_table('financeiro_formas_pagamento') + op.drop_index(op.f('ix_ct_financeiro_contas_corrente_contas_corrente_data_criacao'), table_name='financeiro_contas_corrente') + op.drop_table('financeiro_contas_corrente') + op.drop_table('financeiro_centros_custo') + op.drop_table('financeiro_categorias') + op.drop_table('estoque_setores') + op.drop_index(op.f('ix_ct_comercial_tipos_endereco_updated_at'), table_name='comercial_tipos_endereco') + op.drop_table('comercial_tipos_endereco') + op.drop_index(op.f('ix_ct_comercial_relacoes_comercial_updated_at'), table_name='comercial_relacoes_comercial') + op.drop_table('comercial_relacoes_comercial') + op.drop_index(op.f('ix_ct_comercial_pessoas_updated_at'), table_name='comercial_pessoas') + op.drop_table('comercial_pessoas') + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/54212415a2e0_ajuste_relacionamento_tabela_manutenção.py b/alembic/versions/tenants/54212415a2e0_ajuste_relacionamento_tabela_manutenção.py new file mode 100644 index 0000000..643a5c6 --- /dev/null +++ b/alembic/versions/tenants/54212415a2e0_ajuste_relacionamento_tabela_manutenção.py @@ -0,0 +1,43 @@ +"""Ajuste Relacionamento Tabela Manutenção + +Revision ID: 54212415a2e0 +Revises: 3aee83e24ef5 +Create Date: 2025-01-31 11:48:27.771071 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = '54212415a2e0' +down_revision: Union[str, None] = '3aee83e24ef5' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('estoque_manutencao_itens_equipamentos', + sa.Column('fk_manutencao_uuid', sa.UUID(), nullable=False), + sa.Column('fk_itens_equipamentos_uuid', sa.UUID(), nullable=False), + sa.ForeignKeyConstraint(['fk_itens_equipamentos_uuid'], ['estoque_itens_equipamentos.uuid'], name=op.f('fk_ct_estoque_manutencao_itens_equipamentos_fk_itens_equipamentos_uuid_estoque_itens_equipamentos')), + sa.ForeignKeyConstraint(['fk_manutencao_uuid'], ['estoque_manutencoes_equipamentos.uuid'], name=op.f('fk_ct_estoque_manutencao_itens_equipamentos_fk_manutencao_uuid_estoque_manutencoes_equipamentos')), + sa.PrimaryKeyConstraint('fk_manutencao_uuid', 'fk_itens_equipamentos_uuid', name=op.f('pk_ct_estoque_manutencao_itens_equipamentos')) + ) + op.add_column('estoque_equipamentos', sa.Column('equipamento_ativo', sa.Boolean(), nullable=False, server_default=sa.true())) + op.add_column('estoque_setores', sa.Column('setor_ativo', sa.Boolean(), nullable=False, server_default=sa.true())) + op.add_column('estoque_tipos_equipamentos', sa.Column('tipo_equipamento_ativo', sa.Boolean(), nullable=False, server_default=sa.true())) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('estoque_tipos_equipamentos', 'tipo_equipamento_ativo') + op.drop_column('estoque_setores', 'setor_ativo') + op.drop_column('estoque_equipamentos', 'equipamento_ativo') + op.drop_table('estoque_manutencao_itens_equipamentos') + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/5ffe665c5be9_campo_ativo.py b/alembic/versions/tenants/5ffe665c5be9_campo_ativo.py new file mode 100644 index 0000000..6be0584 --- /dev/null +++ b/alembic/versions/tenants/5ffe665c5be9_campo_ativo.py @@ -0,0 +1,65 @@ +"""Campo Ativo + +Revision ID: 5ffe665c5be9 +Revises: aabed61bf4b6 +Create Date: 2025-03-17 08:21:36.254705 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = '5ffe665c5be9' +down_revision: Union[str, None] = 'aabed61bf4b6' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('comercial_pessoas', sa.Column('ativo', sa.Boolean(), server_default=sa.text('true'), nullable=False)) + op.add_column('comercial_pessoas', sa.Column('data_ativacao', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=False)) + op.add_column('comercial_pessoas', sa.Column('data_desativacao', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=True)) + op.add_column('comercial_relacoes_comercial', sa.Column('ativo', sa.Boolean(), server_default=sa.text('true'), nullable=False)) + op.add_column('comercial_relacoes_comercial', sa.Column('data_ativacao', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=False)) + op.add_column('comercial_relacoes_comercial', sa.Column('data_desativacao', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=True)) + op.add_column('estoque_equipamentos', sa.Column('ativo', sa.Boolean(), server_default=sa.text('true'), nullable=False)) + op.add_column('estoque_equipamentos', sa.Column('data_ativacao', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=False)) + op.add_column('estoque_equipamentos', sa.Column('data_desativacao', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=True)) + op.add_column('estoque_itens_equipamentos', sa.Column('ativo', sa.Boolean(), server_default=sa.text('true'), nullable=False)) + op.add_column('estoque_itens_equipamentos', sa.Column('data_ativacao', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=False)) + op.add_column('estoque_itens_equipamentos', sa.Column('data_desativacao', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=True)) + op.add_column('estoque_setores', sa.Column('ativo', sa.Boolean(), server_default=sa.text('true'), nullable=False)) + op.add_column('estoque_setores', sa.Column('data_ativacao', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=False)) + op.add_column('estoque_setores', sa.Column('data_desativacao', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=True)) + op.add_column('estoque_tipos_equipamentos', sa.Column('ativo', sa.Boolean(), server_default=sa.text('true'), nullable=False)) + op.add_column('estoque_tipos_equipamentos', sa.Column('data_ativacao', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=False)) + op.add_column('estoque_tipos_equipamentos', sa.Column('data_desativacao', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('estoque_tipos_equipamentos', 'data_desativacao') + op.drop_column('estoque_tipos_equipamentos', 'data_ativacao') + op.drop_column('estoque_tipos_equipamentos', 'ativo') + op.drop_column('estoque_setores', 'data_desativacao') + op.drop_column('estoque_setores', 'data_ativacao') + op.drop_column('estoque_setores', 'ativo') + op.drop_column('estoque_itens_equipamentos', 'data_desativacao') + op.drop_column('estoque_itens_equipamentos', 'data_ativacao') + op.drop_column('estoque_itens_equipamentos', 'ativo') + op.drop_column('estoque_equipamentos', 'data_desativacao') + op.drop_column('estoque_equipamentos', 'data_ativacao') + op.drop_column('estoque_equipamentos', 'ativo') + op.drop_column('comercial_relacoes_comercial', 'data_desativacao') + op.drop_column('comercial_relacoes_comercial', 'data_ativacao') + op.drop_column('comercial_relacoes_comercial', 'ativo') + op.drop_column('comercial_pessoas', 'data_desativacao') + op.drop_column('comercial_pessoas', 'data_ativacao') + op.drop_column('comercial_pessoas', 'ativo') + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/6f05efd0ef79_url_nf.py b/alembic/versions/tenants/6f05efd0ef79_url_nf.py new file mode 100644 index 0000000..58a56fc --- /dev/null +++ b/alembic/versions/tenants/6f05efd0ef79_url_nf.py @@ -0,0 +1,31 @@ +"""URL NF + +Revision ID: 6f05efd0ef79 +Revises: 85d86f327de9 +Create Date: 2024-12-24 17:55:56.964916 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = '6f05efd0ef79' +down_revision: Union[str, None] = '85d86f327de9' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('estoque_itens_equipamentos', sa.Column('itens_equipamentos_url', sa.String(length=2083), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('estoque_itens_equipamentos', 'itens_equipamentos_url') + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/71db5898279e_equipamento_ativo.py b/alembic/versions/tenants/71db5898279e_equipamento_ativo.py new file mode 100644 index 0000000..a428c5d --- /dev/null +++ b/alembic/versions/tenants/71db5898279e_equipamento_ativo.py @@ -0,0 +1,31 @@ +"""Equipamento Ativo + +Revision ID: 71db5898279e +Revises: 6f05efd0ef79 +Create Date: 2024-12-27 06:54:36.928365 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = '71db5898279e' +down_revision: Union[str, None] = '6f05efd0ef79' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('estoque_itens_equipamentos', sa.Column('itens_equipamentos_ativo', sa.Boolean(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('estoque_itens_equipamentos', 'itens_equipamentos_ativo') + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/85d86f327de9_ajuste_campo_nulo.py b/alembic/versions/tenants/85d86f327de9_ajuste_campo_nulo.py new file mode 100644 index 0000000..fd2cb85 --- /dev/null +++ b/alembic/versions/tenants/85d86f327de9_ajuste_campo_nulo.py @@ -0,0 +1,119 @@ +"""Ajuste campo nulo + +Revision ID: 85d86f327de9 +Revises: 17c67edadd6a +Create Date: 2024-12-14 18:30:31.199519 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = '85d86f327de9' +down_revision: Union[str, None] = '17c67edadd6a' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('estoque_equipamentos', 'equipamento_nome', + existing_type=sa.VARCHAR(length=50), + nullable=False) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_patrimonio', + existing_type=sa.VARCHAR(length=10), + nullable=False) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_valor_aquisicao', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=True) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_rfid_uid', + existing_type=sa.VARCHAR(length=39), + nullable=True) + op.alter_column('financeiro_contas', 'contas_valor_total', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=True) + op.alter_column('financeiro_contas', 'contas_valor_juros', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=True) + op.alter_column('financeiro_contas', 'contas_valor_multa', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=True) + op.alter_column('financeiro_contas', 'contas_valor_desconto', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=True) + op.alter_column('financeiro_contas_corrente', 'contas_corrente_saldo_inicial', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=True) + op.alter_column('financeiro_movimentacoes_conta', 'movimentacoes_conta_valor_movimentacao', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=True) + op.alter_column('financeiro_pagamentos', 'valor_pago', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=True) + op.alter_column('financeiro_parcelas', 'parcelas_valor_parcela', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=True) + op.alter_column('financeiro_parcelas', 'parcelas_valor_juros', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=True) + op.alter_column('financeiro_parcelas', 'parcelas_valor_multa', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=True) + op.alter_column('financeiro_parcelas', 'parcelas_valor_desconto', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=True) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('financeiro_parcelas', 'parcelas_valor_desconto', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=False) + op.alter_column('financeiro_parcelas', 'parcelas_valor_multa', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=False) + op.alter_column('financeiro_parcelas', 'parcelas_valor_juros', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=False) + op.alter_column('financeiro_parcelas', 'parcelas_valor_parcela', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=False) + op.alter_column('financeiro_pagamentos', 'valor_pago', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=False) + op.alter_column('financeiro_movimentacoes_conta', 'movimentacoes_conta_valor_movimentacao', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=False) + op.alter_column('financeiro_contas_corrente', 'contas_corrente_saldo_inicial', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=False) + op.alter_column('financeiro_contas', 'contas_valor_desconto', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=False) + op.alter_column('financeiro_contas', 'contas_valor_multa', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=False) + op.alter_column('financeiro_contas', 'contas_valor_juros', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=False) + op.alter_column('financeiro_contas', 'contas_valor_total', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=False) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_rfid_uid', + existing_type=sa.VARCHAR(length=39), + nullable=False) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_valor_aquisicao', + existing_type=sa.NUMERIC(precision=10, scale=2), + nullable=False) + op.alter_column('estoque_itens_equipamentos', 'itens_equipamentos_patrimonio', + existing_type=sa.VARCHAR(length=10), + nullable=True) + op.alter_column('estoque_equipamentos', 'equipamento_nome', + existing_type=sa.VARCHAR(length=50), + nullable=True) + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/997336d58696_exclusão_colunoa_tabela_manutenção.py b/alembic/versions/tenants/997336d58696_exclusão_colunoa_tabela_manutenção.py new file mode 100644 index 0000000..05925f2 --- /dev/null +++ b/alembic/versions/tenants/997336d58696_exclusão_colunoa_tabela_manutenção.py @@ -0,0 +1,33 @@ +"""Exclusão Colunoa Tabela Manutenção + +Revision ID: 997336d58696 +Revises: 54212415a2e0 +Create Date: 2025-01-31 15:45:41.416306 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = '997336d58696' +down_revision: Union[str, None] = '54212415a2e0' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint('fk_ct_estoque_manutencoes_equipamentos_fk_itens_equipam_c4a1', 'estoque_manutencoes_equipamentos', type_='foreignkey') + op.drop_column('estoque_manutencoes_equipamentos', 'fk_itens_equipamentos_uuid') + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('estoque_manutencoes_equipamentos', sa.Column('fk_itens_equipamentos_uuid', sa.UUID(), autoincrement=False, nullable=False)) + op.create_foreign_key('fk_ct_estoque_manutencoes_equipamentos_fk_itens_equipam_c4a1', 'estoque_manutencoes_equipamentos', 'estoque_itens_equipamentos', ['fk_itens_equipamentos_uuid'], ['uuid'], ondelete='CASCADE') + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/9a867c0a6f02_ajuste_relacionamentos_s3.py b/alembic/versions/tenants/9a867c0a6f02_ajuste_relacionamentos_s3.py new file mode 100644 index 0000000..d165875 --- /dev/null +++ b/alembic/versions/tenants/9a867c0a6f02_ajuste_relacionamentos_s3.py @@ -0,0 +1,31 @@ +"""Ajuste Relacionamentos S3 + +Revision ID: 9a867c0a6f02 +Revises: ad38237bf077 +Create Date: 2025-01-05 11:57:01.074787 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = '9a867c0a6f02' +down_revision: Union[str, None] = 'ad38237bf077' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_foreign_key(op.f('fk_ct_s3_arquivo_associacoes_fk_arquivo_uuid_s3_arquivos'), 's3_arquivo_associacoes', 's3_arquivos', ['fk_arquivo_uuid'], ['uuid'], ondelete='CASCADE') + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(op.f('fk_ct_s3_arquivo_associacoes_fk_arquivo_uuid_s3_arquivos'), 's3_arquivo_associacoes', type_='foreignkey') + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/a46b9a45e35d_gerenciamento_arquivos.py b/alembic/versions/tenants/a46b9a45e35d_gerenciamento_arquivos.py new file mode 100644 index 0000000..1b9df60 --- /dev/null +++ b/alembic/versions/tenants/a46b9a45e35d_gerenciamento_arquivos.py @@ -0,0 +1,47 @@ +"""Gerenciamento Arquivos + +Revision ID: a46b9a45e35d +Revises: f1a9c53bb090 +Create Date: 2025-01-03 19:42:06.429869 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = 'a46b9a45e35d' +down_revision: Union[str, None] = 'f1a9c53bb090' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('s3_arquivos', + sa.Column('uuid', sa.UUID(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=True), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_s3_arquivos')) + ) + op.create_table('s3_arquivo_associacoes', + sa.Column('arquivo_associacoes_arquivo_id', sa.UUID(), nullable=False), + sa.Column('arquivo_associacoes_tabela_relacionada', sa.String(), nullable=False), + sa.Column('arquivo_associacoes_linha_id', sa.Uuid(), nullable=False), + sa.Column('uuid', sa.UUID(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=True), + sa.ForeignKeyConstraint(['arquivo_associacoes_arquivo_id'], ['s3_arquivos.uuid'], name=op.f('fk_ct_s3_arquivo_associacoes_arquivo_associacoes_arquivo_id_s3_arquivos'), ondelete='CASCADE'), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_ct_s3_arquivo_associacoes')) + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('s3_arquivo_associacoes') + op.drop_table('s3_arquivos') + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/aabed61bf4b6_teste_campo_ativo.py b/alembic/versions/tenants/aabed61bf4b6_teste_campo_ativo.py new file mode 100644 index 0000000..be165bc --- /dev/null +++ b/alembic/versions/tenants/aabed61bf4b6_teste_campo_ativo.py @@ -0,0 +1,41 @@ +"""Teste Campo Ativo + +Revision ID: aabed61bf4b6 +Revises: 03f34e0286e2 +Create Date: 2025-03-17 08:06:24.204853 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision: str = 'aabed61bf4b6' +down_revision: Union[str, None] = '03f34e0286e2' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('comercial_relacoes_comercial', 'transacao_comercial', + existing_type=postgresql.ENUM('PAGAMENTO', 'RECEBIMENTO', 'AMBOS', name='comercialtransacaocomercialenum'), + nullable=False) + op.add_column('comercial_tipos_endereco', sa.Column('ativo', sa.Boolean(), server_default=sa.text('true'), nullable=False)) + op.add_column('comercial_tipos_endereco', sa.Column('data_ativacao', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=False)) + op.add_column('comercial_tipos_endereco', sa.Column('data_desativacao', sa.DateTime(), server_default=sa.text("TIMEZONE('utc', CURRENT_TIMESTAMP)"), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('comercial_tipos_endereco', 'data_desativacao') + op.drop_column('comercial_tipos_endereco', 'data_ativacao') + op.drop_column('comercial_tipos_endereco', 'ativo') + op.alter_column('comercial_relacoes_comercial', 'transacao_comercial', + existing_type=postgresql.ENUM('PAGAMENTO', 'RECEBIMENTO', 'AMBOS', name='comercialtransacaocomercialenum'), + nullable=True) + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/ad38237bf077_ajuste_tabela_s3_arquivo_associacoes.py b/alembic/versions/tenants/ad38237bf077_ajuste_tabela_s3_arquivo_associacoes.py new file mode 100644 index 0000000..52de999 --- /dev/null +++ b/alembic/versions/tenants/ad38237bf077_ajuste_tabela_s3_arquivo_associacoes.py @@ -0,0 +1,43 @@ +"""Ajuste Tabela s3_arquivo_associacoes + +Revision ID: ad38237bf077 +Revises: bcfe3a7124a6 +Create Date: 2025-01-05 11:41:37.874417 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = 'ad38237bf077' +down_revision: Union[str, None] = 'bcfe3a7124a6' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('s3_arquivo_associacoes', sa.Column('fk_arquivo_uuid', sa.UUID(), nullable=False)) + op.add_column('s3_arquivo_associacoes', sa.Column('arquivo_associacoes_linha_uuid', sa.Uuid(), nullable=False)) + op.alter_column('s3_arquivo_associacoes', 'arquivo_associacoes_tabela_relacionada', + existing_type=sa.VARCHAR(), + nullable=True) + op.drop_column('s3_arquivo_associacoes', 'arquivo_associacoes_linha_id') + op.drop_column('s3_arquivo_associacoes', 'arquivo_associacoes_arquivo_id') + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('s3_arquivo_associacoes', sa.Column('arquivo_associacoes_arquivo_id', sa.UUID(), autoincrement=False, nullable=False)) + op.add_column('s3_arquivo_associacoes', sa.Column('arquivo_associacoes_linha_id', sa.UUID(), autoincrement=False, nullable=False)) + op.alter_column('s3_arquivo_associacoes', 'arquivo_associacoes_tabela_relacionada', + existing_type=sa.VARCHAR(), + nullable=False) + op.drop_column('s3_arquivo_associacoes', 'arquivo_associacoes_linha_uuid') + op.drop_column('s3_arquivo_associacoes', 'fk_arquivo_uuid') + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/bcfe3a7124a6_ajuste_tabela_s3_arquivos.py b/alembic/versions/tenants/bcfe3a7124a6_ajuste_tabela_s3_arquivos.py new file mode 100644 index 0000000..218833a --- /dev/null +++ b/alembic/versions/tenants/bcfe3a7124a6_ajuste_tabela_s3_arquivos.py @@ -0,0 +1,33 @@ +"""Ajuste Tabela s3_arquivos + +Revision ID: bcfe3a7124a6 +Revises: a46b9a45e35d +Create Date: 2025-01-03 19:46:55.239751 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = 'bcfe3a7124a6' +down_revision: Union[str, None] = 'a46b9a45e35d' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('s3_arquivos', sa.Column('arquivos_nome_original', sa.String(length=200), nullable=True)) + op.add_column('s3_arquivos', sa.Column('arquivos_nome_armazenado', sa.String(length=200), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('s3_arquivos', 'arquivos_nome_armazenado') + op.drop_column('s3_arquivos', 'arquivos_nome_original') + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/cef79ed4a795_remoção_colunas_tabela_manutenção.py b/alembic/versions/tenants/cef79ed4a795_remoção_colunas_tabela_manutenção.py new file mode 100644 index 0000000..7bd78bf --- /dev/null +++ b/alembic/versions/tenants/cef79ed4a795_remoção_colunas_tabela_manutenção.py @@ -0,0 +1,35 @@ +"""Remoção Colunas Tabela Manutenção + +Revision ID: cef79ed4a795 +Revises: 405e3f59e8af +Create Date: 2025-02-04 07:06:43.847820 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = 'cef79ed4a795' +down_revision: Union[str, None] = '405e3f59e8af' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('estoque_manutencoes_equipamentos', 'manutencao_equipamento_correcao_realizada') + op.drop_column('estoque_manutencoes_equipamentos', 'manutencao_equipamento_defeito_apresentado') + op.drop_column('estoque_manutencoes_equipamentos', 'manutencao_equipamento_data_retorno') + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('estoque_manutencoes_equipamentos', sa.Column('manutencao_equipamento_data_retorno', sa.DATE(), autoincrement=False, nullable=True)) + op.add_column('estoque_manutencoes_equipamentos', sa.Column('manutencao_equipamento_defeito_apresentado', sa.VARCHAR(length=100), autoincrement=False, nullable=True)) + op.add_column('estoque_manutencoes_equipamentos', sa.Column('manutencao_equipamento_correcao_realizada', sa.TEXT(), autoincrement=False, nullable=False)) + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/f1a9c53bb090_aumento_campo_email.py b/alembic/versions/tenants/f1a9c53bb090_aumento_campo_email.py new file mode 100644 index 0000000..ee45a92 --- /dev/null +++ b/alembic/versions/tenants/f1a9c53bb090_aumento_campo_email.py @@ -0,0 +1,37 @@ +"""Aumento Campo Email + +Revision ID: f1a9c53bb090 +Revises: 71db5898279e +Create Date: 2024-12-27 10:34:45.157752 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = 'f1a9c53bb090' +down_revision: Union[str, None] = '71db5898279e' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('comercial_pessoas', 'pessoa_email', + existing_type=sa.VARCHAR(length=100), + type_=sa.String(length=150), + existing_nullable=True) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('comercial_pessoas', 'pessoa_email', + existing_type=sa.String(length=150), + type_=sa.VARCHAR(length=100), + existing_nullable=True) + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/f7d6f6b09b0b_inclusao_boleano_voltagem.py b/alembic/versions/tenants/f7d6f6b09b0b_inclusao_boleano_voltagem.py new file mode 100644 index 0000000..4de12b0 --- /dev/null +++ b/alembic/versions/tenants/f7d6f6b09b0b_inclusao_boleano_voltagem.py @@ -0,0 +1,31 @@ +"""Inclusao Boleano Voltagem + +Revision ID: f7d6f6b09b0b +Revises: 52048623d0cc +Create Date: 2024-12-14 10:39:08.645329 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = 'f7d6f6b09b0b' +down_revision: Union[str, None] = '52048623d0cc' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('estoque_equipamentos', sa.Column('equipamento_eletrico', sa.Boolean(), nullable=True, default=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('estoque_equipamentos', 'equipamento_eletrico') + # ### end Alembic commands ### diff --git a/alembic/versions/tenants/f893530f5281_boleano_manutenção_retirada_url_s3.py b/alembic/versions/tenants/f893530f5281_boleano_manutenção_retirada_url_s3.py new file mode 100644 index 0000000..7b78686 --- /dev/null +++ b/alembic/versions/tenants/f893530f5281_boleano_manutenção_retirada_url_s3.py @@ -0,0 +1,33 @@ +"""Boleano Manutenção retirada url s3 + +Revision ID: f893530f5281 +Revises: 9a867c0a6f02 +Create Date: 2025-01-27 10:52:49.384065 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import fastapi_users_db_sqlalchemy + + +# revision identifiers, used by Alembic. +revision: str = 'f893530f5281' +down_revision: Union[str, None] = '9a867c0a6f02' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('estoque_itens_equipamentos', sa.Column('itens_equipamentos_manutencao', sa.Boolean(), nullable=True)) + op.drop_column('estoque_itens_equipamentos', 'itens_equipamentos_url') + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('estoque_itens_equipamentos', sa.Column('itens_equipamentos_url', sa.VARCHAR(length=2083), autoincrement=False, nullable=True)) + op.drop_column('estoque_itens_equipamentos', 'itens_equipamentos_manutencao') + # ### end Alembic commands ### diff --git a/app/common/debug.py b/app/common/debug.py new file mode 100644 index 0000000..a2d9df7 --- /dev/null +++ b/app/common/debug.py @@ -0,0 +1,62 @@ +# app/common/debug.py +from __future__ import annotations +import os +import builtins as _bi +from functools import lru_cache +from typing import Callable + +# Chaves que aceitamos para ligar o debug (use APP_DEBUG como padrão) +_DEBUG_ENV_KEYS = ("APP_DEBUG", "DEBUG", "NOVO_INQUILINO_DEBUG") +_TRUE = {"1", "true", "yes", "on", "debug"} + +@lru_cache(maxsize=1) +def debug_enabled() -> bool: + """ + Lê as variáveis de ambiente uma única vez (cache). + Prioridade: APP_DEBUG > DEBUG > NOVO_INQUILINO_DEBUG. + """ + for key in _DEBUG_ENV_KEYS: + val = os.getenv(key) + if val is not None: + return val.lower() in _TRUE + return False + +def make_dbg(prefix: str) -> Callable[..., None]: + """ + Retorna uma função _dbg(msg, *args, **kwargs) que: + - formata msg somente quando debug está ON + - prefixa com [][DEBUG] + - é NO-OP quando debug está OFF + Uso: _dbg = make_dbg("MeuModulo") + """ + if debug_enabled(): + def _dbg(msg: str = "", *args, **kwargs) -> None: + if args or kwargs: + try: + msg = msg.format(*args, **kwargs) + except Exception: + # Não quebra log por erro de formatação + pass + _bi.print(f"[{prefix}][DEBUG] {msg}") + return _dbg + else: + def _dbg(*args, **kwargs) -> None: + return None + return _dbg + +def make_dbg_lazy(prefix: str) -> Callable[[Callable[[], object]], None]: + """ + Versão “preguiçosa”: recebe um callable que só é executado quando o debug está ON. + Uso: _dbg_lazy(lambda: f"payload grande: {json.dumps(obj)}") + """ + if debug_enabled(): + def _dbg_lazy(builder) -> None: + try: + _bi.print(f"[{prefix}][DEBUG] {builder()}") + except Exception as e: + _bi.print(f"[{prefix}][DEBUG] [lazy-error] {e}") + return _dbg_lazy + else: + def _dbg_lazy(*args, **kwargs) -> None: + return None + return _dbg_lazy diff --git a/app/main.py b/app/main.py index 9e6a7ce..3d54b75 100644 --- a/app/main.py +++ b/app/main.py @@ -18,6 +18,7 @@ from app.routers import rotas from app.config import URL_BD from app.routers.router_registry import RouterRegistry + from fastapi.middleware.cors import CORSMiddleware # from starlette.middleware.cors import CORSMiddleware @@ -55,6 +56,7 @@ def init_app(init_db=True): router_registry = RouterRegistry(server, rotas.routers) router_registry.register_routers() + return server diff --git a/app/multi_tenant/criar_tenant.py b/app/multi_tenant/criar_tenant.py index 64d933d..7df8d01 100644 --- a/app/multi_tenant/criar_tenant.py +++ b/app/multi_tenant/criar_tenant.py @@ -8,12 +8,23 @@ from app.database.session import sessionmanager from app.rbac.auth import get_user_db, get_user_manager from app.rbac.schemas import UserCreate from fastapi_users.exceptions import UserAlreadyExists +from sqlalchemy import inspect +import json + +# ------------------------------------------------------------------------------ +# Debug helpers (compartilhados) +# ------------------------------------------------------------------------------ +from app.common.debug import make_dbg, make_dbg_lazy +_DBG_NS = "NovoInquilino" # rótulo curto para logar a origem deste módulo +_dbg = make_dbg(_DBG_NS) +_dbg_lazy = make_dbg_lazy(_DBG_NS) async def check_migrations(engine: AsyncEngine): """ Verifica se todas as migrações foram aplicadas. """ + _dbg("check_migrations(): início") alembic_config = Config("alembic.ini") script = ScriptDirectory.from_config(alembic_config) @@ -22,6 +33,7 @@ async def check_migrations(engine: AsyncEngine): context = MigrationContext.configure(sync_conn) current_revision = context.get_current_revision() latest_revision = script.get_current_head() + _dbg(f"check_migrations(): current={current_revision}, latest={latest_revision}") if current_revision != latest_revision: raise RuntimeError( @@ -31,33 +43,48 @@ async def check_migrations(engine: AsyncEngine): await conn.run_sync(sync_check_migrations) + _dbg("check_migrations(): OK (up-to-date)") -async def create_user(session, fk_inquilino_uuid, email, password, is_superuser=False): + +async def create_user(session, fk_inquilino_uuid, email, password, nome, is_superuser=False): """ Cria um usuário no sistema utilizando o gerenciador de usuários do FastAPI Users. """ + _dbg("create_user(): início") try: - + _dbg("create_user(): preparando user_db/user_manager") user_db = await get_user_db(session).__anext__() user_manager = await get_user_manager(user_db).__anext__() + _dbg("create_user(): user_manager OK") try: + _dbg( + "create_user(): chamando user_manager.create(UserCreate(...)) " + f"payload={{'nome_completo': {nome!r}, 'email': {email!r}, " + f"'is_superuser': {is_superuser!r}, 'fk_inquilino_uuid': {fk_inquilino_uuid}}}" + ) + user = await user_manager.create( UserCreate( + nome_completo=nome, email=email, password=password, is_superuser=is_superuser, is_active=True, fk_inquilino_uuid=fk_inquilino_uuid, - ) ) + _dbg(f"create_user(): OK user_id={user.id}") return user.id + except UserAlreadyExists: + _dbg(f"create_user(): UserAlreadyExists para email={email!r}, consultando id existente...") result_user = await session.execute(select(RbacUser).filter_by(email=email)) existing_user = result_user.scalars().first() raise RuntimeError(f"Usuário '{email}' já existe, seu ID é {existing_user.id}") + except Exception as e: + _dbg(f"create_user(): ERRO -> {e!r}") raise RuntimeError(f"Erro ao criar usuário '{email}': {e}") @@ -66,13 +93,23 @@ async def tenant_create(nome: str, email: str, password: str, cpf_cnpj: str): Cria um novo tenant (inquilino) no sistema, configura o schema específico e registra um usuário inicial relacionado ao inquilino. """ + _dbg(f"tenant_create(): início payload={{'nome': {nome!r}, 'email': {email!r}, 'cpf_cnpj': {cpf_cnpj!r}}}") async with sessionmanager.session() as db: try: # Verificar se o tenant já existe + _dbg("tenant_create(): verificando existência do inquilino por cpf_cnpj...") result = await db.execute(select(Inquilino).filter_by(cpf_cnpj=cpf_cnpj)) existing_tenant = result.scalars().first() + if existing_tenant is None: + _dbg("tenant_create(): inquilino inexistente (OK para criar)") + else: + data = {c.key: getattr(existing_tenant, c.key) for c in inspect(existing_tenant).mapper.columns} + _dbg("tenant_create(): inquilino já existe -> " + json.dumps(data, ensure_ascii=False, default=str, indent=2)) + + _dbg("tenant_create(): criando registro do inquilino...") if existing_tenant: + _dbg("tenant_create(): ABORT -> tenant já existe") raise RuntimeError( f"Tenant com CPF/CNPJ '{cpf_cnpj}' já existe. Nome: {existing_tenant.nome}, " f"UUID: {existing_tenant.uuid}" @@ -83,22 +120,28 @@ async def tenant_create(nome: str, email: str, password: str, cpf_cnpj: str): db.add(tenant) await db.commit() await db.refresh(tenant) + _dbg(f"tenant_create(): inquilino criado uuid={tenant.uuid}") # Criar o usuário inicial + _dbg("tenant_create(): criando usuário inicial (superuser)...") user_id = await create_user( session=db, + nome=nome, fk_inquilino_uuid=tenant.uuid, email=email, password=password, is_superuser=True, ) + _dbg(f"tenant_create(): usuário criado id={user_id}") # Nova sessão para associar o papel ao usuário + _dbg("tenant_create(): vinculando papel 'Super Administrador' ao usuário...") async with sessionmanager.session() as new_db: # Buscar o papel "Super Administrador" result_papel = await new_db.execute(select(RbacPapel).filter_by(nome="Super Administrador")) papel = result_papel.scalars().first() if not papel: + _dbg("tenant_create(): ERRO -> Papel 'Super Administrador' não encontrado") raise RuntimeError("Papel 'Super Administrador' não encontrado.") # Relacionar o papel ao usuário @@ -109,9 +152,14 @@ async def tenant_create(nome: str, email: str, password: str, cpf_cnpj: str): ) ) await new_db.commit() + _dbg("tenant_create(): papel vinculado OK") + _dbg("tenant_create(): fim OK") return tenant.uuid + except RuntimeError as e: + _dbg(f"tenant_create(): ERRO (RuntimeError) -> {e!r}") raise RuntimeError(f"Erro inesperado durante a criação do cliente: '{nome}': {e}") except Exception as e: + _dbg(f"tenant_create(): ERRO (Exception) -> {e!r}") raise RuntimeError(f"Erro inesperado durante a criação do cliente: '{nome}': {e}") diff --git a/app/multi_tenant/onboarding/novo_inquilino_service.py b/app/multi_tenant/onboarding/novo_inquilino_service.py new file mode 100644 index 0000000..c931dd6 --- /dev/null +++ b/app/multi_tenant/onboarding/novo_inquilino_service.py @@ -0,0 +1,468 @@ +# from __future__ import annotations +# +# import os +# import asyncio +# import subprocess +# from dataclasses import dataclass +# from typing import Optional +# +# from app.multi_tenant.criar_tenant import tenant_create # sua função já existente +# +# +# class NovoInquilinoError(Exception): +# """Erro de alto nível no fluxo de criação de inquilino.""" +# +# +# @dataclass +# class NovoInquilinoResult: +# tenant: str +# status: str +# alembic_stdout: Optional[str] = None +# +# +# async def _run_alembic_for_tenant(tenant_id: str, alembic_ini_path: Optional[str] = None) -> str: +# """ +# Executa `alembic -x tenant= upgrade head` para aplicar migrações +# (histórico centralizado na API do sistema). +# +# Implementação compatível com Windows/Uvicorn: +# - usa subprocess.run síncrono dentro de asyncio.to_thread +# """ +# cmd = ["alembic"] +# if alembic_ini_path: +# cmd += ["-c", alembic_ini_path] +# cmd += ["-x", f"tenant={tenant_id}", "upgrade", "head"] +# +# def _run_sync(): +# # Se precisar garantir diretório, use cwd="..." aqui. +# return subprocess.run(cmd, capture_output=True, text=True, check=True) +# +# try: +# result = await asyncio.to_thread(_run_sync) +# except subprocess.CalledProcessError as e: +# raise NovoInquilinoError( +# f"Erro Alembic (rc={e.returncode})\nSTDOUT:\n{e.stdout}\nSTDERR:\n{e.stderr}" +# ) from e +# except FileNotFoundError as e: +# # 'alembic' não encontrado no PATH/venv +# raise NovoInquilinoError( +# "Comando 'alembic' não encontrado. Verifique se o virtualenv está ativo e se o Alembic está instalado." +# ) from e +# except Exception as e: +# raise NovoInquilinoError(f"Falha inesperada ao executar Alembic: {e}") from e +# +# return result.stdout +# +# +# async def criar_novo_inquilino_service( +# *, +# nome: str, +# email: str, +# password: str, +# cpf_cnpj: str, +# alembic_ini_path_env: str = "ALEMBIC_INI_PATH", +# ) -> NovoInquilinoResult: +# """ +# Caso de uso: cria um novo inquilino e roda migrações do schema dele. +# Mantém paridade com o seu script original. +# """ +# print("Criar Novo Inquilino") +# try: +# tenant_identifier = await tenant_create( +# nome=nome, email=email, password=password, cpf_cnpj=cpf_cnpj +# ) +# except Exception as e: +# # Falha ao criar registro/base do inquilino +# raise NovoInquilinoError(f"Erro ao criar o tenant: {e}") from e +# +# # Executa migrações a partir do histórico central (API do sistema) +# alembic_ini_path = os.getenv(alembic_ini_path_env) # opcional: apontar para outro repo +# stdout = await _run_alembic_for_tenant(str(tenant_identifier), alembic_ini_path) +# +# return NovoInquilinoResult( +# tenant=str(tenant_identifier), +# status="active", +# alembic_stdout=stdout, +# ) + +############################################################################################################ + +# app/multi_tenant/onboarding/novo_inquilino_service.py +from __future__ import annotations + +import os +import asyncio +import subprocess +from dataclasses import dataclass +from typing import Optional, Dict, Any, List +from contextlib import asynccontextmanager + +from sqlalchemy import text +from sqlalchemy.ext.asyncio import AsyncSession + + +from app.multi_tenant.criar_tenant import tenant_create # sua função já existente +from app.database.session import sessionmanager # precisa já estar inicializado no app + + +# ------------------------------------------------------------------------------ +# Debug helpers (compartilhados) +# ------------------------------------------------------------------------------ +from app.common.debug import make_dbg, make_dbg_lazy +_DBG_NS = "NovoInquilino" # rótulo curto para logar a origem deste módulo +_dbg = make_dbg(_DBG_NS) +_dbg_lazy = make_dbg_lazy(_DBG_NS) + + + +# ------------------------------------------------------------------------------ +# Exceção e resultado de caso de uso +# ------------------------------------------------------------------------------ +class NovoInquilinoError(Exception): + """Erro de alto nível no fluxo de criação de inquilino.""" + + +@dataclass +class NovoInquilinoResult: + tenant: str + status: str + alembic_stdout: Optional[str] = None + + +# ------------------------------------------------------------------------------ +# Helpers de sessão +# ------------------------------------------------------------------------------ +@asynccontextmanager +async def _new_session() -> AsyncSession: + """ + Abre uma AsyncSession a partir do sessionmanager (compatível com sua infra). + """ + _dbg("_new_session(): abertura solicitada") + # Muitos session managers expõem um sessionmaker; aqui usamos esse caminho. + sm = sessionmanager.get_sessionmaker() + _dbg("_new_session(): usando get_sessionmaker()") + session: AsyncSession = sm() + _dbg("_new_session(): AsyncSession obtida") + try: + yield session + finally: + await session.close() + _dbg("_new_session(): sessão fechada") + + +# ------------------------------------------------------------------------------ +# Helpers de schema / infra +# ------------------------------------------------------------------------------ +def _derive_schema_candidates(tenant_uuid: str, prefix: str = "t_") -> List[str]: + """ + Retorna *todas* as possibilidades de nome de schema que queremos tentar dropar, + na ordem de preferência. + + 1) t_ (ex.: t_01996bc991217d51ab00aacb559f8480) + 2) (ex.: 01996bcb-9184-7f68-9cf1-b5fe7af7c348) ← usado pelo seu Alembic + """ + uuid_no_dash = tenant_uuid.replace("-", "") + candidates = [ + f"{prefix}{uuid_no_dash}", + tenant_uuid, + ] + _dbg("_derive_schema_candidates(): tenant_uuid={0} -> {1}", tenant_uuid, candidates) + return candidates + + +async def _drop_schema(schema_name: str) -> bool: + """ + DROP SCHEMA IF EXISTS "" CASCADE + Retorna True se executou o comando sem erro (mesmo se o schema não existisse). + """ + _dbg('_drop_schema(): início. schema_name={0}', schema_name) + async with _new_session() as s: + await s.execute(text(f'DROP SCHEMA IF EXISTS "{schema_name}" CASCADE')) + await s.commit() + _dbg('_drop_schema(): OK. schema_name={0}', schema_name) + return True + + +async def _drop_tenant_schemas_all(tenant_uuid: str, schema_prefix: str) -> Dict[str, Any]: + """ + Tenta dropar *todas* as variações de schema possíveis do tenant. + Retorna um relatório com sucesso/erro por candidato. + """ + report: Dict[str, Any] = {"tenant_uuid": tenant_uuid, "schemas": []} + for schema in _derive_schema_candidates(tenant_uuid, schema_prefix): + try: + await _drop_schema(schema) + report["schemas"].append({"name": schema, "dropped": True}) + except Exception as e: + _dbg("_drop_tenant_schemas_all(): erro ao dropar {0}: {1}", schema, e) + report["schemas"].append({"name": schema, "dropped": False, "error": str(e)}) + _dbg("_drop_tenant_schemas_all(): report={0}", report) + return report + + +# ------------------------------------------------------------------------------ +# Helpers de limpeza no shared (usuários, vínculos, inquilino) +# ------------------------------------------------------------------------------ +async def _delete_users_and_links_all(tenant_uuid: str) -> Dict[str, int]: + """ + Remove vínculos de papéis e usuários para um inquilino no schema 'shared'. + Usa DELETE ... RETURNING 1 para contar afetados de forma portátil. + """ + _dbg("_delete_users_and_links_all(): início. tenant_uuid={0}", tenant_uuid) + stats = {"papel_vinculos": 0, "usuarios": 0} + + async with _new_session() as s: + _dbg("_delete_users_and_links_all(): deletando vínculos em shared.rbac_papeis_usuario ...") + res_links = await s.execute( + text( + """ + DELETE FROM shared.rbac_papeis_usuario + WHERE user_uuid IN ( + SELECT id FROM shared.rbac_usuarios + WHERE fk_inquilino_uuid = :tenant_uuid + ) + RETURNING 1 + """ + ), + {"tenant_uuid": tenant_uuid}, + ) + # Contagem via RETURNING + links_rows = res_links.fetchall() + stats["papel_vinculos"] = len(links_rows) + _dbg("_delete_users_and_links_all(): vínculos deletados={0}", stats["papel_vinculos"]) + + _dbg("_delete_users_and_links_all(): deletando usuários em shared.rbac_usuarios ...") + res_users = await s.execute( + text( + """ + DELETE FROM shared.rbac_usuarios + WHERE fk_inquilino_uuid = :tenant_uuid + RETURNING 1 + """ + ), + {"tenant_uuid": tenant_uuid}, + ) + users_rows = res_users.fetchall() + stats["usuarios"] = len(users_rows) + _dbg("_delete_users_and_links_all(): usuários deletados={0}", stats["usuarios"]) + + await s.commit() + _dbg("_delete_users_and_links_all(): OK. stats={0}", stats) + + return stats + + +async def _delete_tenant_record(tenant_uuid: str) -> int: + """ + Remove o próprio registro do inquilino no shared.inquilinos. + Retorna qtd de linhas afetadas. + """ + _dbg("_delete_tenant_record(): início. tenant_uuid={0}", tenant_uuid) + async with _new_session() as s: + res = await s.execute( + text( + """ + DELETE FROM shared.inquilinos + WHERE uuid = :tenant_uuid + RETURNING 1 + """ + ), + {"tenant_uuid": tenant_uuid}, + ) + rows = res.fetchall() + await s.commit() + count = len(rows) + _dbg("_delete_tenant_record(): OK. linhas_deletadas={0}", count) + return count + + +async def _find_tenant_uuid_by_doc(cpf_cnpj: str) -> Optional[str]: + """ + Busca o uuid do inquilino via cpf_cnpj (schema 'shared'). + """ + _dbg("_find_tenant_uuid_by_doc(): início. cpf_cnpj={0}", cpf_cnpj) + async with _new_session() as s: + res = await s.execute( + text("SELECT uuid FROM shared.inquilinos WHERE cpf_cnpj = :doc"), + {"doc": cpf_cnpj}, + ) + row = res.fetchone() + tenant_uuid = row[0] if row else None + _dbg("_find_tenant_uuid_by_doc(): resultado={0}", tenant_uuid) + return tenant_uuid + + +# ------------------------------------------------------------------------------ +# Limpezas compostas +# ------------------------------------------------------------------------------ +async def _cleanup_full_by_tenant_uuid(tenant_uuid: str, schema_prefix: str) -> Dict[str, Any]: + """ + Limpa toda a sujeira que pode ter ficado para um tenant específico: + - DROP de todos os schemas candidatos + - DELETE vínculos + usuários + - DELETE registro do inquilino + """ + _dbg("_cleanup_full_by_tenant_uuid(): início. tenant_uuid={0}", tenant_uuid) + report: Dict[str, Any] = {"tenant_uuid": tenant_uuid} + + # 1) Drop dos schemas candidatos + drop_report = await _drop_tenant_schemas_all(tenant_uuid, schema_prefix) + report["schema_names"] = [s["name"] for s in drop_report["schemas"]] + report["dropped_schema"] = any(s["dropped"] for s in drop_report["schemas"]) + _dbg("_cleanup_full_by_tenant_uuid(): drop schema OK ({0})", report["schema_names"]) + + # 2) Usuários + vínculos + users_stats = await _delete_users_and_links_all(tenant_uuid) + report["users_cleanup"] = users_stats + _dbg("_cleanup_full_by_tenant_uuid(): users/links cleanup OK: {0}", users_stats) + + # 3) Registro do inquilino + deleted = await _delete_tenant_record(tenant_uuid) + report["tenants_deleted"] = deleted + _dbg("_cleanup_full_by_tenant_uuid(): tenant record delete OK. linhas={0}", deleted) + + _dbg("_cleanup_full_by_tenant_uuid(): fim. report={0}", report) + return report + + +async def _cleanup_full_by_doc(cpf_cnpj: str, schema_prefix: str) -> Dict[str, Any]: + """ + Limpeza quando falhou *antes* de sabermos seguramente o tenant_uuid (p. ex., erro criando usuário). + - Descobre tenant_uuid por cpf_cnpj; + - Se achou, faz a limpeza completa por uuid. + """ + _dbg("_cleanup_full_by_doc(): início. cpf_cnpj={0}", cpf_cnpj) + tenant_uuid = await _find_tenant_uuid_by_doc(cpf_cnpj) + if not tenant_uuid: + report = {"cpf_cnpj": cpf_cnpj, "tenant_uuid": None, "note": "nenhum inquilino encontrado"} + _dbg("_cleanup_full_by_doc(): nenhum inquilino para {0}. report={1}", cpf_cnpj, report) + return report + + inner = await _cleanup_full_by_tenant_uuid(tenant_uuid, schema_prefix) + report = { + "cpf_cnpj": cpf_cnpj, + "tenant_uuid": tenant_uuid, + "schema_names": inner.get("schema_names"), + "dropped_schema": inner.get("dropped_schema"), + "users_cleanup": inner.get("users_cleanup"), + "tenants_deleted": inner.get("tenants_deleted"), + } + _dbg("_cleanup_full_by_doc(): fim. report={0}", report) + return report + + +# ------------------------------------------------------------------------------ +# Alembic runner +# ------------------------------------------------------------------------------ +async def _run_alembic_for_tenant(tenant_id: str, alembic_ini_path: Optional[str] = None) -> str: + """ + Executa `alembic -x tenant= upgrade head` para aplicar migrações + (histórico centralizado na API do sistema). + + Observações: + - Captura STDOUT/STDERR para diagnóstico. + - Em Windows/Uvicorn usamos subprocess.run síncrono dentro de asyncio.to_thread. + """ + cmd = ["alembic"] + if alembic_ini_path: + cmd += ["-c", alembic_ini_path] + cmd += ["-x", f"tenant={tenant_id}", "upgrade", "head"] + + _dbg("_run_alembic_for_tenant(): cmd={0}", " ".join(cmd)) + + def _run_sync(): + return subprocess.run(cmd, capture_output=True, text=True, check=True) + + try: + result = await asyncio.to_thread(_run_sync) + except subprocess.CalledProcessError as e: + _dbg("_run_alembic_for_tenant(): CalledProcessError rc={0}", e.returncode) + _dbg("_run_alembic_for_tenant(): STDOUT\n{0}\n", e.stdout or "") + _dbg("_run_alembic_for_tenant(): STDERR\n{0}\n", e.stderr or "") + raise NovoInquilinoError( + f"Erro Alembic (rc={e.returncode})\nSTDOUT:\n{e.stdout}\n\nSTDERR:\n{e.stderr}" + ) from e + except FileNotFoundError as e: + _dbg("_run_alembic_for_tenant(): 'alembic' não encontrado") + raise NovoInquilinoError( + "Comando 'alembic' não encontrado. Verifique se o virtualenv está ativo e se o Alembic está instalado." + ) from e + except Exception as e: + _dbg("_run_alembic_for_tenant(): falha inesperada: {0}", e) + raise NovoInquilinoError(f"Falha inesperada ao executar Alembic: {e}") from e + + _dbg("_run_alembic_for_tenant(): OK. returncode={0}", result.returncode) + if result.stdout: + _dbg("_run_alembic_for_tenant(): STDOUT (início)\n{0}\n_run_alembic_for_tenant(): STDOUT (fim)", result.stdout) + if result.stderr: + # Alembic/SQLAlchemy costuma logar no STDERR mesmo em sucesso + _dbg("_run_alembic_for_tenant(): STDERR (início)\n{0}\n_run_alembic_for_tenant(): STDERR (fim)", result.stderr) + + # Normalmente não precisamos do stdout; manter só para compat. + return result.stdout or "" + + +# ------------------------------------------------------------------------------ +# Caso de uso principal +# ------------------------------------------------------------------------------ +async def criar_novo_inquilino_service( + *, + nome: str, + email: str, + password: str, + cpf_cnpj: str, + alembic_ini_path_env: str = "ALEMBIC_INI_PATH", + schema_prefix: str = "t_", +) -> NovoInquilinoResult: + """ + Fluxo: + 1) Cria registro do inquilino + usuário inicial (tenant_create) + 2) Executa migrações do schema do tenant (alembic -x tenant= upgrade head) + Em qualquer falha, tenta limpar tudo para evitar dados órfãos. + """ + _dbg("criar_novo_inquilino_service(): início") + _dbg("payload: nome={0!r}, email={1!r}, cpf_cnpj={2!r}", nome, email, cpf_cnpj) + _dbg("criar_novo_inquilino_service(): chamando tenant_create(...)") + + tenant_uuid: Optional[str] = None + + # 1) Criar base do inquilino (inclui usuário inicial via seu fluxo) + try: + tenant_uuid = str( + await tenant_create(nome=nome, email=email, password=password, cpf_cnpj=cpf_cnpj) + ) + _dbg("criar_novo_inquilino_service(): tenant_create OK. tenant_uuid={0}", tenant_uuid) + except Exception as e: + _dbg("criar_novo_inquilino_service(): ERRO em tenant_create: {0}", e) + # Limpeza pelo documento, pois talvez não tenhamos tenant_uuid sólido + _dbg("criar_novo_inquilino_service(): iniciando limpeza por cpf_cnpj (falha no tenant_create)") + try: + cleanup = await _cleanup_full_by_doc(cpf_cnpj, schema_prefix) + _dbg("criar_novo_inquilino_service(): limpeza concluída. cleanup={0}", cleanup) + except Exception as cleanup_err: + _dbg("criar_novo_inquilino_service(): limpeza por cpf_cnpj FALHOU: {0}", cleanup_err) + # Re-lança um erro de alto nível + raise NovoInquilinoError(str(e)) from e + + # 2) Rodar migrações do tenant + alembic_ini_path = os.getenv(alembic_ini_path_env) # opcional: apontar para outro repo/folder + _dbg("criar_novo_inquilino_service(): alembic_ini_path={0}", alembic_ini_path) + _dbg("criar_novo_inquilino_service(): rodando Alembic para o tenant") + + try: + stdout = await _run_alembic_for_tenant(tenant_uuid, alembic_ini_path) + _dbg("criar_novo_inquilino_service(): Alembic OK") + result = NovoInquilinoResult(tenant=tenant_uuid, status="active", alembic_stdout=stdout or "") + _dbg("criar_novo_inquilino_service(): fim com sucesso. result={0}", result) + return result + except Exception as mig_err: + _dbg("criar_novo_inquilino_service(): ERRO na migração: {0}", mig_err) + _dbg("criar_novo_inquilino_service(): iniciando limpeza por tenant_uuid (falha na migração)") + try: + cleanup = await _cleanup_full_by_tenant_uuid(tenant_uuid, schema_prefix) + _dbg("criar_novo_inquilino_service(): limpeza concluída. cleanup={0}", cleanup) + except Exception as cleanup_err: + _dbg("criar_novo_inquilino_service(): limpeza por tenant_uuid FALHOU: {0}", cleanup_err) + + # Após limpar, propaga erro + raise diff --git a/app/rbac/schemas.py b/app/rbac/schemas.py index cec03cf..c34295d 100644 --- a/app/rbac/schemas.py +++ b/app/rbac/schemas.py @@ -33,7 +33,7 @@ class UserRead(schemas.BaseUser[uuid.UUID]): class UserCreate(schemas.BaseUserCreate): fk_inquilino_uuid: UUID - nome: str = Field(min_length=3, max_length=100) + nome_completo: str = Field(min_length=3, max_length=100) class UserUpdate(schemas.BaseUserUpdate): diff --git a/app/routers/rotas.py b/app/routers/rotas.py index 190f942..ea87798 100644 --- a/app/routers/rotas.py +++ b/app/routers/rotas.py @@ -1,6 +1,7 @@ from app.database import models from app.routers.rotas_dinamicas import create_dynamic_router from app.routers.router_pessoa import router as pessoa +from app.routers.router_tenant_admin import router as admin_tenants_router from app.s3.router_s3 import router as s3 from app import schemas from app.rbac.routes_login import router as fastapi_user @@ -76,12 +77,5 @@ fastapi_logado_router = fastapi_logado s3_router = s3 # Lista de roteadores para serem registrados routers = [ - fastapi_user_router, - fastapi_logado_router, - tipo_endereco, - endereco, - pessoa_router, - papel, - s3_router, - + admin_tenants_router ] diff --git a/app/routers/router_tenant_admin.py b/app/routers/router_tenant_admin.py new file mode 100644 index 0000000..69ea17c --- /dev/null +++ b/app/routers/router_tenant_admin.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +from fastapi import APIRouter, HTTPException, status +from app.schemas.tenant_admin import NovoInquilinoRequest, NovoInquilinoResponse +from app.multi_tenant.onboarding.novo_inquilino_service import ( + criar_novo_inquilino_service, + NovoInquilinoResult, + NovoInquilinoError, +) + +router = APIRouter(prefix="/admin/tenants", tags=["Admin - Tenants"]) + +@router.post("", status_code=status.HTTP_201_CREATED, response_model=NovoInquilinoResponse) +async def criar_novo_inquilino(payload: NovoInquilinoRequest) -> NovoInquilinoResponse: + try: + print("try") + result: NovoInquilinoResult = await criar_novo_inquilino_service( + nome=payload.nome, + email=payload.email, + password=payload.password, + cpf_cnpj=payload.cpf_cnpj, # já vem sanitizado/validado + ) + + return NovoInquilinoResponse( + tenant=result.tenant, + status=result.status, + alembic_stdout=result.alembic_stdout, + # campos opcionais; preencha se o seu serviço retornar + schema_name=None, + + ) + except NovoInquilinoError as e: + print("except NovoInquilinoError as e") + raise HTTPException(status_code=500, detail=str(e)) + # except Exception as e: + # print("except NovoInquilinoError as e") + # raise HTTPException(status_code=500, detail=f"Erro inesperado: {e}") diff --git a/app/schemas/tenant_admin.py b/app/schemas/tenant_admin.py new file mode 100644 index 0000000..2ff3334 --- /dev/null +++ b/app/schemas/tenant_admin.py @@ -0,0 +1,59 @@ + +from __future__ import annotations + +from typing import Literal, Optional +from pydantic import BaseModel, EmailStr, Field, ConfigDict, field_validator + +class NovoInquilinoRequest(BaseModel): + model_config = ConfigDict(extra="forbid") # rejeita campos desconhecidos + + nome: str = Field(..., min_length=1, description="Nome do cliente/inquilino") + email: EmailStr + password: str = Field(..., min_length=6) + cpf_cnpj: str = Field(..., min_length=11, max_length=18, description="CPF/CNPJ com ou sem máscara") + + + # @field_validator("nome") + # @classmethod + # def _strip_nome(cls, v: str) -> str: + # v = v.strip() + # if not v: + # raise ValueError("Nome não pode ser vazio") + # return " ".join(v.split()) # colapsa espaços internos + # + # @field_validator("cpf_cnpj", mode="before") + # @classmethod + # def _sanitize_cpf_cnpj(cls, v: str) -> str: + # # remove tudo que não for dígito + # if isinstance(v, str): + # digits = "".join(ch for ch in v if ch.isdigit()) + # return digits + # return v + # + # @field_validator("cpf_cnpj") + # @classmethod + # def _validate_cpf_cnpj_len(cls, v: str) -> str: + # # valida tamanho (11 = CPF, 14 = CNPJ). Validação algorítmica pode ficar na classe de validações especiais depois + # if len(v) not in (11, 14): + # raise ValueError("CPF/CNPJ deve ter 11 (CPF) ou 14 (CNPJ) dígitos após sanitização") + # return v + # + # @field_validator("subdominio") + # @classmethod + # def _normalize_subdominio(cls, v: Optional[str]) -> Optional[str]: + # if v is None: + # return v + # v = v.strip().lower() + # if not v: + # return None + # return v + + +class NovoInquilinoResponse(BaseModel): + model_config = ConfigDict(extra="ignore") + + tenant: str + status: Literal["active", "failed", "provisioning"] + schema_name: Optional[str] = None + subdominio: Optional[str] = None + alembic_stdout: Optional[str] = None