diff --git a/backend/alembic/env.py b/backend/alembic/env.py index 097cb079..348bcb40 100644 --- a/backend/alembic/env.py +++ b/backend/alembic/env.py @@ -80,33 +80,36 @@ target_metadata = Base.metadata def import_models_from_dir(dir_path: str): """Importa recursivamente cualquier archivo models.py desde dir_path y archivos en directorios models/""" + import sys + + def _load_module(module_path: str): + rel_path = os.path.relpath(module_path, BASE_DIR) + module_name = rel_path.replace(os.sep, ".").replace(".py", "") + # Skip if already loaded to avoid duplicate SQLAlchemy table registrations + if module_name in sys.modules: + return + spec = importlib.util.spec_from_file_location(module_name, module_path) + mod = importlib.util.module_from_spec(spec) + # Register in sys.modules before exec so transitive imports resolve correctly + sys.modules[module_name] = mod + spec.loader.exec_module(mod) + for root, dirs, files in os.walk(dir_path): # Importar archivos models.py directos if "models.py" in files: - module_path = os.path.join(root, "models.py") - # Convertir path en nombre de módulo compatible - rel_path = os.path.relpath(module_path, BASE_DIR) - module_name = rel_path.replace(os.sep, ".").replace(".py", "") - # Lazy import - spec = importlib.util.spec_from_file_location(module_name, module_path) - mod = importlib.util.module_from_spec(spec) - spec.loader.exec_module(mod) + try: + _load_module(os.path.join(root, "models.py")) + except Exception as e: + logger.warning(f"No se pudo importar {os.path.join(root, 'models.py')}: {e}") # Importar todos los archivos .py en directorios llamados "models" if os.path.basename(root) == "models": for file in files: if file.endswith(".py") and not file.startswith("__"): - module_path = os.path.join(root, file) - rel_path = os.path.relpath(module_path, BASE_DIR) - module_name = rel_path.replace(os.sep, ".").replace(".py", "") try: - spec = importlib.util.spec_from_file_location( - module_name, module_path - ) - mod = importlib.util.module_from_spec(spec) - spec.loader.exec_module(mod) + _load_module(os.path.join(root, file)) except Exception as e: - logger.warning(f"No se pudo importar {module_path}: {e}") + logger.warning(f"No se pudo importar {os.path.join(root, file)}: {e}") # Importar todos los models dentro de api/v1/modules y api/v1/modules/uploads diff --git a/backend/alembic/versions/7937209f9718_seed_initial_data.py b/backend/alembic/versions/7937209f9718_seed_initial_data.py index 4ab685d1..5bffa511 100644 --- a/backend/alembic/versions/7937209f9718_seed_initial_data.py +++ b/backend/alembic/versions/7937209f9718_seed_initial_data.py @@ -94,175 +94,7 @@ depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: - """Upgrade schema.""" - - # --- CREAR TABLA fraccion canadienses --- - op.create_table('canadian_tariff_fractions', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('fraction', sa.String(length=13), nullable=False), - sa.Column('ad_valorem', sa.Numeric(precision=5, scale=2), nullable=True), - sa.Column('unit_of_measure', sa.String(length=5), nullable=True), - sa.Column('country_code', sa.String(length=3), nullable=False), - sa.Column('description', sa.String(length=1000), nullable=True), - - # Tenant Scoped Mixin - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('company_id', sa.Integer(), nullable=False), - - # Timestamp Mixin - sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.Column('deleted_at', sa.DateTime(), nullable=True), - - sa.PrimaryKeyConstraint('id'), - sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], source_schema='a76', referent_schema='core'), - sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], source_schema='a76', referent_schema='a76'), - sa.UniqueConstraint('fraction', 'country_code', 'company_id', name='uq_canadian_fraction_country_company'), - schema='a76' - ) - op.create_index(op.f('ix_a76_canadian_tariff_fractions_country_code'), 'canadian_tariff_fractions', ['country_code'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_canadian_tariff_fractions_fraction'), 'canadian_tariff_fractions', ['fraction'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_canadian_tariff_fractions_id'), 'canadian_tariff_fractions', ['id'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_canadian_tariff_fractions_company_id'), 'canadian_tariff_fractions', ['company_id'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_canadian_tariff_fractions_tenant_id'), 'canadian_tariff_fractions', ['tenant_id'], unique=False, schema='a76') - - # --- CREAR TABLA fraccion americana --- - op.create_table('us_tariff_fractions', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('code', sa.String(length=16), nullable=False, comment='Código de fracción americana'), - sa.Column('prefix', sa.String(length=10), nullable=True, comment='Prefijo de clasificación'), - sa.Column('type_code', sa.String(length=10), nullable=True, comment='Código de tipo'), - sa.Column('ad_valorem', sa.Numeric(precision=10, scale=2), nullable=True, comment='Porcentaje ad valorem'), - sa.Column('fixed_cost', sa.Numeric(precision=15, scale=8), nullable=True, comment='Tasa fija'), - sa.Column('unit_of_measure', sa.String(length=10), nullable=True, comment='Unidad de medida'), - sa.Column('description', sa.String(), nullable=True, comment='Descripción de la fracción'), - - # Tenant Scoped Mixin - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('company_id', sa.Integer(), nullable=False), - - # Timestamp Mixin - sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.Column('deleted_at', sa.DateTime(), nullable=True), - - sa.PrimaryKeyConstraint('id'), - sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], source_schema='a76', referent_schema='core'), - sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], source_schema='a76', referent_schema='a76'), - schema='a76' - ) - op.create_index(op.f('ix_a76_us_tariff_fractions_id'), 'us_tariff_fractions', ['id'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_us_tariff_fractions_company_id'), 'us_tariff_fractions', ['company_id'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_us_tariff_fractions_tenant_id'), 'us_tariff_fractions', ['tenant_id'], unique=False, schema='a76') - - # --- CREAR TABLA fraccion historico --- - op.create_table('historical_tariff_fractions', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('historical_fraction', sa.String(length=8), nullable=True), - sa.Column('unit_of_measure_code', sa.String(), nullable=True), - sa.Column('country', sa.String(), nullable=True), - sa.Column('fraction_type', sa.String(length=7), nullable=True), - sa.Column('sector', sa.String(length=5), nullable=True), - sa.Column('import_tax_rate', sa.Numeric(precision=7, scale=2), nullable=True), - sa.Column('export_tax_rate', sa.Numeric(precision=7, scale=2), nullable=True), - sa.Column('publication_date', sa.DateTime(), nullable=True), - sa.Column('is_immex', sa.Boolean(), nullable=True), - sa.Column('normal_temporality', sa.Boolean(), nullable=True), - sa.Column('services_temporality', sa.Boolean(), nullable=True), - sa.Column('certified_temporality', sa.Boolean(), nullable=True), - sa.Column('by_log', sa.Boolean(), nullable=True), - sa.Column('end_date', sa.DateTime(), nullable=True), - - # Tenant Scoped Mixin - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('company_id', sa.Integer(), nullable=False), - - # Timestamp Mixin - sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.Column('deleted_at', sa.DateTime(), nullable=True), - - sa.PrimaryKeyConstraint('id'), - sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], source_schema='a76', referent_schema='core'), - sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], source_schema='a76', referent_schema='a76'), - # Foreign Keys to other tables (assuming they exist or are created before/simultaneously, referencing explicit schemas) - sa.ForeignKeyConstraint(['unit_of_measure_code'], ['a76.unit_of_measure_customs.code'], source_schema='a76', referent_schema='a76'), - sa.ForeignKeyConstraint(['country'], ['public.countries.m3_key'], source_schema='a76', referent_schema='public'), - - schema='a76' - ) - op.create_index(op.f('ix_a76_historical_tariff_fractions_id'), 'historical_tariff_fractions', ['id'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_historical_tariff_fractions_company_id'), 'historical_tariff_fractions', ['company_id'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_historical_tariff_fractions_tenant_id'), 'historical_tariff_fractions', ['tenant_id'], unique=False, schema='a76') - - # --- CREAR TABLA fraccion americana --- - op.create_table('us_tariff_fractions', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('code', sa.String(length=16), nullable=False, comment='Código de fracción americana'), - sa.Column('prefix', sa.String(length=10), nullable=True, comment='Prefijo de clasificación'), - sa.Column('type_code', sa.String(length=10), nullable=True, comment='Código de tipo'), - sa.Column('ad_valorem', sa.Numeric(precision=10, scale=2), nullable=True, comment='Porcentaje ad valorem'), - sa.Column('fixed_cost', sa.Numeric(precision=15, scale=8), nullable=True, comment='Tasa fija'), - sa.Column('unit_of_measure', sa.String(length=10), nullable=True, comment='Unidad de medida'), - sa.Column('description', sa.String(), nullable=True, comment='Descripción de la fracción'), - - # Tenant Scoped Mixin - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('company_id', sa.Integer(), nullable=False), - - # Timestamp Mixin - sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.Column('deleted_at', sa.DateTime(), nullable=True), - - sa.PrimaryKeyConstraint('id'), - sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], source_schema='a76', referent_schema='core'), - sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], source_schema='a76', referent_schema='a76'), - schema='a76' - ) - op.create_index(op.f('ix_a76_us_tariff_fractions_id'), 'us_tariff_fractions', ['id'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_us_tariff_fractions_company_id'), 'us_tariff_fractions', ['company_id'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_us_tariff_fractions_tenant_id'), 'us_tariff_fractions', ['tenant_id'], unique=False, schema='a76') - - # --- CREAR TABLA fraccion historico --- - op.create_table('historical_tariff_fractions', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('historical_fraction', sa.String(length=8), nullable=True), - sa.Column('unit_of_measure_code', sa.String(), nullable=True), - sa.Column('country', sa.String(), nullable=True), - sa.Column('fraction_type', sa.String(length=7), nullable=True), - sa.Column('sector', sa.String(length=5), nullable=True), - sa.Column('import_tax_rate', sa.Numeric(precision=7, scale=2), nullable=True), - sa.Column('export_tax_rate', sa.Numeric(precision=7, scale=2), nullable=True), - sa.Column('publication_date', sa.DateTime(), nullable=True), - sa.Column('is_immex', sa.Boolean(), nullable=True), - sa.Column('normal_temporality', sa.Boolean(), nullable=True), - sa.Column('services_temporality', sa.Boolean(), nullable=True), - sa.Column('certified_temporality', sa.Boolean(), nullable=True), - sa.Column('by_log', sa.Boolean(), nullable=True), - sa.Column('end_date', sa.DateTime(), nullable=True), - - # Tenant Scoped Mixin - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('company_id', sa.Integer(), nullable=False), - - # Timestamp Mixin - sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.Column('deleted_at', sa.DateTime(), nullable=True), - - sa.PrimaryKeyConstraint('id'), - sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], source_schema='a76', referent_schema='core'), - sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], source_schema='a76', referent_schema='a76'), - # Foreign Keys to other tables (assuming they exist or are created before/simultaneously, referencing explicit schemas) - sa.ForeignKeyConstraint(['unit_of_measure_code'], ['a76.unit_of_measure_customs.code'], source_schema='a76', referent_schema='a76'), - sa.ForeignKeyConstraint(['country'], ['public.countries.m3_key'], source_schema='a76', referent_schema='public'), - - schema='a76' - ) - op.create_index(op.f('ix_a76_historical_tariff_fractions_id'), 'historical_tariff_fractions', ['id'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_historical_tariff_fractions_company_id'), 'historical_tariff_fractions', ['company_id'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_historical_tariff_fractions_tenant_id'), 'historical_tariff_fractions', ['tenant_id'], unique=False, schema='a76') + """Upgrade schema.""" # --- UTILIDAD DE FORMATEO --- def format_value(val): @@ -690,7 +522,7 @@ def upgrade() -> None: f"{format_value(export_tax)}, {format_timestamp(pub_date)}, {format_bool(is_immex)}, " f"{format_bool(normal_temp)}, {format_bool(services_temp)}, {format_bool(certified_temp)}, " f"{format_bool(by_log)}, {format_timestamp(end_date)}, " - f"1, 1, 'NOW()', 'NOW()')" # tenant_id=1, company_id=1, created_at=NOW(), updated_at=NOW() + f"1, 1)" # tenant_id=1, company_id=1 for historical_fraction, nico, unit_measure, country, fraction_type, sector, import_tax, export_tax, pub_date, is_immex, normal_temp, services_temp, certified_temp, by_log, end_date in historical_tariff_fractions_seed ] ) @@ -702,7 +534,7 @@ def upgrade() -> None: (historical_fraction, nico, unit_of_measure_code, country, fraction_type, sector, import_tax_rate, export_tax_rate, publication_date, is_immex, normal_temporality, services_temporality, certified_temporality, by_log, end_date, - tenant_id, company_id, created_at, updated_at) + tenant_id, company_id) VALUES {values_historical_fractions} ON CONFLICT DO NOTHING; """ diff --git a/backend/api/v1/modules/a76/audit_log/models.py b/backend/api/v1/modules/a76/audit_log/models.py index 499ea453..ace69a32 100644 --- a/backend/api/v1/modules/a76/audit_log/models.py +++ b/backend/api/v1/modules/a76/audit_log/models.py @@ -30,7 +30,7 @@ class AuditLog(Base, TenantScopedMixin, TimestampMixin): # Technical Columns timestamp = Column(DateTime(timezone=True), nullable=False, index=True) # Combined for queries - system = Column(String(20), nullable=False, index=True, default="SCAF") + system = Column(String(20), nullable=False, index=True, default="fixed_asset") # Traceability table_name = Column(String(100), nullable=True, index=True)