diff --git a/.gitignore b/.gitignore index 14c07767..0af971f5 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,6 @@ downloads/ eggs/ .eggs/ lib64/ -parts/ sdist/ var/ wheels/ diff --git a/backend/alembic/versions/03b786378f94_pedimentos.py b/backend/alembic/versions/03b786378f94_pedimentos.py index d760d4b8..be9283e3 100644 --- a/backend/alembic/versions/03b786378f94_pedimentos.py +++ b/backend/alembic/versions/03b786378f94_pedimentos.py @@ -13,7 +13,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. revision: str = '03b786378f94' -down_revision: Union[str, Sequence[str], None] = '7937209f9718' +down_revision: Union[str, Sequence[str], None] = '54f2046774d0' branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None diff --git a/backend/alembic/versions/54f2046774d0_create_new_a76_tables_only.py b/backend/alembic/versions/54f2046774d0_create_new_a76_tables_only.py index 01ae5756..272f1821 100644 --- a/backend/alembic/versions/54f2046774d0_create_new_a76_tables_only.py +++ b/backend/alembic/versions/54f2046774d0_create_new_a76_tables_only.py @@ -22,7 +22,7 @@ def upgrade() -> None: """Upgrade schema - Create only new A76 tables.""" # Create new A76 tables only (skip existing tenants, licenses, license_usage) - op.create_table('gclient_provider', + op.create_table('client_provider', sa.Column('client_id', sa.String(length=8), nullable=False), sa.Column('type_nat_foreign', sa.String(length=1), nullable=True), sa.Column('name', sa.String(length=256), nullable=True), @@ -81,7 +81,7 @@ def upgrade() -> None: schema='a76' ) - op.create_table('gclasses', + op.create_table('classes', sa.Column('client_key', sa.Integer(), nullable=False), sa.Column('class_code', sa.String(length=8), nullable=False), sa.Column('description_spanish', sa.String(length=500), nullable=True), @@ -98,7 +98,7 @@ def upgrade() -> None: schema='a76' ) - op.create_table('gparts', + op.create_table('parts', sa.Column('client_key', sa.Integer(), nullable=False), sa.Column('part_number', sa.String(length=49), nullable=False), sa.Column('fraction', sa.String(length=10), nullable=True), @@ -151,7 +151,7 @@ def upgrade() -> None: sa.Column('email', sa.String(length=100), nullable=True), sa.Column('contact', sa.String(length=50), nullable=True), sa.Column('reference', sa.String(length=250), nullable=True), - sa.ForeignKeyConstraint(['client_id'], ['a76.gclient_provider.client_id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['client_id'], ['a76.client_provider.client_id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('client_id'), schema='a76' ) @@ -178,7 +178,7 @@ def upgrade() -> None: sa.Column('subassembly_service', sa.SmallInteger(), nullable=True), sa.Column('autse_dates', sa.Integer(), nullable=True), sa.Column('autse_number', sa.String(length=300), nullable=True), - sa.ForeignKeyConstraint(['client_id'], ['a76.gclient_provider.client_id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['client_id'], ['a76.client_provider.client_id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('client_id'), schema='a76' ) @@ -189,7 +189,7 @@ def downgrade() -> None: # Drop tables in reverse dependency order op.drop_table('gclient_provider_programs', schema='a76') op.drop_table('gclient_provider_address', schema='a76') - op.drop_table('gparts', schema='a76') - op.drop_table('gclasses', schema='a76') + op.drop_table('parts', schema='a76') + op.drop_table('classes', schema='a76') op.drop_table('gcompany', schema='a76') - op.drop_table('gclient_provider', schema='a76') \ No newline at end of file + op.drop_table('client_provider', schema='a76') \ No newline at end of file diff --git a/backend/alembic/versions/eb8a17e5fbde_create_a76_tables_company_clients_parts_.py b/backend/alembic/versions/eb8a17e5fbde_create_a76_tables_company_clients_parts_.py deleted file mode 100644 index 07992a28..00000000 --- a/backend/alembic/versions/eb8a17e5fbde_create_a76_tables_company_clients_parts_.py +++ /dev/null @@ -1,355 +0,0 @@ -"""create_a76_tables_company_clients_parts_classes - -Revision ID: eb8a17e5fbde -Revises: 7937209f9718 -Create Date: 2025-11-06 03:15:17.248159 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = 'eb8a17e5fbde' -down_revision: Union[str, Sequence[str], None] = '7937209f9718' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema - Create new A76 tables only.""" - - # Crear tabla gcompany - op.create_table('gcompany', - sa.Column('id', sa.String(length=3), nullable=False), - sa.Column('consecutive', sa.Boolean(), nullable=False), - sa.Column('name', sa.String(length=255), nullable=True), - sa.Column('rfc', sa.String(length=30), nullable=True), - sa.Column('main_activity', sa.String(length=255), nullable=True), - sa.Column('program', sa.String(length=10), nullable=True), - sa.Column('program_number', sa.String(length=40), nullable=True), - sa.Column('prosec', sa.SmallInteger(), nullable=True), - sa.Column('prosec_authorization', sa.String(length=20), nullable=True), - sa.Column('manufacturer_id', sa.String(length=25), nullable=True), - sa.Column('broker_company', sa.String(length=10), nullable=True), - sa.Column('responsible', sa.String(length=80), nullable=True), - sa.Column('responsible_name', sa.String(length=20), nullable=True), - sa.Column('responsible_last_name', sa.String(length=20), nullable=True), - sa.Column('responsible_mother_last_name', sa.String(length=20), nullable=True), - sa.Column('responsible_rfc', sa.String(length=30), nullable=True), - sa.Column('position', sa.String(length=30), nullable=True), - sa.Column('logo', sa.String(length=255), nullable=True), - sa.Column('has_express_line', sa.Boolean(), nullable=True), - sa.Column('order_format_type', sa.String(length=19), nullable=True), - sa.Column('previous_code', sa.SmallInteger(), nullable=True), - sa.Column('is_service_company', sa.Boolean(), nullable=True), - sa.Column('client_name', sa.String(length=300), nullable=True), - sa.Column('subassembly_mode', sa.String(length=7), nullable=True), - sa.Column('curp', sa.String(length=19), nullable=True), - sa.Column('inter_db_name', sa.String(length=100), nullable=True), - sa.Column('ctpat_svi', sa.String(length=100), nullable=True), - sa.Column('trusted_exporter_number', sa.String(length=50), nullable=True), - sa.Column('prevalidator_key', sa.String(length=20), nullable=True), - sa.Column('seventh_amendment', sa.Boolean(), nullable=True), - sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('consecutive'), - schema='a76' - ) - - # Crear tabla gclient_provider - op.create_table('gclient_provider', - sa.Column('client_id', sa.String(length=8), nullable=False), - sa.Column('type_nat_foreign', sa.String(length=1), nullable=True), - sa.Column('name', sa.String(length=256), nullable=True), - sa.Column('short_name', sa.String(length=10), nullable=True), - sa.Column('rfc', sa.String(length=30), nullable=True), - sa.Column('curp', sa.String(length=19), nullable=True), - sa.Column('client_or_provider', sa.String(length=1), nullable=True), - sa.Column('linking', sa.String(length=1), nullable=True), - sa.Column('transform_subassembly', sa.String(length=1), nullable=True), - sa.Column('extra_information', sa.String(length=399), nullable=True), - sa.Column('web_key', sa.String(length=40), nullable=True), - sa.Column('responsible', sa.String(length=80), nullable=True), - sa.Column('position', sa.String(length=30), nullable=True), - sa.Column('incoterm', sa.String(length=19), nullable=True), - sa.Column('is_national_provider', sa.String(length=2), nullable=True), - sa.Column('enabled_disabled', sa.SmallInteger(), nullable=True), - sa.PrimaryKeyConstraint('client_id'), - schema='a76' - ) - - # Crear tabla gclasses - op.create_table('gclasses', - sa.Column('client_key', sa.Integer(), nullable=False), - sa.Column('class_code', sa.String(length=8), nullable=False), - sa.Column('description_spanish', sa.String(length=500), nullable=True), - sa.Column('description_english', sa.String(length=500), nullable=True), - sa.Column('material_key', sa.String(length=10), nullable=True), - sa.Column('unit_of_measure', sa.String(length=5), nullable=True), - sa.Column('fraction', sa.String(length=10), nullable=True), - sa.Column('us_fraction', sa.String(length=16), nullable=True), - sa.Column('sub_key', sa.String(length=5), nullable=True), - sa.Column('physical_review', sa.SmallInteger(), nullable=True), - sa.Column('iva_exempt_fraction', sa.String(length=4), nullable=True), - sa.ForeignKeyConstraint(['material_key'], ['public.material_types.key'], ), - sa.PrimaryKeyConstraint('client_key', 'class_code'), - schema='a76' - ) - - # Crear tabla gparts - op.create_table('gparts', - sa.Column('client_key', sa.Integer(), nullable=False), - sa.Column('part_number', sa.String(length=49), nullable=False), - sa.Column('fraction', sa.String(length=10), nullable=True), - sa.Column('description_spanish', sa.String(length=500), nullable=True), - sa.Column('description_english', sa.String(length=500), nullable=True), - sa.Column('part_class', sa.String(length=8), nullable=True), - sa.Column('unit_of_measure', sa.String(length=5), nullable=True), - sa.Column('commercial_part_number', sa.String(length=70), nullable=True), - sa.Column('country_of_origin', sa.String(length=3), nullable=True), - sa.Column('unit_cost', sa.Numeric(precision=23, scale=8), nullable=True), - sa.Column('currency_type', sa.String(length=2), nullable=True), - sa.Column('currency_key', sa.String(length=3), nullable=True), - sa.Column('unit_weight', sa.Numeric(precision=19, scale=8), nullable=True), - sa.Column('weight_type', sa.String(length=6), nullable=True), - sa.Column('us_fraction', sa.String(length=16), nullable=True), - sa.Column('fda_key', sa.String(length=20), nullable=True), - sa.Column('fcc_key', sa.String(length=30), nullable=True), - sa.Column('license_code', sa.String(length=3), nullable=True), - sa.Column('eccn', sa.String(length=20), nullable=True), - sa.Column('export_code', sa.String(length=2), nullable=True), - sa.Column('exclusion_symbol', sa.String(length=19), nullable=True), - sa.Column('supplier', sa.String(length=14), nullable=True), - sa.Column('alternate_unit_measure', sa.String(length=14), nullable=True), - sa.Column('added_value', sa.Numeric(precision=23, scale=8), nullable=True), - sa.Column('enabled_disabled', sa.SmallInteger(), nullable=True), - sa.Column('creation_date', sa.Integer(), nullable=True), - sa.Column('modification_date', sa.Integer(), nullable=True), - sa.Column('modification_date_iso', sa.DateTime(timezone=True), nullable=True), - sa.Column('part_photo', sa.String(length=255), nullable=True), - sa.ForeignKeyConstraint(['country_of_origin'], ['public.countries.m3_key'], ), - sa.ForeignKeyConstraint(['currency_key'], ['public.currency_types.code'], ), - sa.PrimaryKeyConstraint('client_key', 'part_number'), - schema='a76' - ) - - # Crear tabla gclient_provider_address - op.create_table('gclient_provider_address', - sa.Column('client_id', sa.String(length=8), nullable=False), - sa.Column('municipality', sa.String(length=150), nullable=True), - sa.Column('streets', sa.String(length=100), nullable=True), - sa.Column('neighborhood', sa.String(length=40), nullable=True), - sa.Column('interior_number', sa.String(length=20), nullable=True), - sa.Column('exterior_number', sa.String(length=20), nullable=True), - sa.Column('postal_code', sa.String(length=15), nullable=True), - sa.Column('city', sa.String(length=30), nullable=True), - sa.Column('state', sa.String(length=30), nullable=True), - sa.Column('country', sa.String(length=3), nullable=True), - sa.Column('phone', sa.String(length=30), nullable=True), - sa.Column('fax_number', sa.String(length=30), nullable=True), - sa.Column('email', sa.String(length=100), nullable=True), - sa.Column('contact', sa.String(length=50), nullable=True), - sa.Column('reference', sa.String(length=250), nullable=True), - sa.ForeignKeyConstraint(['client_id'], ['a76.gclient_provider.client_id'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('client_id'), - schema='a76' - ) - - # Crear tabla gclient_provider_programs - op.create_table('gclient_provider_programs', - sa.Column('client_id', sa.String(length=8), nullable=False), - sa.Column('program', sa.String(length=7), nullable=True), - sa.Column('program_number', sa.String(length=40), nullable=True), - sa.Column('prosec', sa.SmallInteger(), nullable=True), - sa.Column('prosec_authorization', sa.String(length=20), nullable=True), - sa.Column('secon_auth_date', sa.Integer(), nullable=True), - sa.Column('manufacturer_id', sa.String(length=25), nullable=True), - sa.Column('tax_id', sa.String(length=30), nullable=True), - sa.Column('broker', sa.String(length=6), nullable=True), - sa.Column('import_broker', sa.String(length=6), nullable=True), - sa.Column('transfer_key', sa.String(length=8), nullable=True), - sa.Column('secon_authorization', sa.String(length=20), nullable=True), - sa.Column('applied_proportion', sa.Numeric(precision=7, scale=2), nullable=True), - sa.Column('is_certified_company', sa.String(length=1), nullable=True), - sa.Column('certified_company_registry', sa.String(length=40), nullable=True), - sa.Column('donation_auth_number', sa.String(length=50), nullable=True), - sa.Column('ctpat_svi', sa.String(length=100), nullable=True), - sa.Column('tax_registry_number', sa.String(length=40), nullable=True), - sa.Column('subassembly_service', sa.SmallInteger(), nullable=True), - sa.Column('autse_dates', sa.Integer(), nullable=True), - sa.Column('autse_number', sa.String(length=300), nullable=True), - sa.ForeignKeyConstraint(['client_id'], ['a76.gclient_provider.client_id'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('client_id'), - schema='a76' - ) - op.create_table('gclasses', - sa.Column('client_key', sa.Integer(), nullable=False), - sa.Column('class_code', sa.String(length=8), nullable=False), - sa.Column('description_spanish', sa.String(length=500), nullable=True), - sa.Column('description_english', sa.String(length=500), nullable=True), - sa.Column('material_key', sa.String(length=10), nullable=True), - sa.Column('unit_of_measure', sa.String(length=5), nullable=True), - sa.Column('fraction', sa.String(length=10), nullable=True), - sa.Column('us_fraction', sa.String(length=16), nullable=True), - sa.Column('sub_key', sa.String(length=5), nullable=True), - sa.Column('physical_review', sa.SmallInteger(), nullable=True), - sa.Column('iva_exempt_fraction', sa.String(length=4), nullable=True), - sa.ForeignKeyConstraint(['material_key'], ['public.material_types.key'], ), - sa.PrimaryKeyConstraint('client_key', 'class_code'), - schema='a76' - ) - op.create_table('gclient_provider_address', - sa.Column('client_id', sa.String(length=8), nullable=False), - sa.Column('municipality', sa.String(length=150), nullable=True), - sa.Column('streets', sa.String(length=100), nullable=True), - sa.Column('neighborhood', sa.String(length=40), nullable=True), - sa.Column('interior_number', sa.String(length=20), nullable=True), - sa.Column('exterior_number', sa.String(length=20), nullable=True), - sa.Column('postal_code', sa.String(length=15), nullable=True), - sa.Column('city', sa.String(length=30), nullable=True), - sa.Column('state', sa.String(length=30), nullable=True), - sa.Column('country', sa.String(length=3), nullable=True), - sa.Column('phone', sa.String(length=30), nullable=True), - sa.Column('fax_number', sa.String(length=30), nullable=True), - sa.Column('email', sa.String(length=100), nullable=True), - sa.Column('contact', sa.String(length=50), nullable=True), - sa.Column('reference', sa.String(length=250), nullable=True), - sa.ForeignKeyConstraint(['client_id'], ['a76.gclient_provider.client_id'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('client_id'), - schema='a76' - ) - op.create_table('gclient_provider_programs', - sa.Column('client_id', sa.String(length=8), nullable=False), - sa.Column('program', sa.String(length=7), nullable=True), - sa.Column('program_number', sa.String(length=40), nullable=True), - sa.Column('prosec', sa.SmallInteger(), nullable=True), - sa.Column('prosec_authorization', sa.String(length=20), nullable=True), - sa.Column('secon_auth_date', sa.Integer(), nullable=True), - sa.Column('manufacturer_id', sa.String(length=25), nullable=True), - sa.Column('tax_id', sa.String(length=30), nullable=True), - sa.Column('broker', sa.String(length=6), nullable=True), - sa.Column('import_broker', sa.String(length=6), nullable=True), - sa.Column('transfer_key', sa.String(length=8), nullable=True), - sa.Column('secon_authorization', sa.String(length=20), nullable=True), - sa.Column('applied_proportion', sa.Numeric(precision=7, scale=2), nullable=True), - sa.Column('is_certified_company', sa.String(length=1), nullable=True), - sa.Column('certified_company_registry', sa.String(length=40), nullable=True), - sa.Column('donation_auth_number', sa.String(length=50), nullable=True), - sa.Column('ctpat_svi', sa.String(length=100), nullable=True), - sa.Column('tax_registry_number', sa.String(length=40), nullable=True), - sa.Column('subassembly_service', sa.SmallInteger(), nullable=True), - sa.Column('autse_dates', sa.Integer(), nullable=True), - sa.Column('autse_number', sa.String(length=300), nullable=True), - sa.ForeignKeyConstraint(['client_id'], ['a76.gclient_provider.client_id'], ondelete='CASCADE'), - sa.PrimaryKeyConstraint('client_id'), - schema='a76' - ) - op.create_table('gparts', - sa.Column('client_key', sa.Integer(), nullable=False), - sa.Column('part_number', sa.String(length=49), nullable=False), - sa.Column('fraction', sa.String(length=10), nullable=True), - sa.Column('description_spanish', sa.String(length=500), nullable=True), - sa.Column('description_english', sa.String(length=500), nullable=True), - sa.Column('part_class', sa.String(length=8), nullable=True), - sa.Column('unit_of_measure', sa.String(length=5), nullable=True), - sa.Column('commercial_part_number', sa.String(length=70), nullable=True), - sa.Column('country_of_origin', sa.String(length=3), nullable=True), - sa.Column('unit_cost', sa.Numeric(precision=23, scale=8), nullable=True), - sa.Column('currency_type', sa.String(length=2), nullable=True), - sa.Column('currency_key', sa.String(length=3), nullable=True), - sa.Column('unit_weight', sa.Numeric(precision=19, scale=8), nullable=True), - sa.Column('weight_type', sa.String(length=6), nullable=True), - sa.Column('us_fraction', sa.String(length=16), nullable=True), - sa.Column('fda_key', sa.String(length=20), nullable=True), - sa.Column('fcc_key', sa.String(length=30), nullable=True), - sa.Column('license_code', sa.String(length=3), nullable=True), - sa.Column('eccn', sa.String(length=20), nullable=True), - sa.Column('export_code', sa.String(length=2), nullable=True), - sa.Column('exclusion_symbol', sa.String(length=19), nullable=True), - sa.Column('supplier', sa.String(length=14), nullable=True), - sa.Column('alternate_unit_measure', sa.String(length=14), nullable=True), - sa.Column('added_value', sa.Numeric(precision=23, scale=8), nullable=True), - sa.Column('enabled_disabled', sa.SmallInteger(), nullable=True), - sa.Column('creation_date', sa.Integer(), nullable=True), - sa.Column('modification_date', sa.Integer(), nullable=True), - sa.Column('modification_date_iso', sa.DateTime(timezone=True), nullable=True), - sa.Column('part_photo', sa.String(length=255), nullable=True), - sa.ForeignKeyConstraint(['country_of_origin'], ['public.countries.m3_key'], ), - sa.ForeignKeyConstraint(['currency_key'], ['public.currency_types.code'], ), - sa.PrimaryKeyConstraint('client_key', 'part_number'), - schema='a76' - ) - op.create_table('license_usage', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('period_start', sa.DateTime(timezone=True), nullable=False), - sa.Column('period_end', sa.DateTime(timezone=True), nullable=False), - sa.Column('active_users', sa.Integer(), nullable=True), - sa.Column('storage_used_gb', sa.Integer(), nullable=True), - sa.Column('operations_count', sa.Integer(), nullable=True), - sa.Column('api_calls_count', sa.Integer(), nullable=True), - sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id'), - schema='a76' - ) - op.create_index(op.f('ix_a76_license_usage_id'), 'license_usage', ['id'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_license_usage_tenant_id'), 'license_usage', ['tenant_id'], unique=False, schema='a76') - op.create_table('licenses', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('tenant_id', sa.Integer(), nullable=False), - sa.Column('plan', sa.Enum('FREE', 'BASIC', 'PROFESSIONAL', 'ENTERPRISE', name='licenseplan'), nullable=False), - sa.Column('status', sa.Enum('ACTIVE', 'EXPIRED', 'SUSPENDED', 'PENDING', 'CANCELLED', name='licensestatus'), nullable=False), - sa.Column('max_users', sa.Integer(), nullable=False), - sa.Column('max_storage_gb', sa.Integer(), nullable=False), - sa.Column('max_monthly_operations', sa.Integer(), nullable=False), - sa.Column('feature_api_access', sa.Boolean(), nullable=True), - sa.Column('feature_advanced_reports', sa.Boolean(), nullable=True), - sa.Column('feature_integrations', sa.Boolean(), nullable=True), - sa.Column('feature_dedicated_support', sa.Boolean(), nullable=True), - sa.Column('starts_at', sa.DateTime(timezone=True), nullable=False), - sa.Column('expires_at', sa.DateTime(timezone=True), nullable=False), - sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), - sa.ForeignKeyConstraint(['tenant_id'], ['a76.tenants.id'], ), - sa.PrimaryKeyConstraint('id'), - schema='a76' - ) - op.create_index(op.f('ix_a76_licenses_id'), 'licenses', ['id'], unique=False, schema='a76') - op.create_index(op.f('ix_a76_licenses_tenant_id'), 'licenses', ['tenant_id'], unique=True, schema='a76') - op.drop_constraint(op.f('fk_regimenped'), 'code_pedimento_regimens', type_='foreignkey') - op.drop_constraint(op.f('fk_codeped'), 'code_pedimento_regimens', type_='foreignkey') - op.create_foreign_key('fk_regimenped', 'code_pedimento_regimens', 'pedimento_regimens', ['regimen_code'], ['code'], source_schema='public', referent_schema='public') - op.create_foreign_key('fk_codeped', 'code_pedimento_regimens', 'pedimento_codes', ['pedimento_code'], ['code'], source_schema='public', referent_schema='public') - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.drop_constraint('fk_codeped', 'code_pedimento_regimens', schema='public', type_='foreignkey') - op.drop_constraint('fk_regimenped', 'code_pedimento_regimens', schema='public', type_='foreignkey') - op.create_foreign_key(op.f('fk_codeped'), 'code_pedimento_regimens', 'pedimento_codes', ['pedimento_code'], ['code']) - op.create_foreign_key(op.f('fk_regimenped'), 'code_pedimento_regimens', 'pedimento_regimens', ['regimen_code'], ['code']) - op.drop_index(op.f('ix_a76_licenses_tenant_id'), table_name='licenses', schema='a76') - op.drop_index(op.f('ix_a76_licenses_id'), table_name='licenses', schema='a76') - op.drop_table('licenses', schema='a76') - op.drop_index(op.f('ix_a76_license_usage_tenant_id'), table_name='license_usage', schema='a76') - op.drop_index(op.f('ix_a76_license_usage_id'), table_name='license_usage', schema='a76') - op.drop_table('license_usage', schema='a76') - op.drop_table('gparts', schema='a76') - op.drop_table('gclient_provider_programs', schema='a76') - op.drop_table('gclient_provider_address', schema='a76') - op.drop_table('gclasses', schema='a76') - op.drop_index(op.f('ix_a76_tenants_slug'), table_name='tenants', schema='a76') - op.drop_index(op.f('ix_a76_tenants_name'), table_name='tenants', schema='a76') - op.drop_index(op.f('ix_a76_tenants_id'), table_name='tenants', schema='a76') - op.drop_table('tenants', schema='a76') - op.drop_table('gcompany', schema='a76') - op.drop_table('gclient_provider', schema='a76') - # ### end Alembic commands ### diff --git a/backend/api/v1/modules/a76/GClass/__init__.py b/backend/api/v1/modules/a76/classes/__init__.py similarity index 76% rename from backend/api/v1/modules/a76/GClass/__init__.py rename to backend/api/v1/modules/a76/classes/__init__.py index 09dc90d1..8d0a42ce 100644 --- a/backend/api/v1/modules/a76/GClass/__init__.py +++ b/backend/api/v1/modules/a76/classes/__init__.py @@ -1,5 +1,5 @@ """ -Módulo de GClass +Módulo de Class """ from .routes import router diff --git a/backend/api/v1/modules/a76/GClass/dto.py b/backend/api/v1/modules/a76/classes/dto.py similarity index 100% rename from backend/api/v1/modules/a76/GClass/dto.py rename to backend/api/v1/modules/a76/classes/dto.py diff --git a/backend/api/v1/modules/a76/GClass/models.py b/backend/api/v1/modules/a76/classes/models.py similarity index 82% rename from backend/api/v1/modules/a76/GClass/models.py rename to backend/api/v1/modules/a76/classes/models.py index 77a6765a..9cd42b53 100644 --- a/backend/api/v1/modules/a76/GClass/models.py +++ b/backend/api/v1/modules/a76/classes/models.py @@ -11,15 +11,15 @@ import enum from typing import TYPE_CHECKING, List, Optional if TYPE_CHECKING: - from api.v1.modules.a76.GParts.models import GPart + from api.v1.modules.a76.parts.models import Part from api.v1.modules.public.reference_data.material_types.models import MaterialType -class GClass(Base): +class Class(Base): """ Modelo para la tabla GClases - Información de clases en sistemas SCAII y SCAF """ - __tablename__ = "gclasses" + __tablename__ = "classes" __table_args__ = {"schema": "a76"} # Primary key compuesta @@ -48,14 +48,14 @@ class GClass(Base): # Inverse relationship with GParts that have this class parts = relationship( - "GPart", - primaryjoin="and_(GClass.client_key == GPart.client_key, GClass.class_code == GPart.part_class)", - foreign_keys="[GPart.client_key, GPart.part_class]", + "Part", + primaryjoin="and_(Class.client_key == Part.client_key, Class.class_code == Part.part_class)", + foreign_keys="[Part.client_key, Part.part_class]", viewonly=True, back_populates="part_class_info" ) def __repr__(self): - return f"" + return f"" diff --git a/backend/api/v1/modules/a76/GClass/routes.py b/backend/api/v1/modules/a76/classes/routes.py similarity index 100% rename from backend/api/v1/modules/a76/GClass/routes.py rename to backend/api/v1/modules/a76/classes/routes.py diff --git a/backend/api/v1/modules/a76/GClass/service.py b/backend/api/v1/modules/a76/classes/service.py similarity index 79% rename from backend/api/v1/modules/a76/GClass/service.py rename to backend/api/v1/modules/a76/classes/service.py index ceed5c56..6a11895b 100644 --- a/backend/api/v1/modules/a76/GClass/service.py +++ b/backend/api/v1/modules/a76/classes/service.py @@ -8,7 +8,7 @@ from fastapi import HTTPException from typing import List, Optional import logging -from .models import GClass +from .models import Class from .dto import ( ClassCreateDTO, ClassUpdateDTO, @@ -42,10 +42,10 @@ class ClassService: """ try: # Verificar que no exista la clase - existing = self.db.query(GClass).filter( + existing = self.db.query(Class).filter( and_( - GClass.client_key == class_data.client_key, - GClass.class_code == class_data.class_code + Class.client_key == class_data.client_key, + Class.class_code == class_data.class_code ) ).first() @@ -56,7 +56,7 @@ class ClassService: ) # Crear clase - db_class = GClass( + db_class = Class( client_key=class_data.client_key, class_code=class_data.class_code, description_spanish=class_data.description_spanish, @@ -100,10 +100,10 @@ class ClassService: Returns: ClassResponseDTO o None si no existe """ - class_obj = self.db.query(GClass).filter( + class_obj = self.db.query(Class).filter( and_( - GClass.client_key == client_key, - GClass.class_code == class_code + Class.client_key == client_key, + Class.class_code == class_code ) ).first() @@ -128,33 +128,33 @@ class ClassService: Returns: ClassListDTO con la lista paginada """ - query = self.db.query(GClass) + query = self.db.query(Class) # Aplicar filtros si se proporcionan if search_params: if search_params.client_key: - query = query.filter(GClass.client_key == search_params.client_key) + query = query.filter(Class.client_key == search_params.client_key) if search_params.class_code: - query = query.filter(GClass.class_code.ilike(f"%{search_params.class_code}%")) + query = query.filter(Class.class_code.ilike(f"%{search_params.class_code}%")) if search_params.description: description_pattern = f"%{search_params.description}%" query = query.filter( or_( - GClass.description_spanish.ilike(description_pattern), - GClass.description_english.ilike(description_pattern) + Class.description_spanish.ilike(description_pattern), + Class.description_english.ilike(description_pattern) ) ) if search_params.material_key: - query = query.filter(GClass.material_key.ilike(f"%{search_params.material_key}%")) + query = query.filter(Class.material_key.ilike(f"%{search_params.material_key}%")) if search_params.fraction: - query = query.filter(GClass.fraction.ilike(f"%{search_params.fraction}%")) + query = query.filter(Class.fraction.ilike(f"%{search_params.fraction}%")) if search_params.physical_review is not None: - query = query.filter(GClass.physical_review == search_params.physical_review) + query = query.filter(Class.physical_review == search_params.physical_review) # Contar total total = query.count() @@ -184,10 +184,10 @@ class ClassService: Returns: ClassResponseDTO actualizado o None si no existe """ - class_obj = self.db.query(GClass).filter( + class_obj = self.db.query(Class).filter( and_( - GClass.client_key == client_key, - GClass.class_code == class_code + Class.client_key == client_key, + Class.class_code == class_code ) ).first() @@ -222,10 +222,10 @@ class ClassService: Returns: True si se eliminó, False si no existe """ - class_obj = self.db.query(GClass).filter( + class_obj = self.db.query(Class).filter( and_( - GClass.client_key == client_key, - GClass.class_code == class_code + Class.client_key == client_key, + Class.class_code == class_code ) ).first() @@ -244,40 +244,40 @@ class ClassService: def search_by_fraction(self, fraction: str) -> List[ClassBasicDTO]: """Busca clases por fracción arancelaria""" - classes = self.db.query(GClass).filter(GClass.fraction.ilike(f"%{fraction}%")).all() + classes = self.db.query(Class).filter(Class.fraction.ilike(f"%{fraction}%")).all() return [ClassBasicDTO.model_validate(class_obj) for class_obj in classes] def search_by_client(self, client_key: int, skip: int = 0, limit: int = 100) -> List[ClassBasicDTO]: """Obtiene todas las clases de un cliente específico""" - classes = self.db.query(GClass).filter(GClass.client_key == client_key).offset(skip).limit(limit).all() + classes = self.db.query(Class).filter(Class.client_key == client_key).offset(skip).limit(limit).all() return [ClassBasicDTO.model_validate(class_obj) for class_obj in classes] def search_by_material(self, material_key: str) -> List[ClassBasicDTO]: """Busca clases por clave de material""" - classes = self.db.query(GClass).filter(GClass.material_key.ilike(f"%{material_key}%")).all() + classes = self.db.query(Class).filter(Class.material_key.ilike(f"%{material_key}%")).all() return [ClassBasicDTO.model_validate(class_obj) for class_obj in classes] def get_classes_by_physical_review(self, physical_review: int) -> List[ClassBasicDTO]: """Obtiene clases por indicador de revisión física""" - classes = self.db.query(GClass).filter(GClass.physical_review == physical_review).all() + classes = self.db.query(Class).filter(Class.physical_review == physical_review).all() return [ClassBasicDTO.model_validate(class_obj) for class_obj in classes] def get_classes_statistics(self) -> dict: """Obtiene estadísticas básicas de clases""" - total_classes = self.db.query(GClass).count() + total_classes = self.db.query(Class).count() # Contar por clientes - clients_count = self.db.query(GClass.client_key).distinct().count() + clients_count = self.db.query(Class.client_key).distinct().count() # Contar por revisión física physical_review_stats = {} for i in range(3): # Asumiendo valores 0, 1, 2 - count = self.db.query(GClass).filter(GClass.physical_review == i).count() + count = self.db.query(Class).filter(Class.physical_review == i).count() physical_review_stats[f"physical_review_{i}"] = count # Contar clases con fracciones - with_fraction = self.db.query(GClass).filter(GClass.fraction.isnot(None)).count() - with_us_fraction = self.db.query(GClass).filter(GClass.us_fraction.isnot(None)).count() + with_fraction = self.db.query(Class).filter(Class.fraction.isnot(None)).count() + with_us_fraction = self.db.query(Class).filter(Class.us_fraction.isnot(None)).count() return { "total_classes": total_classes, @@ -289,6 +289,6 @@ class ClassService: def get_classes_by_unit_measure(self, unit_of_measure: str) -> List[ClassBasicDTO]: """Obtiene clases por unidad de medida""" - classes = self.db.query(GClass).filter(GClass.unit_of_measure == unit_of_measure).all() + classes = self.db.query(Class).filter(Class.unit_of_measure == unit_of_measure).all() return [ClassBasicDTO.model_validate(class_obj) for class_obj in classes] diff --git a/backend/api/v1/modules/a76/client_&_provider/__init__.py b/backend/api/v1/modules/a76/client_and_provider/__init__.py similarity index 100% rename from backend/api/v1/modules/a76/client_&_provider/__init__.py rename to backend/api/v1/modules/a76/client_and_provider/__init__.py diff --git a/backend/api/v1/modules/a76/client_&_provider/dto.py b/backend/api/v1/modules/a76/client_and_provider/dto.py similarity index 100% rename from backend/api/v1/modules/a76/client_&_provider/dto.py rename to backend/api/v1/modules/a76/client_and_provider/dto.py diff --git a/backend/api/v1/modules/a76/client_&_provider/models.py b/backend/api/v1/modules/a76/client_and_provider/models.py similarity index 89% rename from backend/api/v1/modules/a76/client_&_provider/models.py rename to backend/api/v1/modules/a76/client_and_provider/models.py index 14834953..af4df115 100644 --- a/backend/api/v1/modules/a76/client_&_provider/models.py +++ b/backend/api/v1/modules/a76/client_and_provider/models.py @@ -8,11 +8,11 @@ from core.database import Base import enum -class GClientProvider(Base): +class ClientProvider(Base): """ Modelo para la tabla GClientesPro - Información de clientes y proveedores """ - __tablename__ = "gclient_provider" + __tablename__ = "client_provider" __table_args__ = {"schema": "a76"} # Primary key @@ -48,7 +48,7 @@ class GClientProviderAddress(Base): __table_args__ = {"schema": "a76"} # Primary key (foreign key) - client_id = Column(String(8), ForeignKey('a76.gclient_provider.client_id', ondelete='CASCADE'), primary_key=True, nullable=False) + client_id = Column(String(8), ForeignKey('a76.client_provider.client_id', ondelete='CASCADE'), primary_key=True, nullable=False) # Address information municipality = Column(String(150), nullable=True) @@ -67,7 +67,7 @@ class GClientProviderAddress(Base): reference = Column(String(250), nullable=True) # Relationship - client_provider = relationship("GClientProvider", back_populates="address") + client_provider = relationship("ClientProvider", back_populates="address") class GClientProviderPrograms(Base): @@ -78,7 +78,7 @@ class GClientProviderPrograms(Base): __table_args__ = {"schema": "a76"} # Primary key (foreign key) - client_id = Column(String(8), ForeignKey('a76.gclient_provider.client_id', ondelete='CASCADE'), primary_key=True, nullable=False) + client_id = Column(String(8), ForeignKey('a76.client_provider.client_id', ondelete='CASCADE'), primary_key=True, nullable=False) # Program information program = Column(String(7), nullable=True) @@ -103,6 +103,6 @@ class GClientProviderPrograms(Base): autse_number = Column(String(300), nullable=True) # Relationship - client_provider = relationship("GClientProvider", back_populates="programs") + client_provider = relationship("ClientProvider", back_populates="programs") diff --git a/backend/api/v1/modules/a76/client_&_provider/routes.py b/backend/api/v1/modules/a76/client_and_provider/routes.py similarity index 98% rename from backend/api/v1/modules/a76/client_&_provider/routes.py rename to backend/api/v1/modules/a76/client_and_provider/routes.py index fb9edd50..02d215b8 100644 --- a/backend/api/v1/modules/a76/client_&_provider/routes.py +++ b/backend/api/v1/modules/a76/client_and_provider/routes.py @@ -16,7 +16,7 @@ from .dto import ( ClientProviderListDTO ) -router = APIRouter(prefix="/clients-providers", tags=["Clients & Providers"]) +router = APIRouter(prefix="/clients-providers") @router.post("/", response_model=ClientProviderResponseDTO, status_code=status.HTTP_201_CREATED) diff --git a/backend/api/v1/modules/a76/client_&_provider/service.py b/backend/api/v1/modules/a76/client_and_provider/service.py similarity index 87% rename from backend/api/v1/modules/a76/client_&_provider/service.py rename to backend/api/v1/modules/a76/client_and_provider/service.py index a9573c10..99340da0 100644 --- a/backend/api/v1/modules/a76/client_&_provider/service.py +++ b/backend/api/v1/modules/a76/client_and_provider/service.py @@ -8,7 +8,7 @@ from fastapi import HTTPException from typing import List, Optional import logging -from .models import GClientProvider, GClientProviderAddress, GClientProviderPrograms +from .models import ClientProvider, GClientProviderAddress, GClientProviderPrograms from .dto import ( ClientProviderCreateDTO, ClientProviderUpdateDTO, @@ -43,12 +43,12 @@ class ClientProviderService: """ try: # Verificar que no exista el cliente - existing = self.db.query(GClientProvider).filter(GClientProvider.client_id == client_data.client_id).first() + existing = self.db.query(ClientProvider).filter(ClientProvider.client_id == client_data.client_id).first() if existing: raise HTTPException(status_code=400, detail=f"Client with ID '{client_data.client_id}' already exists") # Crear cliente/proveedor principal - db_client = GClientProvider( + db_client = ClientProvider( client_id=client_data.client_id, type_nat_foreign=client_data.type_nat_foreign, name=client_data.name, @@ -118,10 +118,10 @@ class ClientProviderService: def _get_client_with_relations(self, client_id: str) -> Optional[ClientProviderResponseDTO]: """Método privado para obtener cliente con relaciones""" - client = self.db.query(GClientProvider).options( - joinedload(GClientProvider.address), - joinedload(GClientProvider.programs) - ).filter(GClientProvider.client_id == client_id).first() + client = self.db.query(ClientProvider).options( + joinedload(ClientProvider.address), + joinedload(ClientProvider.programs) + ).filter(ClientProvider.client_id == client_id).first() if not client: return None @@ -148,25 +148,25 @@ class ClientProviderService: Returns: ClientProviderListDTO con la lista paginada """ - query = self.db.query(GClientProvider) + query = self.db.query(ClientProvider) # Aplicar filtros if search: search_pattern = f"%{search}%" query = query.filter( or_( - GClientProvider.name.ilike(search_pattern), - GClientProvider.short_name.ilike(search_pattern), - GClientProvider.rfc.ilike(search_pattern), - GClientProvider.client_id.ilike(search_pattern) + ClientProvider.name.ilike(search_pattern), + ClientProvider.short_name.ilike(search_pattern), + ClientProvider.rfc.ilike(search_pattern), + ClientProvider.client_id.ilike(search_pattern) ) ) if client_or_provider: - query = query.filter(GClientProvider.client_or_provider == client_or_provider) + query = query.filter(ClientProvider.client_or_provider == client_or_provider) if enabled_only: - query = query.filter(GClientProvider.enabled_disabled == 1) + query = query.filter(ClientProvider.enabled_disabled == 1) # Contar total total = query.count() @@ -195,7 +195,7 @@ class ClientProviderService: Returns: ClientProviderResponseDTO actualizado o None si no existe """ - client = self.db.query(GClientProvider).filter(GClientProvider.client_id == client_id).first() + client = self.db.query(ClientProvider).filter(ClientProvider.client_id == client_id).first() if not client: return None @@ -257,7 +257,7 @@ class ClientProviderService: Returns: True si se eliminó, False si no existe """ - client = self.db.query(GClientProvider).filter(GClientProvider.client_id == client_id).first() + client = self.db.query(ClientProvider).filter(ClientProvider.client_id == client_id).first() if not client: return False @@ -273,24 +273,24 @@ class ClientProviderService: def get_clients_only(self, skip: int = 0, limit: int = 100) -> List[ClientProviderBasicDTO]: """Obtiene solo clientes (C)""" - query = self.db.query(GClientProvider).filter(GClientProvider.client_or_provider == 'C') + query = self.db.query(ClientProvider).filter(ClientProvider.client_or_provider == 'C') clients = query.offset(skip).limit(limit).all() return [ClientProviderBasicDTO.model_validate(client) for client in clients] def get_providers_only(self, skip: int = 0, limit: int = 100) -> List[ClientProviderBasicDTO]: """Obtiene solo proveedores (P)""" - query = self.db.query(GClientProvider).filter(GClientProvider.client_or_provider == 'P') + query = self.db.query(ClientProvider).filter(ClientProvider.client_or_provider == 'P') providers = query.offset(skip).limit(limit).all() return [ClientProviderBasicDTO.model_validate(provider) for provider in providers] def search_by_rfc(self, rfc: str) -> List[ClientProviderBasicDTO]: """Busca clientes/proveedores por RFC""" - clients = self.db.query(GClientProvider).filter(GClientProvider.rfc.ilike(f"%{rfc}%")).all() + clients = self.db.query(ClientProvider).filter(ClientProvider.rfc.ilike(f"%{rfc}%")).all() return [ClientProviderBasicDTO.model_validate(client) for client in clients] def toggle_status(self, client_id: str) -> Optional[ClientProviderResponseDTO]: """Cambia el estado habilitado/deshabilitado""" - client = self.db.query(GClientProvider).filter(GClientProvider.client_id == client_id).first() + client = self.db.query(ClientProvider).filter(ClientProvider.client_id == client_id).first() if not client: return None diff --git a/backend/api/v1/modules/a76/company/routes.py b/backend/api/v1/modules/a76/company/routes.py index 4473591d..1b72f7a0 100644 --- a/backend/api/v1/modules/a76/company/routes.py +++ b/backend/api/v1/modules/a76/company/routes.py @@ -10,7 +10,7 @@ from core.security import get_current_user, has_role from .service import CompanyService from .dto import CompanyCreateDTO, CompanyUpdateDTO, CompanyResponseDTO -router = APIRouter(prefix="/company", tags=["Company"]) +router = APIRouter(prefix="/company") @router.post("/", response_model=CompanyResponseDTO, status_code=status.HTTP_201_CREATED) diff --git a/backend/api/v1/modules/a76/GParts/__init__.py b/backend/api/v1/modules/a76/parts/__init__.py similarity index 100% rename from backend/api/v1/modules/a76/GParts/__init__.py rename to backend/api/v1/modules/a76/parts/__init__.py diff --git a/backend/api/v1/modules/a76/GParts/dto.py b/backend/api/v1/modules/a76/parts/dto.py similarity index 100% rename from backend/api/v1/modules/a76/GParts/dto.py rename to backend/api/v1/modules/a76/parts/dto.py diff --git a/backend/api/v1/modules/a76/GParts/models.py b/backend/api/v1/modules/a76/parts/models.py similarity index 85% rename from backend/api/v1/modules/a76/GParts/models.py rename to backend/api/v1/modules/a76/parts/models.py index df58cb04..4e787537 100644 --- a/backend/api/v1/modules/a76/GParts/models.py +++ b/backend/api/v1/modules/a76/parts/models.py @@ -13,14 +13,14 @@ from typing import TYPE_CHECKING, Optional if TYPE_CHECKING: from api.v1.modules.public.reference_data.countries.models import Country from api.v1.modules.public.reference_data.currency_types.models import CurrencyType - from api.v1.modules.a76.GClass.models import GClass + from api.v1.modules.a76.classes.models import Class -class GPart(Base): +class Part(Base): """ Modelo para la tabla GPartes - Información de partes en los sistemas SCAII (N), SCAF (S) Y WINSAAI (W) """ - __tablename__ = "gparts" + __tablename__ = "parts" __table_args__ = {"schema": "a76"} # Primary key compuesta @@ -72,17 +72,17 @@ class GPart(Base): country = relationship("Country", foreign_keys=[country_of_origin]) currency = relationship("CurrencyType", foreign_keys=[currency_key]) - # Relationship with GClass through composite foreign key - # Note: This requires both client_key and part_class to match client_key and class_code in GClass + # Relationship with Class through composite foreign key + # Note: This requires both client_key and part_class to match client_key and class_code in Class part_class_info = relationship( - "GClass", - primaryjoin="and_(GPart.client_key == GClass.client_key, GPart.part_class == GClass.class_code)", - foreign_keys="[GPart.client_key, GPart.part_class]", + "Class", + primaryjoin="and_(Part.client_key == Class.client_key, Part.part_class == Class.class_code)", + foreign_keys="[Part.client_key, Part.part_class]", viewonly=True, back_populates="parts" ) def __repr__(self): - return f"" + return f"" diff --git a/backend/api/v1/modules/a76/GParts/routes.py b/backend/api/v1/modules/a76/parts/routes.py similarity index 99% rename from backend/api/v1/modules/a76/GParts/routes.py rename to backend/api/v1/modules/a76/parts/routes.py index 36486b04..4757fbc5 100644 --- a/backend/api/v1/modules/a76/GParts/routes.py +++ b/backend/api/v1/modules/a76/parts/routes.py @@ -17,7 +17,7 @@ from .dto import ( PartSearchDTO ) -router = APIRouter(prefix="/parts", tags=["Parts"]) +router = APIRouter(prefix="/parts") @router.post("/", response_model=PartResponseDTO, status_code=status.HTTP_201_CREATED) diff --git a/backend/api/v1/modules/a76/GParts/service.py b/backend/api/v1/modules/a76/parts/service.py similarity index 81% rename from backend/api/v1/modules/a76/GParts/service.py rename to backend/api/v1/modules/a76/parts/service.py index cdc08dc4..11cbb13f 100644 --- a/backend/api/v1/modules/a76/GParts/service.py +++ b/backend/api/v1/modules/a76/parts/service.py @@ -9,7 +9,7 @@ from typing import List, Optional import logging from datetime import datetime -from .models import GPart +from .models import Part from .dto import PartCreateDTO, PartUpdateDTO logger = logging.getLogger(__name__) @@ -21,12 +21,12 @@ class PartService: """ @staticmethod - def create_part(db: Session, part_data: PartCreateDTO) -> GPart: + def create_part(db: Session, part_data: PartCreateDTO) -> Part: """ Crear una nueva parte """ try: - db_part = GPart(**part_data.model_dump()) + db_part = Part(**part_data.model_dump()) db.add(db_part) db.commit() db.refresh(db_part) @@ -41,15 +41,15 @@ class PartService: raise HTTPException(status_code=500, detail="Error creating part") @staticmethod - def get_part(db: Session, client_key: int, part_number: str) -> Optional[GPart]: + def get_part(db: Session, client_key: int, part_number: str) -> Optional[Part]: """ Obtener una parte por clave de cliente y número de parte """ try: - return db.query(GPart).filter( + return db.query(Part).filter( and_( - GPart.client_key == client_key, - GPart.part_number == part_number + Part.client_key == client_key, + Part.part_number == part_number ) ).first() except Exception as e: @@ -65,29 +65,29 @@ class PartService: client_key: Optional[int] = None, fraction: Optional[str] = None, country_of_origin: Optional[str] = None - ) -> tuple[List[GPart], int]: + ) -> tuple[List[Part], int]: """ Obtener partes con paginación y filtros """ try: - query = db.query(GPart) + query = db.query(Part) # Aplicar filtros if search: query = query.filter(or_( - GPart.description_spanish.ilike(f"%{search}%"), - GPart.description_english.ilike(f"%{search}%"), - GPart.part_number.ilike(f"%{search}%") + Part.description_spanish.ilike(f"%{search}%"), + Part.description_english.ilike(f"%{search}%"), + Part.part_number.ilike(f"%{search}%") )) if client_key is not None: - query = query.filter(GPart.client_key == client_key) + query = query.filter(Part.client_key == client_key) if fraction: - query = query.filter(GPart.fraction == fraction) + query = query.filter(Part.fraction == fraction) if country_of_origin: - query = query.filter(GPart.country_of_origin == country_of_origin) + query = query.filter(Part.country_of_origin == country_of_origin) # Contar total total = query.count() @@ -101,26 +101,26 @@ class PartService: raise HTTPException(status_code=500, detail="Error retrieving parts") @staticmethod - def get_parts_by_client(db: Session, client_key: int) -> List[GPart]: + def get_parts_by_client(db: Session, client_key: int) -> List[Part]: """ Obtener todas las partes de un cliente específico """ try: - return db.query(GPart).filter(GPart.client_key == client_key).all() + return db.query(Part).filter(Part.client_key == client_key).all() except Exception as e: logger.error(f"Error getting parts by client: {e}") raise HTTPException(status_code=500, detail="Error retrieving client parts") @staticmethod - def search_parts_by_fraction(db: Session, fraction: str) -> List[GPart]: + def search_parts_by_fraction(db: Session, fraction: str) -> List[Part]: """ Buscar partes por fracción arancelaria """ try: - return db.query(GPart).filter( + return db.query(Part).filter( or_( - GPart.fraction.ilike(f"%{fraction}%"), - GPart.us_fraction.ilike(f"%{fraction}%") + Part.fraction.ilike(f"%{fraction}%"), + Part.us_fraction.ilike(f"%{fraction}%") ) ).all() except Exception as e: @@ -128,29 +128,29 @@ class PartService: raise HTTPException(status_code=500, detail="Error searching parts by fraction") @staticmethod - def search_parts_by_supplier(db: Session, supplier: str) -> List[GPart]: + def search_parts_by_supplier(db: Session, supplier: str) -> List[Part]: """ Buscar partes por proveedor """ try: - return db.query(GPart).filter(GPart.supplier.ilike(f"%{supplier}%")).all() + return db.query(Part).filter(Part.supplier.ilike(f"%{supplier}%")).all() except Exception as e: logger.error(f"Error searching parts by supplier: {e}") raise HTTPException(status_code=500, detail="Error searching parts by supplier") @staticmethod - def search_parts_by_country(db: Session, country_code: str) -> List[GPart]: + def search_parts_by_country(db: Session, country_code: str) -> List[Part]: """ Buscar partes por país de origen """ try: - return db.query(GPart).filter(GPart.country_of_origin == country_code).all() + return db.query(Part).filter(Part.country_of_origin == country_code).all() except Exception as e: logger.error(f"Error searching parts by country: {e}") raise HTTPException(status_code=500, detail="Error searching parts by country") @staticmethod - def update_part(db: Session, client_key: int, part_number: str, part_data: PartUpdateDTO) -> Optional[GPart]: + def update_part(db: Session, client_key: int, part_number: str, part_data: PartUpdateDTO) -> Optional[Part]: """ Actualizar una parte existente """ @@ -190,7 +190,7 @@ class PartService: raise HTTPException(status_code=500, detail="Error deleting part") @staticmethod - def toggle_part_status(db: Session, client_key: int, part_number: str) -> Optional[GPart]: + def toggle_part_status(db: Session, client_key: int, part_number: str) -> Optional[Part]: """ Cambiar el estado habilitado/deshabilitado de una parte """ @@ -216,24 +216,24 @@ class PartService: Obtener estadísticas de partes """ try: - total_parts = db.query(GPart).count() + total_parts = db.query(Part).count() # Partes por cliente parts_by_client = db.query( - GPart.client_key, - func.count(GPart.part_number).label('count') - ).group_by(GPart.client_key).all() + Part.client_key, + func.count(Part.part_number).label('count') + ).group_by(Part.client_key).all() # Partes por país de origen parts_by_country = db.query( - GPart.country_of_origin, - func.count(GPart.part_number).label('count') - ).filter(GPart.country_of_origin.isnot(None))\ - .group_by(GPart.country_of_origin).all() + Part.country_of_origin, + func.count(Part.part_number).label('count') + ).filter(Part.country_of_origin.isnot(None))\ + .group_by(Part.country_of_origin).all() # Partes habilitadas vs deshabilitadas - enabled_parts = db.query(GPart).filter(GPart.enabled_disabled == 1).count() - disabled_parts = db.query(GPart).filter(GPart.enabled_disabled == 0).count() + enabled_parts = db.query(Part).filter(Part.enabled_disabled == 1).count() + disabled_parts = db.query(Part).filter(Part.enabled_disabled == 0).count() return { "total_parts": total_parts, diff --git a/backend/api/v1/modules/a76/router.py b/backend/api/v1/modules/a76/router.py index 06ebeac3..6d695913 100644 --- a/backend/api/v1/modules/a76/router.py +++ b/backend/api/v1/modules/a76/router.py @@ -9,6 +9,10 @@ from .auth import router as auth_router from .tenants import router as tenants_router from .licenses import router as licenses_router from .pedmientos.router import router as pedimentos_router +from .client_and_provider import router as client_and_provider_router +from .company import router as company_router +from .classes import router as classes_router +from .parts import router as parts_router # Router principal router = APIRouter() @@ -18,3 +22,8 @@ router.include_router(auth_router) router.include_router(tenants_router, prefix="/a76", tags=["a76 / tenants"]) router.include_router(licenses_router, prefix="/a76", tags=["a76 / licenses"]) router.include_router(pedimentos_router, prefix="/a76") +router.include_router(client_and_provider_router, prefix="/a76", tags=["a76 / clients and providers"]) +router.include_router(company_router, prefix="/a76", tags=["a76 / company"]) +router.include_router(classes_router, prefix="/a76", tags=["a76 / classes"]) +router.include_router(parts_router, prefix="/a76", tags=["a76 / parts"]) + diff --git a/docs/MODULOS_A76_IMPLEMENTADOS.md b/docs/MODULOS_A76_IMPLEMENTADOS.md index 90dd563c..e76b8329 100644 --- a/docs/MODULOS_A76_IMPLEMENTADOS.md +++ b/docs/MODULOS_A76_IMPLEMENTADOS.md @@ -20,7 +20,7 @@ - Control de inventario y clasificación arancelaria - Información regulatoria y de cumplimiento -### Módulo de Clases (GClass) +### Módulo de Clases (Class) - Clasificaciones para sistemas SCAII y SCAF - Información arancelaria detallada - Gestión de fracciones arancelarias y materiales @@ -30,17 +30,17 @@ ## 🔗 Relaciones de Base de Datos ### Relaciones Principales -- **GPart ↔ GClass**: Relación de clave compuesta (client_key, part_class ↔ class_code) -- **GPart → Country**: Clave foránea a public.countries (country_of_origin) -- **GPart → CurrencyType**: Clave foránea a public.currency_types (currency_key) -- **GClass → MaterialType**: Clave foránea a public.material_types (material_key) +- **Part ↔ Class**: Relación de clave compuesta (client_key, part_class ↔ class_code) +- **Part → Country**: Clave foránea a public.countries (country_of_origin) +- **Part → CurrencyType**: Clave foránea a public.currency_types (currency_key) +- **Class → MaterialType**: Clave foránea a public.material_types (material_key) ### Esquema de Relaciones ``` -GPart (Partes) +Part (Partes) ├── País de origen → Country ├── Tipo de moneda → CurrencyType -└── Información de clase → GClass +└── Información de clase → Class └── Tipo de material → MaterialType ``` diff --git a/docs/RELATIONSHIPS.md b/docs/RELATIONSHIPS.md index 362ca6fe..78895954 100644 --- a/docs/RELATIONSHIPS.md +++ b/docs/RELATIONSHIPS.md @@ -2,8 +2,8 @@ ## Resumen de Relaciones Establecidas -### GPart (Tabla: gparts) -El modelo `GPart` representa las partes/componentes en los sistemas SCAII, SCAF y WINSAAI. +### Part (Tabla: parts) +El modelo `Part` representa las partes/componentes en los sistemas SCAII, SCAF y WINSAAI. #### Relaciones: @@ -17,14 +17,14 @@ El modelo `GPart` representa las partes/componentes en los sistemas SCAII, SCAF - Relación: Many-to-One - Propósito: Tipo de moneda para el costo unitario -3. **Con GClass (gclasses)** +3. **Con Class (classes)** - Campos: `(client_key, part_class)` → `(client_key, class_code)` - Relación: Many-to-One (usando primaryjoin complejo) - Propósito: Clasificación de la parte - Atributo: `part_class_info` -### GClass (Tabla: gclasses) -El modelo `GClass` representa las clases de clasificación en sistemas SCAII y SCAF. +### Class (Tabla: classes) +El modelo `Class` representa las clases de clasificación en sistemas SCAII y SCAF. #### Relaciones: @@ -33,24 +33,24 @@ El modelo `GClass` representa las clases de clasificación en sistemas SCAII y S - Relación: Many-to-One - Propósito: Tipo de material de la clase -2. **Con GPart (gparts)** +2. **Con Part (parts)** - Campos: `(client_key, class_code)` → `(client_key, part_class)` - - Relación: One-to-Many (inversa de la relación en GPart) + - Relación: One-to-Many (inversa de la relación en Part) - Propósito: Partes que pertenecen a esta clase - Atributo: `parts` ## Esquema de Relaciones ``` -GPart +Part ├── country (Country) # País de origen ├── currency (CurrencyType) # Tipo de moneda -└── part_class_info (GClass) # Información de clasificación +└── part_class_info (Class) # Información de clasificación └── material_type (MaterialType) # Tipo de material -GClass +Class ├── material_type (MaterialType) # Tipo de material -└── parts (List[GPart]) # Partes que usan esta clase +└── parts (List[Part]) # Partes que usan esta clase ``` ## Uso de las Relaciones @@ -58,13 +58,13 @@ GClass ### En consultas: ```python # Obtener una parte con su información completa -part = session.query(GPart).options( - joinedload(GPart.country), - joinedload(GPart.currency), - joinedload(GPart.part_class_info).joinedload(GClass.material_type) +part = session.query(Part).options( + joinedload(Part.country), + joinedload(Part.currency), + joinedload(Part.part_class_info).joinedload(Class.material_type) ).filter( - GPart.client_key == 1, - GPart.part_number == "PART001" + Part.client_key == 1, + Part.part_number == "PART001" ).first() # Acceder a los datos relacionados @@ -89,7 +89,7 @@ class PartDetailResponseDTO(BaseModel): ## Consideraciones Técnicas -1. **Composite Foreign Keys**: La relación entre `GPart` y `GClass` usa claves foráneas compuestas que requieren `primaryjoin` personalizado. +1. **Composite Foreign Keys**: La relación entre `Part` y `Class` usa claves foráneas compuestas que requieren `primaryjoin` personalizado. 2. **Viewonly Relationships**: Algunas relaciones están marcadas como `viewonly=True` para evitar problemas de escritura accidental. diff --git a/docs/SCHEMA_A76_UPDATE.md b/docs/SCHEMA_A76_UPDATE.md index 5a3fadba..c4587692 100644 --- a/docs/SCHEMA_A76_UPDATE.md +++ b/docs/SCHEMA_A76_UPDATE.md @@ -13,11 +13,11 @@ Se han actualizado todos los modelos en `api/v1/modules/a76/` para usar el schem | Módulo | Tabla | Schema | Estado | |--------|-------|---------|---------| | **Company** | `gcompany` | `a76` | ✅ Actualizada | -| **Client & Provider** | `gclient_provider` | `a76` | ✅ Actualizada | +| **Client & Provider** | `client_provider` | `a76` | ✅ Actualizada | | **Client & Provider** | `gclient_provider_address` | `a76` | ✅ Actualizada | | **Client & Provider** | `gclient_provider_programs` | `a76` | ✅ Actualizada | -| **GParts** | `gparts` | `a76` | ✅ Actualizada | -| **GClass** | `gclasses` | `a76` | ✅ Actualizada | +| **GParts** | `parts` | `a76` | ✅ Actualizada | +| **Class** | `classes` | `a76` | ✅ Actualizada | | **Licenses** | `licenses` | `a76` | ✅ Ya estaba | | **Licenses** | `license_usage` | `a76` | ✅ Ya estaba | | **Tenants** | `tenants` | `a76` | ✅ Ya estaba | @@ -39,10 +39,10 @@ class GCompany(Base): #### 2. Foreign Keys Actualizadas ```python # ANTES -client_id = Column(String(8), ForeignKey('gclient_provider.client_id'), ...) +client_id = Column(String(8), ForeignKey('client_provider.client_id'), ...) # DESPUÉS -client_id = Column(String(8), ForeignKey('a76.gclient_provider.client_id'), ...) +client_id = Column(String(8), ForeignKey('a76.client_provider.client_id'), ...) ``` ### 🏗️ Estructura de Schemas @@ -60,11 +60,11 @@ PostgreSQL Database ├── licenses ├── license_usage ├── gcompany - ├── gclient_provider + ├── client_provider ├── gclient_provider_address ├── gclient_provider_programs - ├── gparts - └── gclasses + ├── parts + └── classes ``` ### 🔗 Relaciones Mantenidas @@ -77,13 +77,13 @@ Las relaciones entre schemas funcionan correctamente: #### Ejemplos de Relaciones Cross-Schema: ```python -# GPart (a76) → Country (public) +# Part (a76) → Country (public) country_of_origin = Column(String(3), ForeignKey('public.countries.m3_key')) -# GPart (a76) → CurrencyType (public) +# Part (a76) → CurrencyType (public) currency_key = Column(String(3), ForeignKey('public.currency_types.code')) -# GClass (a76) → MaterialType (public) +# Class (a76) → MaterialType (public) material_key = Column(String(10), ForeignKey('public.material_types.key')) ```