diff --git a/backend/alembic/env.py b/backend/alembic/env.py index 348bcb40..fbe67248 100644 --- a/backend/alembic/env.py +++ b/backend/alembic/env.py @@ -6,6 +6,7 @@ from logging.config import fileConfig from urllib.parse import quote_plus from alembic import context +from alembic.operations import ops from core.config import settings from core.database import Base from sqlalchemy import engine_from_config, pool @@ -78,6 +79,125 @@ fileConfig(config.config_file_name) target_metadata = Base.metadata +def include_object(object_, name, type_, reflected, compare_to): + """ + Keep all objects in autogenerate. + FK noise is cleaned in process_revision_directives. + """ + return True + + +def _fk_drop_signature(op_): + if not isinstance(op_, ops.DropConstraintOp): + return None + if getattr(op_, "constraint_type", None) != "foreignkey": + return None + return ( + getattr(op_, "schema", None), + getattr(op_, "table_name", None), + getattr(op_, "constraint_name", None), + ) + + +def _fk_create_signature(op_): + if not isinstance(op_, ops.CreateForeignKeyOp): + return None + local_cols = tuple(getattr(op_, "local_cols", ()) or ()) + remote_cols = tuple(getattr(op_, "remote_cols", ()) or ()) + return ( + getattr(op_, "source_schema", None), + getattr(op_, "source_table", None), + getattr(op_, "referent_schema", None), + getattr(op_, "referent_table", None), + local_cols, + remote_cols, + ) + + +def _drop_to_create_match(drop_op, create_op): + if not isinstance(drop_op, ops.DropConstraintOp): + return False + if not isinstance(create_op, ops.CreateForeignKeyOp): + return False + if getattr(drop_op, "constraint_type", None) != "foreignkey": + return False + + def _normalize_schema(value): + # PostgreSQL reports default schema inconsistently as None/public. + return "public" if value in (None, "") else value + + # Prefer structural comparison using Alembic's reverse op when available. + reverse_create = getattr(drop_op, "_reverse", None) + if isinstance(reverse_create, ops.CreateForeignKeyOp): + return ( + _normalize_schema(getattr(reverse_create, "source_schema", None)) + == _normalize_schema(getattr(create_op, "source_schema", None)) + and getattr(reverse_create, "source_table", None) == getattr(create_op, "source_table", None) + and _normalize_schema(getattr(reverse_create, "referent_schema", None)) + == _normalize_schema(getattr(create_op, "referent_schema", None)) + and getattr(reverse_create, "referent_table", None) == getattr(create_op, "referent_table", None) + and tuple(getattr(reverse_create, "local_cols", ()) or ()) + == tuple(getattr(create_op, "local_cols", ()) or ()) + and tuple(getattr(reverse_create, "remote_cols", ()) or ()) + == tuple(getattr(create_op, "remote_cols", ()) or ()) + ) + + # Fallback for older op payloads: compare source table/schema and name. + return ( + _normalize_schema(getattr(drop_op, "schema", None)) == _normalize_schema(getattr(create_op, "source_schema", None)) + and getattr(drop_op, "table_name", None) == getattr(create_op, "source_table", None) + and getattr(drop_op, "constraint_name", None) == getattr(create_op, "constraint_name", None) + ) + + +def _prune_fk_churn(container): + if not hasattr(container, "ops"): + return + + # First recurse into nested containers. + for op_ in list(container.ops): + _prune_fk_churn(op_) + + table_ops = container.ops + kept_ops = [] + consumed_indexes = set() + + for i, op_i in enumerate(table_ops): + if i in consumed_indexes: + continue + + if isinstance(op_i, ops.DropConstraintOp) and getattr(op_i, "constraint_type", None) == "foreignkey": + matched_j = None + for j in range(i + 1, len(table_ops)): + if j in consumed_indexes: + continue + op_j = table_ops[j] + if _drop_to_create_match(op_i, op_j): + matched_j = j + break + if matched_j is not None: + # Drop + recreate same FK detected; remove both. + consumed_indexes.add(i) + consumed_indexes.add(matched_j) + continue + + kept_ops.append(op_i) + + container.ops = kept_ops + + +def process_revision_directives(context_, revision, directives): + """ + Remove autogenerate noise where Alembic emits drop/create for equivalent FKs. + Real FK changes are preserved. + """ + if not directives: + return + script = directives[0] + _prune_fk_churn(script.upgrade_ops) + _prune_fk_churn(script.downgrade_ops) + + def import_models_from_dir(dir_path: str): """Importa recursivamente cualquier archivo models.py desde dir_path y archivos en directorios models/""" import sys @@ -132,6 +252,10 @@ def run_migrations_offline() -> None: context.configure( url=url, target_metadata=target_metadata, + compare_type=True, + include_schemas=True, + include_object=include_object, + process_revision_directives=process_revision_directives, literal_binds=True, dialect_opts={"paramstyle": "named"}, ) @@ -153,7 +277,14 @@ def run_migrations_online() -> None: ) with connectable.connect() as connection: - context.configure(connection=connection, target_metadata=target_metadata) + context.configure( + connection=connection, + target_metadata=target_metadata, + compare_type=True, + include_schemas=True, + include_object=include_object, + process_revision_directives=process_revision_directives, + ) with context.begin_transaction(): context.run_migrations() diff --git a/backend/alembic/versions/4ad64605fad2_first_migration.py b/backend/alembic/versions/4ad64605fad2_first_migration.py new file mode 100644 index 00000000..9f0e1130 --- /dev/null +++ b/backend/alembic/versions/4ad64605fad2_first_migration.py @@ -0,0 +1,4643 @@ +"""first_migration + +Revision ID: 4ad64605fad2 +Revises: +Create Date: 2026-03-18 16:32:41.420671 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision: str = '4ad64605fad2' +down_revision: Union[str, Sequence[str], None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('inv_aphis_catalog', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('program_code', sa.String(length=10), nullable=True), + sa.Column('processing_code', sa.String(length=10), nullable=True), + sa.Column('aphis_type', sa.String(length=10), nullable=True), + sa.Column('disclaimer', sa.String(length=10), nullable=True), + sa.Column('electronic_image', sa.String(length=50), nullable=True), + sa.Column('confidential', sa.String(length=1), nullable=True), + sa.Column('global_product_id', sa.String(length=100), nullable=True), + sa.Column('intended_use_code', sa.String(length=10), nullable=True), + sa.Column('intended_use_description', sa.String(length=200), nullable=True), + sa.Column('item_type', sa.String(length=20), nullable=True), + sa.Column('product_code', sa.String(length=20), nullable=True), + sa.Column('product_code_2', sa.String(length=20), nullable=True), + sa.Column('product_code_3', sa.String(length=20), nullable=True), + sa.Column('scientific_genus_name', sa.String(length=100), nullable=True), + sa.Column('scientific_species_name', sa.String(length=100), nullable=True), + sa.Column('scientific_sub_species_name', sa.String(length=100), nullable=True), + sa.Column('common_name_specific', sa.String(length=200), nullable=True), + sa.Column('common_name_general', sa.String(length=200), nullable=True), + sa.Column('signed_doc', sa.String(length=100), nullable=True), + sa.Column('signed_doc_date', sa.Date(), nullable=True), + sa.Column('signed_doc_id', sa.String(length=50), nullable=True), + sa.Column('invoice_number', sa.String(length=50), nullable=True), + sa.Column('quantity_1', sa.String(length=50), nullable=True), + sa.Column('quantity_2', sa.String(length=50), nullable=True), + sa.Column('quantity_3', sa.String(length=50), nullable=True), + sa.Column('inspection', sa.String(length=200), nullable=True), + sa.Column('inspection_date', sa.Date(), nullable=True), + sa.Column('inspection_loc_date', sa.Date(), nullable=True), + sa.Column('inspection_location', sa.String(length=200), nullable=True), + sa.Column('country_production', sa.String(length=3), nullable=True), + sa.Column('country_source', sa.String(length=3), nullable=True), + sa.Column('characteristics', sa.JSON(), nullable=True), + sa.Column('pitems', sa.JSON(), nullable=True), + sa.Column('lpcos', sa.JSON(), nullable=True), + sa.Column('entities', sa.JSON(), nullable=True), + sa.Column('containers', sa.JSON(), nullable=True), + sa.Column('routing', sa.JSON(), nullable=True), + sa.PrimaryKeyConstraint('id', name='inv_aphis_catalog_pkey'), + schema='a24' + ) + op.create_index(op.f('ix_a24_inv_aphis_catalog_company_id'), 'inv_aphis_catalog', ['company_id'], unique=False, schema='a24') + op.create_table('tariff_fractions', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('code', sa.String(length=10), nullable=False), + sa.Column('fraction', sa.String(length=15), nullable=False), + sa.Column('description', sa.String(length=1000), nullable=True), + sa.Column('nico', sa.String(length=10), nullable=True), + sa.Column('umt', sa.String(length=10), nullable=True), + sa.Column('adv_impo', sa.String(length=20), nullable=True), + sa.Column('adv_expo', sa.String(length=20), nullable=True), + sa.PrimaryKeyConstraint('id', name='tariff_fractions_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_tariff_fractions_code'), 'tariff_fractions', ['code'], unique=True, schema='a76') + op.create_index(op.f('ix_a76_tariff_fractions_fraction'), 'tariff_fractions', ['fraction'], unique=False, schema='a76') + op.create_table('unit_of_measure_ace', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('code', sa.String(length=4), nullable=False), + sa.Column('description', sa.String(length=49), nullable=True), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', name='uq_uom_ace_code'), + schema='a76' + ) + op.create_table('unit_of_measure_american', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('code', sa.String(length=3), nullable=False), + sa.Column('description', sa.String(length=40), nullable=True), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', name='uq_uom_american_code'), + schema='a76' + ) + op.create_table('unit_of_measure_customs', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('code', sa.String(length=10), nullable=False), + sa.Column('description', sa.String(length=20), nullable=True), + sa.Column('a76_unit_code', sa.String(length=5), nullable=True), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', name='uq_uom_customs_code'), + schema='a76' + ) + op.create_table('unit_of_measure_oma', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('code', sa.String(length=10), nullable=False), + sa.Column('description', sa.String(length=200), nullable=True), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', name='uq_uom_oma_code'), + schema='a76' + ) + op.create_table('containers', + sa.Column('key', sa.String(length=3), nullable=False), + sa.Column('description', sa.String(length=500), nullable=False), + sa.PrimaryKeyConstraint('key', name='containers_pkey') + ) + op.create_table('permissions', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('code', sa.String(length=100), nullable=False), + sa.Column('description', sa.String(length=255), nullable=True), + sa.Column('module', sa.String(length=50), nullable=False), + sa.Column('action', sa.String(length=50), nullable=False), + sa.Column('is_active', sa.Boolean(), server_default='true', nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id'), + schema='core' + ) + op.create_index(op.f('ix_core_permissions_code'), 'permissions', ['code'], unique=True, schema='core') + op.create_index(op.f('ix_core_permissions_id'), 'permissions', ['id'], unique=False, schema='core') + op.create_index(op.f('ix_core_permissions_module'), 'permissions', ['module'], unique=False, schema='core') + op.create_table('tenants', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=255), nullable=False), + sa.Column('slug', sa.String(length=100), nullable=False), + sa.Column('type', sa.Enum('SHARED', 'DEDICATED', name='tenanttype'), server_default='SHARED', nullable=False), + sa.Column('keycloak_realm', sa.String(length=255), nullable=False), + sa.Column('db_config', sa.Text(), nullable=True), + sa.Column('contact_name', sa.String(length=255), nullable=True), + sa.Column('contact_email', sa.String(length=255), nullable=True), + sa.Column('contact_phone', sa.String(length=50), nullable=True), + sa.Column('is_active', sa.Boolean(), server_default='true', nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('id'), + schema='core' + ) + op.create_index(op.f('ix_core_tenants_id'), 'tenants', ['id'], unique=False, schema='core') + op.create_index(op.f('ix_core_tenants_name'), 'tenants', ['name'], unique=False, schema='core') + op.create_index(op.f('ix_core_tenants_slug'), 'tenants', ['slug'], unique=True, schema='core') + op.create_table('help_articles', + sa.Column('uuid', sa.UUID(), nullable=False), + sa.Column('slug', sa.String(length=255), nullable=False), + sa.Column('title', sa.String(length=255), nullable=False), + sa.Column('content', sa.Text(), nullable=False), + sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False), + sa.Column('last_editor', sa.String(length=255), nullable=False), + sa.Column('category', sa.String(length=255), nullable=True), + sa.Column('order', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('uuid') + ) + op.create_index(op.f('ix_help_articles_slug'), 'help_articles', ['slug'], unique=True) + op.create_index(op.f('ix_help_articles_uuid'), 'help_articles', ['uuid'], unique=False) + op.create_table('agency_tariff_codes', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('tariff_flag_code', sa.String(length=10), nullable=False), + sa.Column('agency_code', sa.String(length=10), nullable=False), + sa.Column('requirement_level', sa.String(length=1), nullable=False), + sa.Column('program_code', sa.String(length=10), nullable=False), + sa.Column('definition', sa.String(length=500), nullable=False), + sa.PrimaryKeyConstraint('id', name='agency_tariff_codes_pkey'), + schema='public' + ) + op.create_index(op.f('ix_public_agency_tariff_codes_agency_code'), 'agency_tariff_codes', ['agency_code'], unique=False, schema='public') + op.create_index(op.f('ix_public_agency_tariff_codes_program_code'), 'agency_tariff_codes', ['program_code'], unique=False, schema='public') + op.create_index(op.f('ix_public_agency_tariff_codes_tariff_flag_code'), 'agency_tariff_codes', ['tariff_flag_code'], unique=False, schema='public') + op.create_table('carta_porte_codes', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('code', sa.String(length=20), nullable=False), + sa.Column('description', sa.String(length=2000), nullable=False), + sa.Column('similar_words', sa.String(length=2000), nullable=True), + sa.Column('is_hazardous', sa.Integer(), nullable=False), + sa.Column('start_date', sa.String(length=20), nullable=True), + sa.Column('end_date', sa.String(length=20), nullable=True), + sa.PrimaryKeyConstraint('id'), + schema='public' + ) + op.create_index(op.f('ix_public_carta_porte_code'), 'carta_porte_codes', ['code'], unique=False, schema='public') + op.create_table('countries', + sa.Column('m3_key', sa.String(length=3), nullable=False), + sa.Column('mex_key', sa.String(length=2), nullable=False), + sa.Column('ame_key', sa.String(length=2), nullable=False), + sa.Column('description_es', sa.String(length=50), nullable=False), + sa.Column('description_en', sa.String(length=50), nullable=False), + sa.PrimaryKeyConstraint('m3_key', name='countries_pkey'), + schema='public' + ) + op.create_index('ak_country_ame', 'countries', ['ame_key'], unique=True, schema='public') + op.create_table('currency_types', + sa.Column('code', sa.String(length=3), nullable=False), + sa.Column('currency_name', sa.String(length=15), nullable=False), + sa.Column('country_description', sa.String(length=50), nullable=False), + sa.PrimaryKeyConstraint('code', name='currency_types_pkey'), + schema='public' + ) + op.create_table('customs_sections', + sa.Column('customs_code', sa.String(length=3), nullable=False), + sa.Column('section_name', sa.String(length=255), nullable=False), + sa.PrimaryKeyConstraint('customs_code', name='customs_code_pkey'), + schema='public' + ) + op.create_table('customs_warehouses', + sa.Column('key', sa.String(length=3), nullable=False), + sa.Column('customs', sa.String(length=100), nullable=False), + sa.Column('fiscalized_warehouse', sa.String(length=1000), nullable=False), + sa.PrimaryKeyConstraint('key', 'customs', name='pk_customs_warehouse'), + schema='public' + ) + op.create_table('identifiers', + sa.Column('key', sa.String(length=10), nullable=False), + sa.Column('description', sa.String(length=2000), nullable=False), + sa.Column('level', sa.String(length=1), nullable=False), + sa.Column('complement', sa.String(length=5000), nullable=False), + sa.PrimaryKeyConstraint('key', name='identifiers_pkey'), + schema='public' + ) + op.create_table('incoterms', + sa.Column('code', sa.String(length=5), nullable=False), + sa.Column('description_es', sa.String(length=256), nullable=False), + sa.Column('description_en', sa.String(length=256), nullable=False), + sa.PrimaryKeyConstraint('code', name='incoterms_pkey'), + schema='public' + ) + op.create_table('invoice_types', + sa.Column('key', sa.String(length=5), nullable=False), + sa.Column('description', sa.String(length=50), nullable=False), + sa.Column('note', sa.String(length=500), nullable=False), + sa.Column('type', sa.String(length=15), nullable=False), + sa.Column('operation', sa.String(length=5), nullable=False), + sa.PrimaryKeyConstraint('key', name='invoice_types_pkey'), + schema='public' + ) + op.create_table('license_exceptions', + sa.Column('key', sa.String(length=10), nullable=False), + sa.Column('description', sa.String(length=500), nullable=False), + sa.PrimaryKeyConstraint('key', name='license_exceptions_pkey'), + schema='public' + ) + op.create_table('material_types', + sa.Column('key', sa.String(length=10), nullable=False), + sa.Column('type', sa.String(length=15), nullable=False), + sa.Column('description', sa.String(length=256), nullable=False), + sa.PrimaryKeyConstraint('key', name='material_types_pkey'), + schema='public' + ) + op.create_table('payment_methods', + sa.Column('key', sa.String(length=2), nullable=False), + sa.Column('description', sa.String(length=100), nullable=False), + sa.PrimaryKeyConstraint('key', name='payment_methods_pkey'), + schema='public' + ) + op.create_table('pedimento_codes', + sa.Column('code', sa.String(length=3), nullable=False), + sa.Column('description', sa.String(length=250), nullable=False), + sa.PrimaryKeyConstraint('code', name='pedimento_codes_pkey'), + schema='public' + ) + op.create_table('pedimento_regimens', + sa.Column('code', sa.String(length=3), nullable=False), + sa.Column('description', sa.String(length=100), nullable=False), + sa.PrimaryKeyConstraint('code', name='pedimento_regimens_pkey'), + schema='public' + ) + op.create_table('pedimento_transport_catalog', + sa.Column('code', sa.String(length=3), nullable=False), + sa.Column('transport_en', sa.String(length=80), nullable=False), + sa.Column('transport_es', sa.String(length=120), nullable=False), + sa.Column('payment_date_code', sa.String(length=1), nullable=False), + sa.CheckConstraint("payment_date_code IN ('E', 'P')", name='pedimento_transport_catalog_payment_date_code_chk'), + sa.PrimaryKeyConstraint('code', name='pedimento_transport_catalog_pkey'), + schema='public' + ) + op.create_table('trailer_type', + sa.Column('trailer_type_key', sa.String(length=2), nullable=False), + sa.Column('description', sa.String(length=100), nullable=True), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('trailer_type_key'), + schema='public' + ) + op.create_table('transport_modes', + sa.Column('key', sa.String(length=3), nullable=False), + sa.Column('name', sa.String(length=30), nullable=False), + sa.PrimaryKeyConstraint('key', name='transport_modes_pkey'), + schema='public' + ) + op.create_table('transport_types', + sa.Column('transport_code', sa.String(length=2), nullable=False), + sa.Column('description', sa.String(length=100), nullable=False), + sa.PrimaryKeyConstraint('transport_code', name='transport_types_pkey'), + schema='public' + ) + op.create_table('valuation_methods', + sa.Column('key', sa.String(length=2), nullable=False), + sa.Column('description', sa.String(length=200), nullable=False), + sa.PrimaryKeyConstraint('key', name='valuation_methods_pkey'), + schema='public' + ) + op.create_table('company', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=256), nullable=True), + sa.Column('rfc', sa.String(length=30), nullable=True), + sa.Column('curp', sa.String(length=19), nullable=True), + sa.Column('main_activity', sa.String(length=80), nullable=True), + sa.Column('program', sa.String(length=7), nullable=True), + sa.Column('program_number', sa.String(length=40), nullable=True), + sa.Column('prosec', sa.Boolean(), server_default='false', nullable=False), + sa.Column('prosec_authorization', sa.String(length=20), nullable=True), + sa.Column('sector1', sa.String(length=150), nullable=True), + sa.Column('sector2', sa.String(length=150), nullable=True), + sa.Column('sector3', sa.String(length=5), nullable=True), + sa.Column('manufacturer_id', sa.String(length=25), nullable=True), + sa.Column('broker_company', sa.String(length=6), 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(), server_default='false', nullable=True), + sa.Column('order_format_type', sa.String(length=19), nullable=True), + sa.Column('is_service_company', sa.Boolean(), server_default='false', nullable=True), + sa.Column('client_name', sa.String(length=300), nullable=True), + sa.Column('subassembly_mode', sa.String(length=7), nullable=True), + sa.Column('previous_code', sa.SmallInteger(), nullable=True), + sa.Column('active_labels', sa.SmallInteger(), nullable=True), + sa.Column('active_fractions', sa.SmallInteger(), nullable=True), + sa.Column('activate_caat', sa.SmallInteger(), nullable=True), + sa.Column('trans_interface', sa.SmallInteger(), nullable=True), + sa.Column('american_costs', sa.SmallInteger(), nullable=True), + sa.Column('scaf_readonly', sa.SmallInteger(), nullable=True), + sa.Column('parts_replacement', sa.SmallInteger(), nullable=True), + sa.Column('activate_facmexame', sa.SmallInteger(), nullable=True), + sa.Column('part_reference', sa.SmallInteger(), nullable=True), + sa.Column('international_firm', sa.SmallInteger(), nullable=True), + sa.Column('seventh_amendment', sa.Boolean(), nullable=True), + sa.Column('ftp_key', sa.String(length=10), nullable=True), + sa.Column('sifra_path', sa.String(length=255), nullable=True), + sa.Column('version_type', sa.String(length=20), nullable=True), + sa.Column('sql_language', sa.String(length=19), nullable=True), + sa.Column('balance_operation_mode', sa.String(length=50), nullable=True), + sa.Column('inter_db_name', sa.String(length=100), nullable=True), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_company_tenant_id'), 'company', ['tenant_id'], unique=False, schema='a76') + op.create_table('customs_broker_concepts', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('broker_key', sa.String(length=5), nullable=False), + sa.Column('concept', sa.String(length=15), nullable=False), + sa.Column('amount', sa.Numeric(precision=11, scale=2), nullable=True), + sa.Column('priority', sa.Integer(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('broker_key', 'concept', 'company_id', name='uq_broker_concept'), + schema='a76' + ) + op.create_index(op.f('ix_a76_customs_broker_concepts_tenant_id'), 'customs_broker_concepts', ['tenant_id'], unique=False, 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(), server_default='0', nullable=True), + sa.Column('storage_used_gb', sa.Integer(), server_default='0', nullable=True), + sa.Column('operations_count', sa.Integer(), server_default='0', nullable=True), + sa.Column('api_calls_count', sa.Integer(), server_default='0', nullable=True), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='core' + ) + op.create_index(op.f('ix_core_license_usage_id'), 'license_usage', ['id'], unique=False, schema='core') + op.create_index(op.f('ix_core_license_usage_tenant_id'), 'license_usage', ['tenant_id'], unique=False, schema='core') + 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'), server_default='FREE', nullable=False), + sa.Column('status', sa.Enum('ACTIVE', 'EXPIRED', 'SUSPENDED', 'PENDING', 'CANCELLED', name='licensestatus'), server_default='PENDING', nullable=False), + sa.Column('max_users', sa.Integer(), server_default='5', nullable=False), + sa.Column('max_storage_gb', sa.Integer(), server_default='10', nullable=False), + sa.Column('max_monthly_operations', sa.Integer(), server_default='1000', nullable=False), + sa.Column('feature_api_access', sa.Boolean(), server_default='true', nullable=True), + sa.Column('feature_advanced_reports', sa.Boolean(), server_default='false', nullable=True), + sa.Column('feature_integrations', sa.Boolean(), server_default='false', nullable=True), + sa.Column('feature_dedicated_support', sa.Boolean(), server_default='false', 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(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='core' + ) + op.create_index(op.f('ix_core_licenses_id'), 'licenses', ['id'], unique=False, schema='core') + op.create_index(op.f('ix_core_licenses_tenant_id'), 'licenses', ['tenant_id'], unique=True, schema='core') + op.create_table('code_pedimento_regimens', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_code', sa.String(length=3), nullable=False), + sa.Column('regimen_code', sa.String(length=3), nullable=False), + sa.Column('type_code', sa.String(length=1), nullable=True), + sa.ForeignKeyConstraint(['pedimento_code'], ['public.pedimento_codes.code'], name='fk_codeped'), + sa.ForeignKeyConstraint(['regimen_code'], ['public.pedimento_regimens.code'], name='fk_regimenped'), + sa.PrimaryKeyConstraint('id', name='clave_pedimento_regimens_pkey'), + schema='public' + ) + op.create_table('states', + sa.Column('m3_key', sa.String(length=3), nullable=False), + sa.Column('description', sa.String(length=50), nullable=False), + sa.Column('mex_key', sa.String(length=3), nullable=True), + sa.ForeignKeyConstraint(['m3_key'], ['public.countries.m3_key'], ), + sa.PrimaryKeyConstraint('m3_key', 'description', name='states_pkey'), + schema='public' + ) + op.create_table('CompanyVU', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('webservice_user', sa.String(length=100), nullable=True), + sa.Column('webservice_password', sa.String(length=100), nullable=True), + sa.Column('email', sa.String(length=800), nullable=True), + sa.Column('figure_type', sa.String(length=29), nullable=True), + sa.Column('central_path', sa.String(length=1499), nullable=True), + sa.Column('xml_files_path', sa.String(length=1499), nullable=True), + sa.Column('query_rfc', sa.String(length=30), nullable=True), + sa.Column('validation_rfc', sa.String(length=30), nullable=True), + sa.Column('configuration_source', sa.String(length=30), nullable=True), + sa.Column('measurement_units', sa.String(length=3), nullable=True), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_CompanyVU_company_id'), 'CompanyVU', ['company_id'], unique=True, schema='a76') + op.create_table('audit_logs', + sa.Column('spec_id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('reference', sa.String(length=100), nullable=False), + sa.Column('procedure', sa.String(length=100), nullable=False), + sa.Column('movement', sa.String(length=255), nullable=False), + sa.Column('username', sa.String(length=100), nullable=False), + sa.Column('date', sa.Date(), nullable=False), + sa.Column('time', sa.Time(), nullable=False), + sa.Column('timestamp', sa.DateTime(timezone=True), nullable=False), + sa.Column('system', sa.String(length=20), nullable=False), + sa.Column('table_name', sa.String(length=100), nullable=True), + sa.Column('record_id', sa.String(length=255), nullable=True), + sa.Column('operation_type', sa.String(length=20), nullable=True), + sa.Column('old_values', postgresql.JSONB(astext_type=sa.Text()), nullable=True), + sa.Column('new_values', postgresql.JSONB(astext_type=sa.Text()), nullable=True), + sa.Column('changed_fields', postgresql.ARRAY(sa.String()), nullable=True), + sa.Column('ip_address', sa.String(length=45), nullable=True), + sa.Column('user_agent', sa.Text(), nullable=True), + sa.Column('endpoint', sa.String(length=500), nullable=True), + sa.Column('request_method', sa.String(length=10), nullable=True), + sa.Column('session_id', sa.String(length=50), nullable=True), + sa.Column('execution_time_ms', sa.Integer(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('spec_id'), + schema='a76' + ) + op.create_index('idx_audit_procedure_date', 'audit_logs', ['procedure', 'date'], unique=False, schema='a76') + op.create_index('idx_audit_system_timestamp', 'audit_logs', ['system', 'timestamp'], unique=False, schema='a76') + op.create_index('idx_audit_table_record', 'audit_logs', ['table_name', 'record_id'], unique=False, schema='a76') + op.create_index('idx_audit_username_date', 'audit_logs', ['username', 'date'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_audit_logs_company_id'), 'audit_logs', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_audit_logs_date'), 'audit_logs', ['date'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_audit_logs_operation_type'), 'audit_logs', ['operation_type'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_audit_logs_procedure'), 'audit_logs', ['procedure'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_audit_logs_record_id'), 'audit_logs', ['record_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_audit_logs_reference'), 'audit_logs', ['reference'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_audit_logs_session_id'), 'audit_logs', ['session_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_audit_logs_system'), 'audit_logs', ['system'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_audit_logs_table_name'), 'audit_logs', ['table_name'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_audit_logs_tenant_id'), 'audit_logs', ['tenant_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_audit_logs_timestamp'), 'audit_logs', ['timestamp'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_audit_logs_username'), 'audit_logs', ['username'], unique=False, schema='a76') + 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), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + 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_company_id'), 'canadian_tariff_fractions', ['company_id'], unique=False, 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_tenant_id'), 'canadian_tariff_fractions', ['tenant_id'], unique=False, schema='a76') + op.create_table('classification_concepts', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('classification', sa.String(length=30), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('classification', name='uq_classification_concept'), + schema='a76' + ) + op.create_index(op.f('ix_a76_classification_concepts_company_id'), 'classification_concepts', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_classification_concepts_tenant_id'), 'classification_concepts', ['tenant_id'], unique=False, schema='a76') + op.create_table('clients_and_providers', + sa.Column('id', sa.Integer(), 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.Enum('CLIENT', 'PROVIDER', 'BOTH', name='entity_client_or_provider'), nullable=False), + 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_active', sa.Boolean(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='clients_and_providers_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_clients_and_providers_company_id'), 'clients_and_providers', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_clients_and_providers_tenant_id'), 'clients_and_providers', ['tenant_id'], unique=False, schema='a76') + op.create_table('company_address', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('address_type', sa.String(length=20), nullable=False), + sa.Column('street', sa.String(length=100), nullable=True), + sa.Column('exterior_number', sa.String(length=20), nullable=True), + sa.Column('interior_number', sa.String(length=20), nullable=True), + sa.Column('postal_code', sa.String(length=15), nullable=True), + sa.Column('neighborhood', sa.String(length=40), nullable=True), + sa.Column('city', sa.String(length=40), nullable=True), + sa.Column('municipality', sa.String(length=50), nullable=True), + sa.Column('state', sa.String(length=30), nullable=True), + sa.Column('country', sa.String(length=4), nullable=True), + sa.Column('phone', sa.String(length=30), nullable=True), + sa.Column('fax', sa.String(length=30), nullable=True), + sa.Column('email', sa.String(length=100), nullable=True), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_company_address_company_id'), 'company_address', ['company_id'], unique=False, schema='a76') + op.create_table('company_certification', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('is_certified_company', sa.String(length=1), nullable=True), + sa.Column('certified_company_registration', sa.String(length=40), nullable=True), + sa.Column('certified_company_start_date', sa.Integer(), nullable=True), + sa.Column('certified_company_end_date', sa.Integer(), nullable=True), + sa.Column('annex31_certification_date', sa.Integer(), nullable=True), + sa.Column('annex31_certification_number', sa.String(length=50), nullable=True), + sa.Column('annex31_modality', sa.String(length=50), nullable=True), + sa.Column('annex31_company_type', sa.String(length=50), nullable=True), + sa.Column('annex31_renewal_date', sa.Integer(), nullable=True), + sa.Column('annex31_final_certification_date', sa.Integer(), nullable=True), + sa.Column('is_oea_company', sa.SmallInteger(), 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('neec_company', sa.Integer(), nullable=True), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_company_certification_company_id'), 'company_certification', ['company_id'], unique=True, schema='a76') + op.create_table('company_cfdi', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('xml_save_path', sa.String(length=5000), nullable=True), + sa.Column('cfdi_app_path', sa.String(length=5000), nullable=True), + sa.Column('pac_app_path', sa.String(length=5000), nullable=True), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_company_cfdi_company_id'), 'company_cfdi', ['company_id'], unique=True, schema='a76') + op.create_table('company_digital_certificate', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('certificate_type', sa.String(length=20), nullable=False), + sa.Column('cer_file_path', sa.String(length=5000), nullable=True), + sa.Column('key_file_path', sa.String(length=5000), nullable=True), + sa.Column('password', sa.String(length=200), nullable=True), + sa.Column('access_key', sa.String(length=50), nullable=True), + sa.Column('cer_expiration_date', sa.Integer(), nullable=True), + sa.Column('key_expiration_date', sa.Integer(), nullable=True), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_company_digital_certificate_company_id'), 'company_digital_certificate', ['company_id'], unique=False, schema='a76') + op.create_table('company_electronic_agent', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('input_folder', sa.String(length=1000), nullable=True), + sa.Column('output_folder', sa.String(length=1000), nullable=True), + sa.Column('send_mask', sa.String(length=20), nullable=True), + sa.Column('response_mask', sa.String(length=20), nullable=True), + sa.Column('response_extension', sa.String(length=20), nullable=True), + sa.Column('counter_start', sa.Integer(), nullable=True), + sa.Column('counter_end', sa.Integer(), nullable=True), + sa.Column('counter_next', sa.Integer(), nullable=True), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_company_electronic_agent_company_id'), 'company_electronic_agent', ['company_id'], unique=True, schema='a76') + op.create_table('company_prevalidator', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('customs', sa.String(length=20), nullable=True), + sa.Column('key', sa.String(length=20), nullable=True), + sa.Column('patent', sa.String(length=4), nullable=True), + sa.Column('description', sa.String(length=100), nullable=True), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_company_prevalidator_company_id'), 'company_prevalidator', ['company_id'], unique=True, schema='a76') + op.create_table('customs_brokers', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('type', sa.String(length=9), nullable=True), + sa.Column('broker_key', sa.String(length=5), nullable=False), + sa.Column('name', sa.String(length=80), nullable=True), + sa.Column('address', sa.String(length=1500), 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('phone', sa.String(length=30), nullable=True), + sa.Column('fax', sa.String(length=30), nullable=True), + sa.Column('email', sa.String(length=100), nullable=True), + sa.Column('country', sa.String(length=3), nullable=True), + sa.Column('tax_id', sa.String(length=30), nullable=True), + sa.Column('personal_id', sa.String(length=20), nullable=True), + sa.Column('position', sa.String(length=30), nullable=True), + sa.Column('license', sa.String(length=4), nullable=True), + sa.Column('company', sa.String(length=200), nullable=True), + sa.Column('contact', sa.String(length=80), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='customs_brokers_pkey'), + sa.UniqueConstraint('broker_key', 'tenant_id', 'company_id', name='uq_broker_key_tenant_company'), + schema='a76' + ) + op.create_index(op.f('ix_a76_customs_brokers_company_id'), 'customs_brokers', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_customs_brokers_tenant_id'), 'customs_brokers', ['tenant_id'], unique=False, schema='a76') + op.create_table('depreciation_catalog', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('fraction', sa.String(length=10), nullable=False), + sa.Column('description', sa.String(length=500), nullable=False), + sa.Column('depreciation_rate', sa.Numeric(precision=5, scale=2), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='depreciation_catalog_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_depreciation_catalog_company_id'), 'depreciation_catalog', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_depreciation_catalog_description'), 'depreciation_catalog', ['description'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_depreciation_catalog_fraction'), 'depreciation_catalog', ['fraction'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_depreciation_catalog_tenant_id'), 'depreciation_catalog', ['tenant_id'], unique=False, schema='a76') + op.create_table('document_types_digitization', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('code', sa.String(length=10), nullable=False), + sa.Column('description', sa.Text(), nullable=False), + sa.Column('active', sa.Boolean(), server_default='true', nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='document_types_digitization_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'code', name='document_types_digitization_code_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_document_types_digitization_code'), 'document_types_digitization', ['code'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_document_types_digitization_company_id'), 'document_types_digitization', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_document_types_digitization_tenant_id'), 'document_types_digitization', ['tenant_id'], unique=False, schema='a76') + op.create_table('doda', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('integration_number', sa.String(length=30), nullable=True), + sa.Column('doda_date', sa.Integer(), nullable=True), + sa.Column('doda_time', sa.Integer(), nullable=True), + sa.Column('dispatch_customs', sa.String(length=3), nullable=True), + sa.Column('customs_sections', sa.String(length=3), nullable=True), + sa.Column('patent', sa.String(length=4), nullable=True), + sa.Column('pedimentos', sa.String(length=80), nullable=True), + sa.Column('caat', sa.String(length=10), nullable=True), + sa.Column('transport_identification', sa.String(length=20), nullable=True), + sa.Column('fast_id', sa.String(length=20), nullable=True), + sa.Column('operation_type', sa.String(length=1), nullable=True), + sa.Column('selected', sa.Boolean(), nullable=True), + sa.Column('user_selected', sa.String(length=30), nullable=True), + sa.Column('last_user', sa.String(length=30), nullable=True), + sa.Column('responsible', sa.String(length=14), nullable=True), + sa.Column('carrier', sa.String(length=8), nullable=True), + sa.Column('shipments', sa.String(length=80), nullable=True), + sa.Column('pedimento_type', sa.String(length=30), nullable=True), + sa.Column('original_chain', sa.String(length=5000), nullable=True), + sa.Column('serial_number', sa.String(length=21), nullable=True), + sa.Column('electronic_signature', sa.String(length=2000), nullable=True), + sa.Column('transaction_number', sa.String(length=30), nullable=True), + sa.Column('status', sa.String(length=30), nullable=True), + sa.Column('linq_sat_qr', sa.String(length=1000), nullable=True), + sa.Column('sat_certificate', sa.String(length=2001), nullable=True), + sa.Column('sat_digital_seal', sa.Text(), nullable=True), + sa.Column('xml_doda_sent_path', sa.String(length=1000), nullable=True), + sa.Column('xml_doda_response_path', sa.String(length=1000), nullable=True), + sa.Column('sat_original_chain', sa.Text(), nullable=True), + sa.Column('customs_clearance', sa.Integer(), nullable=True), + sa.Column('unique_badge_number', sa.String(length=250), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='doda_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_doda_company_id'), 'doda', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_doda_tenant_id'), 'doda', ['tenant_id'], unique=False, schema='a76') + op.create_table('electronic_notices', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('notice_number', sa.String(length=500), nullable=True), + sa.Column('year', sa.String(length=20), nullable=True), + sa.Column('patent', sa.String(length=4), nullable=True), + sa.Column('pedimento', sa.String(length=15), nullable=True), + sa.Column('file_sent', sa.String(length=1000), nullable=True), + sa.Column('file_response', sa.String(length=1000), nullable=True), + sa.Column('status', sa.String(length=100), nullable=True), + sa.Column('invoice', sa.String(length=50), nullable=True), + sa.Column('validation_acknowledgment', sa.String(length=20), nullable=True), + sa.Column('fea', sa.String(length=1000), nullable=True), + sa.Column('certificate_number', sa.String(length=50), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='electronic_notices_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_electronic_notices_company_id'), 'electronic_notices', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_electronic_notices_tenant_id'), 'electronic_notices', ['tenant_id'], unique=False, schema='a76') + op.create_table('equivalency_items', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('original_field', sa.String(length=100), nullable=False), + sa.Column('external_field', sa.String(length=100), nullable=False), + sa.Column('conversion_factor', sa.Numeric(precision=13, scale=6), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('original_field', 'external_field', 'tenant_id', 'company_id', name='uq_equivalency_item_fields'), + schema='a76' + ) + op.create_index(op.f('ix_a76_equivalency_items_company_id'), 'equivalency_items', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_equivalency_items_tenant_id'), 'equivalency_items', ['tenant_id'], unique=False, schema='a76') + op.create_table('error_classifications', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('code', sa.String(length=100), nullable=False), + sa.Column('level', sa.String(length=3), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='error_classifications_pkey'), + sa.UniqueConstraint('code'), + sa.UniqueConstraint('code', name='error_classifications_code_unique'), + schema='a76' + ) + op.create_index(op.f('ix_a76_error_classifications_company_id'), 'error_classifications', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_error_classifications_tenant_id'), 'error_classifications', ['tenant_id'], unique=False, schema='a76') + op.create_table('exchange_rate', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('date', sa.DateTime(), nullable=False), + sa.Column('value', sa.DECIMAL(precision=13, scale=6), nullable=True), + sa.Column('local_currency', sa.String(length=7), nullable=True), + sa.Column('foreign_currency', sa.String(length=7), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='exchange_rate_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'date', name='uq_exchange_rate_date_tenant'), + schema='a76' + ) + op.create_index(op.f('ix_a76_exchange_rate_company_id'), 'exchange_rate', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_exchange_rate_tenant_id'), 'exchange_rate', ['tenant_id'], unique=False, schema='a76') + op.create_table('fda_catalog', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('fda_key', sa.String(length=20), nullable=False), + sa.Column('description', sa.String(length=500), nullable=False), + sa.Column('fda_code', sa.String(length=50), nullable=True), + sa.Column('requirements', sa.String(length=500), nullable=True), + sa.Column('manufacturer_number', sa.String(length=50), nullable=True), + sa.Column('country_of_production', sa.String(length=100), nullable=True), + sa.Column('storage_status', sa.String(length=100), nullable=True), + sa.Column('warehouse_code', sa.String(length=20), nullable=True), + sa.Column('call_atl', sa.String(length=20), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='fda_catalog_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'fda_key', name='idx_fda_catalog_unique'), + schema='a76' + ) + op.create_index(op.f('ix_a76_fda_catalog_company_id'), 'fda_catalog', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_fda_catalog_description'), 'fda_catalog', ['description'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_fda_catalog_fda_key'), 'fda_catalog', ['fda_key'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_fda_catalog_tenant_id'), 'fda_catalog', ['tenant_id'], unique=False, schema='a76') + op.create_table('fraction_rule_octave', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('permission', sa.String(length=20), nullable=False), + sa.Column('line', sa.Integer(), nullable=False), + sa.Column('fraction', sa.String(length=10), nullable=False), + sa.Column('quota_quantity', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('quantity_used', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('quota_value', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_used', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('unit_cost_me', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('unit_of_measure', sa.String(length=5), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('tenant_id', 'company_id', 'permission', 'line', 'fraction', name='uq_fraction_rule_octave_permission_line_fraction'), + schema='a76' + ) + op.create_index(op.f('ix_a76_fraction_rule_octave_company_id'), 'fraction_rule_octave', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_fraction_rule_octave_tenant_id'), 'fraction_rule_octave', ['tenant_id'], unique=False, schema='a76') + 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('nico', sa.String(length=2), nullable=True), + sa.Column('unit_of_measure_code', sa.String(length=10), nullable=True), + sa.Column('country', sa.String(length=3), 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), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['country'], ['public.countries.m3_key'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.ForeignKeyConstraint(['unit_of_measure_code'], ['a76.unit_of_measure_customs.code'], ), + sa.PrimaryKeyConstraint('id'), + 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') + op.create_table('identifiers', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('code', sa.String(length=2), nullable=False), + sa.Column('description', sa.String(length=1000), nullable=True), + sa.Column('level', sa.String(length=1), nullable=True), + sa.Column('complement', sa.String(length=5000), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', name='uq_identifier_code'), + schema='a76' + ) + op.create_index(op.f('ix_a76_identifiers_company_id'), 'identifiers', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_identifiers_tenant_id'), 'identifiers', ['tenant_id'], unique=False, schema='a76') + op.create_table('inpc', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('year', sa.String(length=4), nullable=False), + sa.Column('month', sa.String(length=2), nullable=False), + sa.Column('value', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('year', 'month', 'tenant_id', 'company_id', name='uq_inpc_year_month'), + schema='a76' + ) + op.create_index(op.f('ix_a76_inpc_company_id'), 'inpc', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_inpc_tenant_id'), 'inpc', ['tenant_id'], unique=False, schema='a76') + op.create_table('invoice_header', + sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column('system', sa.String(length=12), nullable=False), + sa.Column('operation_type', sa.String(length=11), nullable=False), + sa.Column('invoice_type', sa.String(length=5), nullable=False), + sa.Column('document_type', sa.String(length=3), nullable=True), + sa.Column('invoice_number', sa.String(length=100), nullable=False), + sa.Column('project_number', sa.String(length=14), nullable=True), + sa.Column('purchase_order', sa.String(length=50), nullable=True), + sa.Column('related_doc_id', sa.Integer(), nullable=True), + sa.Column('alternate_invoice', sa.String(length=99), nullable=True), + sa.Column('invoice_ref', sa.String(length=19), nullable=True), + sa.Column('proforma_number', sa.String(length=20), nullable=True), + sa.Column('invoice_date', sa.Date(), nullable=False), + sa.Column('capture_date', sa.TIMESTAMP(), server_default=sa.text('now()'), nullable=False), + sa.Column('emission_date', sa.Date(), nullable=True), + sa.Column('status', sa.String(length=10), nullable=False), + sa.Column('status_rec', sa.String(length=10), nullable=True), + sa.Column('status_rep', sa.String(length=10), nullable=True), + sa.Column('processed_date', sa.TIMESTAMP(), nullable=True), + sa.Column('who_processed', sa.String(length=20), nullable=True), + sa.Column('capture_user', sa.String(length=20), nullable=True), + sa.Column('traffic_light_status', sa.String(length=50), nullable=True), + sa.Column('process_log', sa.String(length=300), nullable=True), + sa.Column('observation_es', sa.Text(), nullable=True), + sa.Column('observation_en', sa.Text(), nullable=True), + sa.Column('comments_status', sa.Text(), nullable=True), + sa.Column('vu_observations', sa.String(length=500), nullable=True), + sa.Column('cfdi_uuid', sa.String(length=100), nullable=True), + sa.Column('path_pdf', sa.String(length=500), nullable=True), + sa.Column('path_xml', sa.String(length=500), nullable=True), + sa.Column('subcompany', sa.String(length=5), nullable=True), + sa.Column('party_count', sa.Integer(), nullable=True), + sa.Column('generate_id', sa.Boolean(), server_default='false', nullable=True), + sa.Column('generate_desc_parties', sa.String(length=12), nullable=True), + sa.Column('apply_manual_discount', sa.Boolean(), server_default='false', nullable=True), + sa.Column('is_bulk', sa.Boolean(), nullable=True), + sa.Column('download_substance', sa.Boolean(), nullable=True), + sa.Column('download_class', sa.Boolean(), nullable=True), + sa.Column('download_def', sa.Boolean(), nullable=True), + sa.Column('payment_terms', sa.String(length=200), nullable=True), + sa.Column('handling_fees', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('option_iv18', sa.String(length=50), nullable=True), + sa.Column('enajenation_goods', sa.Boolean(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['document_type'], ['public.pedimento_regimens.code'], ), + sa.ForeignKeyConstraint(['invoice_type'], ['public.invoice_types.key'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_invoice_header_company_id'), 'invoice_header', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_invoice_header_tenant_id'), 'invoice_header', ['tenant_id'], unique=False, schema='a76') + op.create_table('invoice_settings', + sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column('invoice_type', sa.String(length=5), nullable=False), + sa.Column('operation_type', sa.String(length=11), nullable=False), + sa.Column('settings', postgresql.JSONB(astext_type=sa.Text()), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['invoice_type'], ['public.invoice_types.key'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('tenant_id', 'company_id', 'invoice_type', 'operation_type', name='uq_invoice_settings_tenant_company_type_op'), + schema='a76' + ) + op.create_index(op.f('ix_a76_invoice_settings_company_id'), 'invoice_settings', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_invoice_settings_tenant_id'), 'invoice_settings', ['tenant_id'], unique=False, schema='a76') + op.create_table('item_presets', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(length=100), nullable=False), + sa.Column('description', sa.String(length=500), nullable=True), + sa.Column('items', sa.JSON(), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_item_presets_company_id'), 'item_presets', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_item_presets_tenant_id'), 'item_presets', ['tenant_id'], unique=False, schema='a76') + op.create_table('legends', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('code', sa.Integer(), nullable=False), + sa.Column('description', sa.String(length=2000), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='uq_legend_code'), + schema='a76' + ) + op.create_index(op.f('ix_a76_legends_company_id'), 'legends', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_legends_tenant_id'), 'legends', ['tenant_id'], unique=False, schema='a76') + op.create_table('location', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('clave_localizacion', sa.String(length=20), nullable=False), + sa.Column('localizacion', sa.String(length=200), nullable=True), + sa.Column('system', sa.String(length=20), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('clave_localizacion', 'tenant_id', 'company_id', 'system', name='uq_location_clave_tenant_company_system'), + schema='a76' + ) + op.create_index(op.f('ix_a76_location_company_id'), 'location', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_location_tenant_id'), 'location', ['tenant_id'], unique=False, schema='a76') + op.create_table('manifest_anexos', + sa.Column('consecutive', sa.Integer(), nullable=False), + sa.Column('line_number', sa.Integer(), nullable=False), + sa.Column('attachment_type', sa.String(length=10), nullable=True), + sa.Column('number', sa.String(length=10), nullable=True), + sa.Column('attached_doc', sa.String(length=200), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('consecutive', 'line_number', name='manifest_anexos_pkey'), + schema='a76' + ) + op.create_index('idx_manifest_anexos_consecutive', 'manifest_anexos', ['consecutive'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_manifest_anexos_company_id'), 'manifest_anexos', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_manifest_anexos_tenant_id'), 'manifest_anexos', ['tenant_id'], unique=False, schema='a76') + op.create_table('manifest_drivers', + sa.Column('manifest_number', sa.String(length=15), nullable=False), + sa.Column('driver_name', sa.String(length=80), nullable=False), + sa.Column('driver_type', sa.String(length=1), nullable=True), + sa.Column('address_1', sa.String(length=100), nullable=True), + sa.Column('address_2', sa.String(length=100), nullable=True), + sa.Column('city', sa.String(length=30), nullable=True), + sa.Column('state', sa.String(length=30), nullable=True), + sa.Column('postal_code', sa.String(length=15), nullable=True), + sa.Column('country', sa.String(length=3), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('manifest_number', 'driver_name', name='manifest_drivers_pkey'), + schema='a76' + ) + op.create_index('idx_manifest_drivers_manifest_number', 'manifest_drivers', ['manifest_number'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_manifest_drivers_company_id'), 'manifest_drivers', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_manifest_drivers_tenant_id'), 'manifest_drivers', ['tenant_id'], unique=False, schema='a76') + op.create_table('manifests', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('manifest_number', sa.String(length=15), nullable=True), + sa.Column('importer_details', sa.String(length=60), nullable=True), + sa.Column('person_in_charge', sa.String(length=60), nullable=True), + sa.Column('consigned_to', sa.String(length=8), nullable=True), + sa.Column('sent_by', sa.String(length=8), nullable=True), + sa.Column('foreign_exit_port', sa.String(length=6), nullable=True), + sa.Column('foreign_exit_port_loc', sa.String(length=4), nullable=True), + sa.Column('destination_port', sa.String(length=6), nullable=True), + sa.Column('destination_port_loc', sa.String(length=4), nullable=True), + sa.Column('entry_port', sa.String(length=6), nullable=True), + sa.Column('entry_port_loc', sa.String(length=4), nullable=True), + sa.Column('entry_date', sa.Integer(), nullable=True), + sa.Column('net_weight', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('gross_weight', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('total_value', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('description', sa.String(length=2500), nullable=True), + sa.Column('broker_code', sa.String(length=5), nullable=True), + sa.Column('carrier_code', sa.String(length=5), nullable=True), + sa.Column('payment_invoice_number', sa.String(length=5), nullable=True), + sa.Column('seal_number', sa.String(length=30), nullable=True), + sa.Column('entry_hour', sa.Integer(), nullable=True), + sa.Column('hazardous_material', sa.String(length=2), nullable=True), + sa.Column('transport_mode', sa.String(length=2), nullable=True), + sa.Column('transport_code', sa.String(length=14), nullable=True), + sa.Column('trailer_number', sa.String(length=20), nullable=True), + sa.Column('status', sa.String(length=14), nullable=True), + sa.Column('status_description', sa.String(length=1000), nullable=True), + sa.Column('manifest_type', sa.String(length=3), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index('idx_manifests_manifest_number', 'manifests', ['manifest_number'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_manifests_company_id'), 'manifests', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_manifests_tenant_id'), 'manifests', ['tenant_id'], unique=False, schema='a76') + op.create_table('multi_currency_types', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('currency_type_code', sa.String(length=3), nullable=False), + sa.Column('country_key', sa.String(length=3), nullable=True), + sa.Column('conversion_factor', sa.Numeric(precision=13, scale=6), nullable=True), + sa.Column('publication_date', sa.Integer(), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['country_key'], ['public.countries.m3_key'], ), + sa.ForeignKeyConstraint(['currency_type_code'], ['public.currency_types.code'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('currency_type_code', 'publication_date', 'tenant_id', 'company_id', name='uq_multi_currency_type_code_date'), + schema='a76' + ) + op.create_index(op.f('ix_a76_multi_currency_types_company_id'), 'multi_currency_types', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_multi_currency_types_tenant_id'), 'multi_currency_types', ['tenant_id'], unique=False, schema='a76') + op.create_table('octave_balance', + sa.Column('invoice_import', sa.String(length=15), nullable=False), + sa.Column('part_number', sa.String(length=70), nullable=False), + sa.Column('origin_country', sa.String(length=3), nullable=False), + sa.Column('fraction_type', sa.String(length=7), nullable=False), + sa.Column('sector', sa.String(length=8), nullable=False), + sa.Column('octave_permit', sa.String(length=20), nullable=False), + sa.Column('origin', sa.String(length=3), nullable=False), + sa.Column('system', sa.String(length=5), nullable=False), + sa.Column('line', sa.Integer(), nullable=False), + sa.Column('quantity_stock', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('unit_of_measure', sa.String(length=5), nullable=True), + sa.Column('class_code', sa.String(length=8), nullable=True), + sa.Column('import_fraction', sa.String(length=10), nullable=True), + sa.Column('ro_fraction', sa.String(length=10), nullable=True), + sa.Column('value_me', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('tenant_id', 'company_id', 'invoice_import', 'part_number', 'origin_country', 'fraction_type', 'sector', 'octave_permit', 'origin', 'system', 'line', name='pk_octave_balance'), + schema='a76' + ) + op.create_index(op.f('ix_a76_octave_balance_company_id'), 'octave_balance', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_octave_balance_tenant_id'), 'octave_balance', ['tenant_id'], unique=False, schema='a76') + op.create_table('packages', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('key', sa.String(length=5), nullable=False), + sa.Column('description_es', sa.String(length=40), nullable=True), + sa.Column('description_en', sa.String(length=40), nullable=True), + sa.Column('weight_unit', sa.DECIMAL(precision=19, scale=8), nullable=True), + sa.Column('plurals', sa.String(length=4), nullable=True), + sa.Column('plural_in', sa.String(length=4), nullable=True), + sa.Column('code_ace', sa.String(length=4), nullable=True), + sa.Column('code_aamex', sa.String(length=9), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='packages_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'key', name='packages_key_ukey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_packages_company_id'), 'packages', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_packages_tenant_id'), 'packages', ['tenant_id'], unique=False, schema='a76') + op.create_table('packing_lists', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('item_line_id', sa.Integer(), nullable=False), + sa.Column('packing_list_number', sa.String(length=100), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_packing_lists_company_id'), 'packing_lists', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_packing_lists_tenant_id'), 'packing_lists', ['tenant_id'], unique=False, schema='a76') + op.create_table('permission_rule_oct', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('permission', sa.String(length=20), nullable=False), + sa.Column('start_date', sa.Integer(), nullable=True), + sa.Column('end_date', sa.Integer(), nullable=True), + sa.Column('sector', sa.String(length=8), nullable=True), + sa.Column('system', sa.String(length=5), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='permission_rule_oct_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'permission', name='permission_rule_oct_permission_tenant_ukey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_permission_rule_oct_company_id'), 'permission_rule_oct', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_permission_rule_oct_tenant_id'), 'permission_rule_oct', ['tenant_id'], unique=False, schema='a76') + op.create_table('permission_rule_octave', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('permission', sa.String(length=20), nullable=False), + sa.Column('start_date', sa.DateTime(), nullable=True), + sa.Column('end_date', sa.DateTime(), nullable=True), + sa.Column('sector', sa.String(length=8), nullable=True), + sa.Column('system', sa.String(length=5), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('tenant_id', 'company_id', 'permission', name='uq_permissions_rule_octave_permission'), + schema='a76' + ) + op.create_index(op.f('ix_a76_permission_rule_octave_company_id'), 'permission_rule_octave', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_permission_rule_octave_tenant_id'), 'permission_rule_octave', ['tenant_id'], unique=False, schema='a76') + op.create_table('ports', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('port_code', sa.String(length=6), nullable=False), + sa.Column('description', sa.String(length=20), nullable=True), + sa.Column('location_code', sa.String(length=4), nullable=False), + sa.Column('location_description', sa.String(length=20), nullable=True), + sa.Column('port_type', sa.String(length=15), server_default='ENTRY', nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('port_code', 'location_code', 'tenant_id', 'company_id', name='uq_port_location'), + schema='a76' + ) + op.create_index(op.f('ix_a76_ports_company_id'), 'ports', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_ports_tenant_id'), 'ports', ['tenant_id'], unique=False, schema='a76') + op.create_table('prevalidators', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('code', sa.String(length=20), nullable=False), + sa.Column('customs_prevalidator', sa.String(length=20), nullable=True), + sa.Column('patent_prevalidator', sa.String(length=20), nullable=True), + sa.Column('description', sa.String(length=50), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='prevalidators_pkey'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='prevalidators_code_unique'), + schema='a76' + ) + op.create_index(op.f('ix_a76_prevalidators_company_id'), 'prevalidators', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_prevalidators_tenant_id'), 'prevalidators', ['tenant_id'], unique=False, schema='a76') + op.create_table('previous_fractions', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('current_fraction', sa.String(length=50), nullable=True), + sa.Column('previous_fraction', sa.String(length=50), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_previous_fractions_company_id'), 'previous_fractions', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_previous_fractions_tenant_id'), 'previous_fractions', ['tenant_id'], unique=False, schema='a76') + op.create_table('seal', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('seal', sa.String(length=15), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='seal_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'seal', name='seal_ukey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_seal_company_id'), 'seal', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_seal_tenant_id'), 'seal', ['tenant_id'], unique=False, schema='a76') + op.create_table('sectors', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('key', sa.String(length=8), nullable=False), + sa.Column('description', sa.String(length=150), nullable=False), + sa.Column('authorized', sa.Boolean(), server_default='false', nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='sectors_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'key', name='sectors_key_ukey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_sectors_company_id'), 'sectors', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_sectors_tenant_id'), 'sectors', ['tenant_id'], unique=False, schema='a76') + op.create_table('signatures', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('code', sa.String(length=10), nullable=False), + sa.Column('signature', sa.String(length=1000), nullable=True), + sa.Column('photo_path', sa.String(length=1000), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='signatures_pkey'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='signatures_code_unique'), + schema='a76' + ) + op.create_index(op.f('ix_a76_signatures_company_id'), 'signatures', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_signatures_tenant_id'), 'signatures', ['tenant_id'], unique=False, schema='a76') + op.create_table('subassembly_entries', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('remission_line', sa.Integer(), nullable=False), + sa.Column('exit_invoice', sa.String(length=15), nullable=True), + sa.Column('exit_line', sa.Integer(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_subassembly_entries_company_id'), 'subassembly_entries', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_subassembly_entries_tenant_id'), 'subassembly_entries', ['tenant_id'], unique=False, schema='a76') + op.create_table('trailer', + sa.Column('trailer_number', sa.String(length=20), nullable=False), + sa.Column('ace_trailer_number', sa.String(length=10), nullable=True), + sa.Column('trailer_type_key', sa.String(length=2), nullable=True), + sa.Column('seal', sa.String(length=15), nullable=True), + sa.Column('entity_code', sa.String(length=1), nullable=True), + sa.Column('plate_number', sa.String(length=17), nullable=True), + sa.Column('state', sa.String(length=30), nullable=True), + sa.Column('country', sa.String(length=3), nullable=True), + sa.Column('container_key', sa.String(length=3), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.ForeignKeyConstraint(['trailer_type_key'], ['public.trailer_type.trailer_type_key'], ), + sa.PrimaryKeyConstraint('trailer_number'), + schema='a76' + ) + op.create_index(op.f('ix_a76_trailer_company_id'), 'trailer', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_trailer_tenant_id'), 'trailer', ['tenant_id'], unique=False, schema='a76') + op.create_table('transporter', + sa.Column('transporter_key', sa.String(length=23), nullable=False), + sa.Column('name', sa.String(length=256), nullable=True), + sa.Column('short_name', sa.String(length=10), nullable=True), + sa.Column('responsible', sa.String(length=100), nullable=True), + sa.Column('rfc', sa.String(length=30), nullable=True), + sa.Column('streets', sa.String(length=100), 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('loader_code', sa.String(length=9), nullable=True), + sa.Column('caat_code', sa.String(length=49), nullable=True), + sa.Column('transport_code', sa.String(length=8), nullable=True), + sa.Column('transport_interface_type', sa.String(length=20), nullable=True), + sa.Column('ftp_server', sa.String(length=200), nullable=True), + sa.Column('ftp_user', sa.String(length=200), nullable=True), + sa.Column('ftp_password', sa.String(length=100), nullable=True), + sa.Column('ftp_directory', sa.String(length=1000), nullable=True), + sa.Column('filler_code', sa.String(length=20), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('transporter_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_transporter_company_id'), 'transporter', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_transporter_tenant_id'), 'transporter', ['tenant_id'], unique=False, schema='a76') + op.create_table('units_of_measure', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('code', sa.String(length=5), nullable=False), + sa.Column('description', sa.String(length=100), nullable=True), + sa.Column('description_en', sa.String(length=100), nullable=True), + sa.Column('customs_code', sa.String(length=10), nullable=True), + sa.Column('american_code', sa.String(length=3), nullable=True), + sa.Column('ace_code', sa.String(length=4), nullable=True), + sa.Column('oma_code', sa.String(length=10), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['ace_code'], ['a76.unit_of_measure_ace.code'], ), + sa.ForeignKeyConstraint(['american_code'], ['a76.unit_of_measure_american.code'], ), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['customs_code'], ['a76.unit_of_measure_customs.code'], ), + sa.ForeignKeyConstraint(['oma_code'], ['a76.unit_of_measure_oma.code'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='uq_uom_code'), + schema='a76' + ) + op.create_index(op.f('ix_a76_units_of_measure_company_id'), 'units_of_measure', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_units_of_measure_tenant_id'), 'units_of_measure', ['tenant_id'], unique=False, schema='a76') + op.create_table('units_of_measure_general', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('code', sa.String(length=5), nullable=False), + sa.Column('description', sa.String(length=100), nullable=True), + sa.Column('conversion_factor', sa.Numeric(precision=13, scale=6), nullable=True), + sa.Column('mexico_unit', sa.String(length=5), nullable=True), + sa.Column('american_unit_code', sa.String(length=3), nullable=True), + sa.Column('customs_code', sa.String(length=10), nullable=True), + sa.Column('ace_code', sa.String(length=4), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['ace_code'], ['a76.unit_of_measure_ace.code'], ), + sa.ForeignKeyConstraint(['ace_code'], ['a76.unit_of_measure_ace.code'], name='fk_uom_general_ace', use_alter=True), + sa.ForeignKeyConstraint(['american_unit_code'], ['a76.unit_of_measure_american.code'], ), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['customs_code'], ['a76.unit_of_measure_customs.code'], ), + sa.ForeignKeyConstraint(['customs_code'], ['a76.unit_of_measure_customs.code'], name='fk_uom_general_customs', use_alter=True), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='uq_uom_general_code'), + schema='a76' + ) + op.create_index(op.f('ix_a76_units_of_measure_general_company_id'), 'units_of_measure_general', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_units_of_measure_general_tenant_id'), 'units_of_measure_general', ['tenant_id'], unique=False, schema='a76') + 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'), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + 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_id'), 'us_tariff_fractions', ['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') + op.create_table('value_manifestations', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('manifestation_number', sa.String(length=100), nullable=True), + sa.Column('pedimento', sa.String(length=15), nullable=True), + sa.Column('periodicity', sa.String(length=10), nullable=True), + sa.Column('semester', sa.SmallInteger(), nullable=True), + sa.Column('year', sa.String(length=4), nullable=True), + sa.Column('pedimento_type', sa.String(length=3), nullable=True), + sa.Column('aa_code', sa.String(length=5), nullable=True), + sa.Column('patent', sa.String(length=4), nullable=True), + sa.Column('first_name', sa.String(length=80), nullable=True), + sa.Column('last_name_paternal', sa.String(length=80), nullable=True), + sa.Column('last_name_maternal', sa.String(length=80), nullable=True), + sa.Column('methods_count', sa.Integer(), nullable=True), + sa.Column('merchandise_value_method', sa.String(length=10), nullable=True), + sa.Column('transaction_value', sa.SmallInteger(), nullable=True), + sa.Column('identical_merchandise_value', sa.SmallInteger(), nullable=True), + sa.Column('similar_merchandise_value', sa.SmallInteger(), nullable=True), + sa.Column('unit_sale_price_value', sa.SmallInteger(), nullable=True), + sa.Column('reconstructed_value', sa.SmallInteger(), nullable=True), + sa.Column('article_78_value', sa.SmallInteger(), nullable=True), + sa.Column('provisional_value_declaration', sa.Integer(), nullable=True), + sa.Column('has_attachments', sa.SmallInteger(), nullable=True), + sa.Column('attachment_pages_number', sa.String(length=100), nullable=True), + sa.Column('transaction_value_paid_price', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('price_pre_invoice', sa.SmallInteger(), nullable=True), + sa.Column('price_other_docs', sa.SmallInteger(), nullable=True), + sa.Column('concept_article_66', sa.SmallInteger(), nullable=True), + sa.Column('concept_article_66_breakdown', sa.SmallInteger(), nullable=True), + sa.Column('attachment_article_66', sa.String(length=2), nullable=True), + sa.Column('prepaid_merchandise_article_65', sa.String(length=2), nullable=True), + sa.Column('attachment_article_65', sa.String(length=2), nullable=True), + sa.Column('tax_base_no_sale', sa.String(length=2), nullable=True), + sa.Column('exists_circumstances_article_67_71', sa.String(length=2), nullable=True), + sa.Column('customs_value_attachment', sa.String(length=2), nullable=True), + sa.Column('provisional_value_determination', sa.String(length=2), nullable=True), + sa.Column('merchandise_value_proof_attachment', sa.String(length=2), nullable=True), + sa.Column('legal_rep_rfc', sa.String(length=30), nullable=True), + sa.Column('legal_representative', sa.String(length=100), nullable=True), + sa.Column('date', sa.Integer(), nullable=True), + sa.Column('selected_invoice', sa.String(length=20), nullable=True), + sa.Column('invoice_option', sa.String(length=3), nullable=True), + sa.Column('importer_to_use', sa.String(length=8), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index('idx_value_manifestations_manifestation_number', 'value_manifestations', ['manifestation_number'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_value_manifestations_company_id'), 'value_manifestations', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_value_manifestations_tenant_id'), 'value_manifestations', ['tenant_id'], unique=False, schema='a76') + op.create_table('vehicle', + sa.Column('vehicle_key', sa.String(length=14), nullable=False), + sa.Column('ace_vehicle_key', sa.String(length=10), nullable=True), + sa.Column('transporter_key', sa.String(length=23), nullable=True), + sa.Column('transport_identifier', sa.String(length=30), nullable=True), + sa.Column('transport_type', sa.String(length=2), nullable=True), + sa.Column('entity_code', sa.String(length=1), nullable=True), + sa.Column('transponder_number', sa.String(length=16), nullable=True), + sa.Column('dot_number', sa.String(length=8), nullable=True), + sa.Column('plate_number', sa.String(length=17), 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('seal', sa.String(length=49), nullable=True), + sa.Column('insurance_company_name', sa.String(length=30), nullable=True), + sa.Column('insurance_number', sa.String(length=20), nullable=True), + sa.Column('insurance_amount', sa.DECIMAL(precision=13, scale=2), nullable=True), + sa.Column('insurance_date', sa.Integer(), nullable=True), + sa.Column('box_number', sa.String(length=300), nullable=True), + sa.Column('brand', sa.String(length=20), nullable=True), + sa.Column('year', sa.String(length=4), nullable=True), + sa.Column('series', sa.String(length=30), nullable=True), + sa.Column('description', sa.String(length=100), nullable=True), + sa.Column('engine_number', sa.String(length=50), nullable=True), + sa.Column('sct_permission', sa.String(length=40), nullable=True), + sa.Column('color', sa.String(length=20), nullable=True), + sa.Column('container_key', sa.String(length=3), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('vehicle_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_vehicle_company_id'), 'vehicle', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_vehicle_tenant_id'), 'vehicle', ['tenant_id'], unique=False, schema='a76') + op.create_table('company_roles', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=100), nullable=False), + sa.Column('code', sa.String(length=100), nullable=False), + sa.Column('description', sa.String(length=255), nullable=True), + sa.Column('is_active', sa.Boolean(), server_default='true', nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('company_id', 'tenant_id', 'code', name='uq_company_role_code'), + schema='core' + ) + op.create_index('ix_company_roles_company_id_is_active', 'company_roles', ['company_id', 'tenant_id', 'is_active'], unique=False, schema='core') + op.create_index(op.f('ix_core_company_roles_company_id'), 'company_roles', ['company_id'], unique=False, schema='core') + op.create_index(op.f('ix_core_company_roles_id'), 'company_roles', ['id'], unique=False, schema='core') + op.create_index(op.f('ix_core_company_roles_tenant_id'), 'company_roles', ['tenant_id'], unique=False, schema='core') + op.create_table('user_company_permissions', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('user_id', sa.String(length=100), nullable=False), + sa.Column('permission_id', sa.Integer(), nullable=False), + sa.Column('is_granted', sa.Boolean(), server_default='true', nullable=False), + sa.Column('is_active', sa.Boolean(), server_default='true', nullable=False), + sa.Column('assigned_by', sa.String(length=100), nullable=True), + sa.Column('expires_at', sa.DateTime(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['permission_id'], ['core.permissions.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('user_id', 'company_id', 'tenant_id', 'permission_id', name='uq_user_company_permission'), + schema='core' + ) + op.create_index(op.f('ix_core_user_company_permissions_company_id'), 'user_company_permissions', ['company_id'], unique=False, schema='core') + op.create_index(op.f('ix_core_user_company_permissions_id'), 'user_company_permissions', ['id'], unique=False, schema='core') + op.create_index(op.f('ix_core_user_company_permissions_permission_id'), 'user_company_permissions', ['permission_id'], unique=False, schema='core') + op.create_index(op.f('ix_core_user_company_permissions_tenant_id'), 'user_company_permissions', ['tenant_id'], unique=False, schema='core') + op.create_index(op.f('ix_core_user_company_permissions_user_id'), 'user_company_permissions', ['user_id'], unique=False, schema='core') + op.create_index('ix_user_company_permissions_composite', 'user_company_permissions', ['user_id', 'company_id', 'tenant_id', 'is_active'], unique=False, schema='core') + op.create_table('user_tenants', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('keycloak_user_id', sa.String(length=255), nullable=False), + sa.Column('is_active', sa.Boolean(), server_default='true', nullable=False), + sa.Column('role', sa.String(length=50), nullable=True), + sa.Column('avatar_url', sa.String(length=500), nullable=True, comment='URL de la imagen de perfil'), + sa.Column('phone', sa.String(length=20), nullable=True, comment='Teléfono del usuario'), + sa.Column('bio', sa.Text(), nullable=True, comment='Biografía del usuario'), + sa.Column('preferences', sa.JSON(), nullable=True, comment='Preferencias del usuario (tema, idioma, etc.)'), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('keycloak_user_id', 'tenant_id', 'company_id', name='uq_user_tenant'), + schema='core' + ) + op.create_index(op.f('ix_core_user_tenants_company_id'), 'user_tenants', ['company_id'], unique=False, schema='core') + op.create_index(op.f('ix_core_user_tenants_id'), 'user_tenants', ['id'], unique=False, schema='core') + op.create_index(op.f('ix_core_user_tenants_keycloak_user_id'), 'user_tenants', ['keycloak_user_id'], unique=False, schema='core') + op.create_index(op.f('ix_core_user_tenants_tenant_id'), 'user_tenants', ['tenant_id'], unique=False, schema='core') + op.create_table('warning_fractions', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('fraction', sa.String(length=15), nullable=False), + sa.Column('description', sa.String(length=1000), nullable=True), + sa.Column('warning_type', sa.String(length=50), nullable=True), + sa.Column('is_active', sa.Boolean(), server_default='true', nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='warning_fractions_pkey'), + sa.UniqueConstraint('fraction', 'company_id', name='uq_warning_fractions_fraction_company'), + schema='public' + ) + op.create_index(op.f('ix_public_warning_fractions_company_id'), 'warning_fractions', ['company_id'], unique=False, schema='public') + op.create_index(op.f('ix_public_warning_fractions_fraction'), 'warning_fractions', ['fraction'], unique=False, schema='public') + op.create_index(op.f('ix_public_warning_fractions_tenant_id'), 'warning_fractions', ['tenant_id'], unique=False, schema='public') + op.create_index(op.f('ix_public_warning_fractions_warning_type'), 'warning_fractions', ['warning_type'], unique=False, schema='public') + op.create_table('discharge_header', + sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column('source_invoice_id', sa.BigInteger(), nullable=False, comment='Export, SM-out or CTM-send invoice that owns this discharge.'), + sa.Column('def_import_invoice_id', sa.BigInteger(), nullable=True, comment='Populated only for discharge_type=DEFINITIVE.'), + sa.Column('discharge_type', sa.String(length=15), nullable=False), + sa.Column('status', sa.String(length=15), server_default=sa.text("'applied'"), nullable=False), + sa.Column('discharge_date', sa.Date(), nullable=False), + sa.Column('reference_invoice', sa.String(length=19), nullable=True, comment='FACREFERENCIA — for rectifications'), + sa.Column('discharge_subtype', sa.String(length=10), nullable=True, comment='TIPODESC: NORMAL, PARCIAL, REPARACION, UTILERIA'), + sa.Column('partial_sequence', sa.Integer(), nullable=True, comment='CONSECPARCIAL — for partial discharges'), + sa.Column('sales_order', sa.String(length=20), nullable=True), + sa.Column('ctm_section', sa.String(length=3), nullable=True), + sa.Column('is_tooling', sa.Boolean(), server_default=sa.text('false'), nullable=False, comment='PORUTILERIA'), + sa.Column('discharge_sm', sa.String(length=4), nullable=True), + sa.Column('is_repair_update', sa.Boolean(), server_default=sa.text('false'), nullable=False, comment='ACTUALREPARACION'), + sa.Column('material_type_expo', sa.String(length=10), nullable=True, comment='TIPOMATEXPO'), + sa.Column('cancelled_by', sa.String(length=20), nullable=True), + sa.Column('cancellation_reason', sa.String(length=300), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['def_import_invoice_id'], ['a76.invoice_header.id'], ), + sa.ForeignKeyConstraint(['source_invoice_id'], ['a76.invoice_header.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a24' + ) + op.create_index(op.f('ix_a24_discharge_header_company_id'), 'discharge_header', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_discharge_header_tenant_id'), 'discharge_header', ['tenant_id'], unique=False, schema='a24') + op.create_index('ix_dischdr_date', 'discharge_header', ['tenant_id', 'discharge_date', 'discharge_type'], unique=False, schema='a24') + op.create_index('ix_dischdr_source', 'discharge_header', ['source_invoice_id', 'status'], unique=False, schema='a24') + op.create_table('classes', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('class_code', sa.String(length=8), nullable=False), + sa.Column('description_es', sa.String(length=500), nullable=True), + sa.Column('description_en', 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=20), 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.Column('is_active', sa.Boolean(), server_default='true', nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['material_key'], ['public.material_types.key'], name='fk_classes_material_type'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.ForeignKeyConstraint(['unit_of_measure', 'tenant_id', 'company_id'], ['a76.units_of_measure.code', 'a76.units_of_measure.tenant_id', 'a76.units_of_measure.company_id'], ), + sa.PrimaryKeyConstraint('id', name='classes_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'class_code', name='uq_classes_tenant_company_code'), + schema='a76' + ) + op.create_index(op.f('ix_a76_classes_company_id'), 'classes', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_classes_tenant_id'), 'classes', ['tenant_id'], unique=False, schema='a76') + op.create_table('clients_and_providers_address', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('client_id', sa.Integer(), 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.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['client_id'], ['a76.clients_and_providers.id'], name='fk_clients_and_providers_address_client', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['client_id'], ['a76.clients_and_providers.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='clients_and_providers_address_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_clients_and_providers_address_company_id'), 'clients_and_providers_address', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_clients_and_providers_address_tenant_id'), 'clients_and_providers_address', ['tenant_id'], unique=False, schema='a76') + op.create_table('clients_and_providers_programs', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('client_id', sa.Integer(), 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.String(length=8), 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('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.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['client_id'], ['a76.clients_and_providers.id'], name='fk_clients_and_providers_programs_client', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['client_id'], ['a76.clients_and_providers.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='clients_and_providers_programs_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_clients_and_providers_programs_company_id'), 'clients_and_providers_programs', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_clients_and_providers_programs_tenant_id'), 'clients_and_providers_programs', ['tenant_id'], unique=False, schema='a76') + op.create_table('concept_manifestations', + sa.Column('value_manifestation_id', sa.Integer(), nullable=False), + sa.Column('line_number', sa.Integer(), nullable=False), + sa.Column('attachment_type', sa.String(length=10), nullable=True), + sa.Column('number', sa.String(length=10), nullable=True), + sa.Column('merchandise_provider', sa.String(length=100), nullable=True), + sa.Column('invoice_document', sa.String(length=200), nullable=True), + sa.Column('amount', sa.Numeric(precision=19, scale=9), nullable=True), + sa.Column('currency', sa.String(length=3), nullable=True), + sa.Column('concept_load', sa.String(length=200), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.ForeignKeyConstraint(['value_manifestation_id'], ['a76.value_manifestations.id'], name='fk_concept_manifestation_value_manifestation'), + sa.PrimaryKeyConstraint('value_manifestation_id', 'line_number', name='concept_manifestations_pkey'), + schema='a76' + ) + op.create_index('idx_concept_manifestations_value_manifestation_id', 'concept_manifestations', ['value_manifestation_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_concept_manifestations_company_id'), 'concept_manifestations', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_concept_manifestations_tenant_id'), 'concept_manifestations', ['tenant_id'], unique=False, schema='a76') + op.create_table('concepts', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('code', sa.String(length=15), nullable=False), + sa.Column('description', sa.String(length=120), nullable=True), + sa.Column('description_en', sa.String(length=120), nullable=True), + sa.Column('detailed_description', sa.String(length=1000), nullable=True), + sa.Column('priority', sa.Integer(), nullable=True), + sa.Column('priority_ame', sa.Integer(), nullable=True), + sa.Column('first_total', sa.Boolean(), nullable=True), + sa.Column('type', sa.String(length=9), nullable=True), + sa.Column('is_printed', sa.Boolean(), nullable=True), + sa.Column('section', sa.Integer(), nullable=True), + sa.Column('classification', sa.String(length=30), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['classification'], ['a76.classification_concepts.classification'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', name='uq_concept_code'), + schema='a76' + ) + op.create_index(op.f('ix_a76_concepts_tenant_id'), 'concepts', ['tenant_id'], unique=False, schema='a76') + op.create_table('country_rule_oct', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('permission', sa.String(length=20), nullable=False), + sa.Column('line', sa.Integer(), nullable=False), + sa.Column('fraction', sa.String(length=10), nullable=False), + sa.Column('country_code', sa.String(length=3), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id', 'company_id', 'permission', 'line', 'fraction'], ['a76.fraction_rule_octave.tenant_id', 'a76.fraction_rule_octave.company_id', 'a76.fraction_rule_octave.permission', 'a76.fraction_rule_octave.line', 'a76.fraction_rule_octave.fraction'], name='fk_country_rule_oct_frac_octava', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='country_rule_oct_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'permission', 'line', 'fraction', 'country_code', name='uq_country_rule_oct_permission_line_fraction_country'), + schema='a76' + ) + op.create_index(op.f('ix_a76_country_rule_oct_company_id'), 'country_rule_oct', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_country_rule_oct_tenant_id'), 'country_rule_oct', ['tenant_id'], unique=False, schema='a76') + op.create_table('customs_brokers_personnel', + sa.Column('customs_broker_id', sa.Integer(), nullable=False), + sa.Column('line', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=80), nullable=True), + sa.Column('tax_id', sa.String(length=30), nullable=True), + sa.Column('personal_id', sa.String(length=20), nullable=True), + sa.Column('position', sa.String(length=30), nullable=True), + sa.Column('license', sa.String(length=4), nullable=True), + sa.Column('first_name', sa.String(length=80), nullable=True), + sa.Column('last_name', sa.String(length=80), nullable=True), + sa.Column('middle_name', sa.String(length=80), nullable=True), + sa.Column('email', sa.String(length=100), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['customs_broker_id'], ['a76.customs_brokers.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('customs_broker_id', 'line'), + schema='a76' + ) + op.create_index(op.f('ix_a76_customs_brokers_personnel_company_id'), 'customs_brokers_personnel', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_customs_brokers_personnel_tenant_id'), 'customs_brokers_personnel', ['tenant_id'], unique=False, schema='a76') + op.create_table('customs_brokers_vu', + sa.Column('customs_broker_id', sa.Integer(), nullable=False), + sa.Column('certificate_path', sa.String(length=1499), nullable=True), + sa.Column('key_path', sa.String(length=1499), nullable=True), + sa.Column('access_key', sa.String(length=50), nullable=True), + sa.Column('fiel_format', sa.String(length=19), nullable=True), + sa.Column('signature_read_path', sa.String(length=1499), nullable=True), + sa.Column('archive_path', sa.String(length=1499), nullable=True), + sa.Column('fiel_access_key', sa.String(length=50), nullable=True), + sa.Column('web_service_user', sa.String(length=100), nullable=True), + sa.Column('web_service_access_key', sa.String(length=100), nullable=True), + sa.Column('vu_email', sa.String(length=800), nullable=True), + sa.Column('vu_figure_type', sa.String(length=29), nullable=True), + sa.Column('xml_files_path', sa.String(length=1499), nullable=True), + sa.Column('query_tax_id', sa.String(length=30), nullable=True), + sa.Column('doda_certificate_path', sa.String(length=1499), nullable=True), + sa.Column('doda_key_path', sa.String(length=1499), nullable=True), + sa.Column('doda_web_service_user', sa.String(length=100), nullable=True), + sa.Column('doda_web_service_access_key', sa.String(length=100), nullable=True), + sa.Column('doda_fiel_access_key', sa.String(length=50), nullable=True), + sa.Column('doda_xml_files_path', sa.String(length=1499), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['customs_broker_id'], ['a76.customs_brokers.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('customs_broker_id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_customs_brokers_vu_company_id'), 'customs_brokers_vu', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_customs_brokers_vu_tenant_id'), 'customs_brokers_vu', ['tenant_id'], unique=False, schema='a76') + op.create_table('doda_american_pedimentos', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('doda_id', sa.Integer(), nullable=False), + sa.Column('american_pedimento_line', sa.Integer(), nullable=False), + sa.Column('american_pedimento_type', sa.String(length=2), nullable=True), + sa.Column('american_pedimento_value', sa.String(length=20), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['doda_id'], ['a76.doda.id'], name='fk_doda_american_pedimentos_doda'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='doda_american_pedimentos_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_doda_american_pedimentos_company_id'), 'doda_american_pedimentos', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_doda_american_pedimentos_tenant_id'), 'doda_american_pedimentos', ['tenant_id'], unique=False, schema='a76') + op.create_table('doda_containers', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('doda_id', sa.Integer(), nullable=False), + sa.Column('container_line', sa.Integer(), nullable=False), + sa.Column('container_value', sa.String(length=20), nullable=True), + sa.Column('seals', sa.String(length=254), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['doda_id'], ['a76.doda.id'], name='fk_doda_containers_doda'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='doda_containers_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_doda_containers_company_id'), 'doda_containers', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_doda_containers_tenant_id'), 'doda_containers', ['tenant_id'], unique=False, schema='a76') + op.create_table('doda_pedimentos', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('doda_id', sa.Integer(), nullable=False), + sa.Column('pedimento_line', sa.Integer(), nullable=False), + sa.Column('authorization_patent', sa.String(length=10), nullable=True), + sa.Column('document', sa.String(length=50), nullable=True), + sa.Column('shipment', sa.String(length=11), nullable=True), + sa.Column('cove', sa.String(length=50), nullable=True), + sa.Column('umc', sa.String(length=20), nullable=True), + sa.Column('effective_amount_usd', sa.Numeric(precision=15, scale=2), nullable=True), + sa.Column('difference_amount_usd', sa.Numeric(precision=15, scale=2), nullable=True), + sa.Column('dta_niu', sa.String(length=20), nullable=True), + sa.Column('article_7', sa.Boolean(), nullable=True), + sa.Column('pedimento_id', sa.Integer(), nullable=True), + sa.Column('invoice_line', sa.Integer(), nullable=True), + sa.Column('part_ii_line', sa.Integer(), nullable=True), + sa.Column('pedimento_type', sa.String(length=20), nullable=True), + sa.Column('zero_packaging_validation', sa.Boolean(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['doda_id'], ['a76.doda.id'], name='fk_doda_pedimentos_doda'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='doda_pedimentos_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_doda_pedimentos_company_id'), 'doda_pedimentos', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_doda_pedimentos_tenant_id'), 'doda_pedimentos', ['tenant_id'], unique=False, schema='a76') + op.create_table('driver', + sa.Column('transporter_key', sa.String(length=5), nullable=False), + sa.Column('line', sa.Integer(), nullable=False), + sa.Column('driver_name', sa.String(length=80), nullable=True), + sa.Column('license_number', sa.String(length=29), nullable=True), + sa.Column('express_line_id', sa.String(length=17), nullable=True), + sa.Column('ace_id', sa.String(length=20), nullable=True), + sa.Column('birth_date', sa.Integer(), nullable=True), + sa.Column('gender', sa.String(length=1), nullable=True), + sa.Column('birth_country', sa.String(length=3), nullable=True), + sa.Column('hazardous_material_auth', sa.String(length=2), nullable=True), + sa.Column('hazardous_material_state', sa.String(length=30), nullable=True), + sa.Column('first_name', sa.String(length=20), nullable=True), + sa.Column('last_name', sa.String(length=20), nullable=True), + sa.Column('id_key1', sa.String(length=40), nullable=True), + sa.Column('id_number1', sa.String(length=20), nullable=True), + sa.Column('id_state1', sa.String(length=30), nullable=True), + sa.Column('id_country1', sa.String(length=3), nullable=True), + sa.Column('id_key2', sa.String(length=40), nullable=True), + sa.Column('id_number2', sa.String(length=20), nullable=True), + sa.Column('id_state2', sa.String(length=30), nullable=True), + sa.Column('id_country2', sa.String(length=3), nullable=True), + sa.Column('badge_number', sa.String(length=20), nullable=True), + sa.Column('class_type', sa.String(length=1), nullable=True), + sa.Column('unique_badge_number', sa.String(length=100), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.ForeignKeyConstraint(['transporter_key'], ['a76.transporter.transporter_key'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('transporter_key', 'line'), + schema='a76' + ) + op.create_index(op.f('ix_a76_driver_company_id'), 'driver', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_driver_tenant_id'), 'driver', ['tenant_id'], unique=False, schema='a76') + op.create_table('equivalencies', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('identifier', sa.String(length=10), nullable=False), + sa.Column('description', sa.String(length=200), nullable=True), + sa.Column('item_id', sa.Integer(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['item_id'], ['a76.equivalency_items.id'], ondelete='SET NULL'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('identifier', 'tenant_id', 'company_id', name='uq_equivalency_identifier'), + schema='a76' + ) + op.create_index(op.f('ix_a76_equivalencies_company_id'), 'equivalencies', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_equivalencies_tenant_id'), 'equivalencies', ['tenant_id'], unique=False, schema='a76') + op.create_table('error_catalogs', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('code', sa.String(length=15), nullable=False), + sa.Column('description', sa.String(length=255), nullable=True), + sa.Column('classification_id', sa.Integer(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['classification_id'], ['a76.error_classifications.id'], name='fk_error_catalogs_classification'), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='error_catalogs_pkey'), + sa.UniqueConstraint('code'), + sa.UniqueConstraint('code', name='error_catalogs_code_unique'), + schema='a76' + ) + op.create_index(op.f('ix_a76_error_catalogs_company_id'), 'error_catalogs', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_error_catalogs_tenant_id'), 'error_catalogs', ['tenant_id'], unique=False, schema='a76') + op.create_table('fa_location_ext', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('location_id', sa.Integer(), nullable=False), + sa.Column('department', sa.String(length=100), nullable=True), + sa.Column('responsible', sa.String(length=200), nullable=True), + sa.Column('observations', sa.Text(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['location_id'], ['a76.location.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('location_id'), + sa.UniqueConstraint('location_id', name='uq_fa_location_ext_location_id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_fa_location_ext_company_id'), 'fa_location_ext', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_fa_location_ext_tenant_id'), 'fa_location_ext', ['tenant_id'], unique=False, schema='a76') + op.create_table('fda_affirmation_codes', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('fda_catalog_id', sa.Integer(), nullable=False), + sa.Column('line', sa.Integer(), nullable=False), + sa.Column('aoc_code', sa.String(length=50), nullable=False), + sa.Column('aoc_qual', sa.String(length=100), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['fda_catalog_id'], ['a76.fda_catalog.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='fda_affirmation_codes_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_fda_affirmation_codes_company_id'), 'fda_affirmation_codes', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_fda_affirmation_codes_fda_catalog_id'), 'fda_affirmation_codes', ['fda_catalog_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_fda_affirmation_codes_tenant_id'), 'fda_affirmation_codes', ['tenant_id'], unique=False, schema='a76') + op.create_table('fda_constituent_elements', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('fda_catalog_id', sa.Integer(), nullable=False), + sa.Column('line', sa.Integer(), nullable=False), + sa.Column('ele_name', sa.String(length=200), nullable=False), + sa.Column('ele_qty', sa.Numeric(precision=15, scale=2), nullable=True), + sa.Column('ele_qty_uom', sa.String(length=20), nullable=True), + sa.Column('ele_pctg', sa.Numeric(precision=15, scale=2), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['fda_catalog_id'], ['a76.fda_catalog.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='fda_constituent_elements_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_fda_constituent_elements_company_id'), 'fda_constituent_elements', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_fda_constituent_elements_fda_catalog_id'), 'fda_constituent_elements', ['fda_catalog_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_fda_constituent_elements_tenant_id'), 'fda_constituent_elements', ['tenant_id'], unique=False, schema='a76') + op.create_table('fda_lot_production', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('fda_catalog_id', sa.Integer(), nullable=False), + sa.Column('line', sa.Integer(), nullable=False), + sa.Column('lot_number', sa.String(length=100), nullable=False), + sa.Column('production_start_date', sa.String(length=50), nullable=True), + sa.Column('production_end_date', sa.String(length=50), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['fda_catalog_id'], ['a76.fda_catalog.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='fda_lot_production_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_fda_lot_production_company_id'), 'fda_lot_production', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_fda_lot_production_fda_catalog_id'), 'fda_lot_production', ['fda_catalog_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_fda_lot_production_tenant_id'), 'fda_lot_production', ['tenant_id'], unique=False, schema='a76') + op.create_table('fda_specifications', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('fda_catalog_id', sa.Integer(), nullable=False), + sa.Column('prod_code', sa.String(length=50), nullable=True), + sa.Column('commodity_desc', sa.String(length=200), nullable=True), + sa.Column('brand_name', sa.String(length=100), nullable=True), + sa.Column('disclaimer', sa.String(length=100), nullable=True), + sa.Column('pgm_code', sa.String(length=50), nullable=True), + sa.Column('proc_code', sa.String(length=50), nullable=True), + sa.Column('intnd_use_code', sa.String(length=50), nullable=True), + sa.Column('intnd_use_desc', sa.String(length=200), nullable=True), + sa.Column('temp_qual', sa.String(length=50), nullable=True), + sa.Column('temp_type', sa.String(length=50), nullable=True), + sa.Column('temp_degrees', sa.Numeric(precision=10, scale=2), nullable=True), + sa.Column('temp_negative', sa.Numeric(precision=10, scale=2), nullable=True), + sa.Column('temp_location', sa.String(length=100), nullable=True), + sa.Column('quantity_1', sa.Numeric(precision=15, scale=2), nullable=True), + sa.Column('qty_uom_1', sa.String(length=20), nullable=True), + sa.Column('quantity_2', sa.Numeric(precision=15, scale=2), nullable=True), + sa.Column('qty_uom_2', sa.String(length=20), nullable=True), + sa.Column('quantity_3', sa.Numeric(precision=15, scale=2), nullable=True), + sa.Column('qty_uom_3', sa.String(length=20), nullable=True), + sa.Column('ctry_prod', sa.String(length=50), nullable=True), + sa.Column('ctry_source', sa.String(length=50), nullable=True), + sa.Column('ctry_growth', sa.String(length=50), nullable=True), + sa.Column('ctry_refusal', sa.String(length=50), nullable=True), + sa.Column('ctry_shipping', sa.String(length=50), nullable=True), + sa.Column('manuf_key', sa.String(length=50), nullable=True), + sa.Column('shipper_key', sa.String(length=50), nullable=True), + sa.Column('ult_cons_key', sa.String(length=50), nullable=True), + sa.Column('fda_imp_key', sa.String(length=50), nullable=True), + sa.Column('pn_subm_key', sa.String(length=50), nullable=True), + sa.Column('consol_key', sa.String(length=50), nullable=True), + sa.Column('producer_key', sa.String(length=50), nullable=True), + sa.Column('owner_key', sa.String(length=50), nullable=True), + sa.Column('deli_party_key', sa.String(length=50), nullable=True), + sa.Column('grower_key', sa.String(length=50), nullable=True), + sa.Column('dev_ini_imp_key', sa.String(length=50), nullable=True), + sa.Column('lacf_cont_1', sa.String(length=100), nullable=True), + sa.Column('lacf_cont_2', sa.String(length=100), nullable=True), + sa.Column('lacf_cont_3', sa.String(length=100), nullable=True), + sa.Column('pn_transmitter_key', sa.String(length=50), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['fda_catalog_id'], ['a76.fda_catalog.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='fda_specifications_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_fda_specifications_company_id'), 'fda_specifications', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_fda_specifications_fda_catalog_id'), 'fda_specifications', ['fda_catalog_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_fda_specifications_tenant_id'), 'fda_specifications', ['tenant_id'], unique=False, schema='a76') + op.create_table('invoice_collections', + sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column('invoice_id', sa.BigInteger(), nullable=False), + sa.Column('line_number', sa.Integer(), nullable=False), + sa.Column('invoice_number', sa.String(length=15), nullable=True), + sa.Column('concept', sa.String(length=100), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['invoice_id'], ['a76.invoice_header.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_invoice_collections_company_id'), 'invoice_collections', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_invoice_collections_tenant_id'), 'invoice_collections', ['tenant_id'], unique=False, schema='a76') + op.create_table('invoice_financials', + sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column('invoice_id', sa.BigInteger(), nullable=False), + sa.Column('currency', sa.String(length=7), nullable=False), + sa.Column('currency_type', sa.String(length=3), nullable=True), + sa.Column('exchange_rate', sa.Numeric(precision=13, scale=6), nullable=True), + sa.Column('exchange_rate_mm', sa.Numeric(precision=13, scale=6), nullable=True), + sa.Column('value_mn', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('value_me', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('value_mc', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('customs_value_mn', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('customs_value_me', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('raw_material_value_mn', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('raw_material_value_me', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('aggregate_value_mn', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('aggregate_value_me', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('aggregate_value_mc', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('mexican_value_mn', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('mexican_value_me', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('mexican_value_mc', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('national_packaging_mn', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('national_packaging_me', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('national_packaging_mc', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('freight', sa.Numeric(precision=19, scale=8), server_default='0', nullable=True), + sa.Column('insurance', sa.Numeric(precision=19, scale=8), server_default='0', nullable=True), + sa.Column('insurance_value', sa.Numeric(precision=19, scale=8), server_default='0', nullable=True), + sa.Column('packaging', sa.Numeric(precision=19, scale=8), server_default='0', nullable=True), + sa.Column('other_increments', sa.Numeric(precision=19, scale=8), server_default='0', nullable=True), + sa.Column('other_deductibles', sa.Numeric(precision=19, scale=8), server_default='0', nullable=True), + sa.Column('total_increments_mn', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('total_increments_me', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('iva_mn', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('iva_me', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('iva_mc', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('iva_factor', sa.String(length=10), nullable=True), + sa.Column('tax_value_me', sa.Numeric(precision=23, scale=8), server_default='0', nullable=True), + sa.Column('seal_value_2500', sa.Boolean(), nullable=True), + sa.Column('total_quantity', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('total_packages', sa.Integer(), nullable=True), + sa.Column('gross_weight', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('net_weight', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('bundle_count', sa.Integer(), nullable=True), + sa.Column('weight_factor', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['currency_type'], ['public.currency_types.code'], ), + sa.ForeignKeyConstraint(['invoice_id'], ['a76.invoice_header.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_invoice_financials_company_id'), 'invoice_financials', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_invoice_financials_tenant_id'), 'invoice_financials', ['tenant_id'], unique=False, schema='a76') + op.create_table('invoice_logistics', + sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column('invoice_id', sa.BigInteger(), nullable=False), + sa.Column('carrier_id', sa.String(length=10), nullable=True), + sa.Column('transport_id', sa.String(length=10), nullable=True), + sa.Column('transport_us_id', sa.String(length=10), nullable=True), + sa.Column('transport_type', sa.String(length=15), server_default='none', nullable=False), + sa.Column('transport_num', sa.String(length=20), nullable=True), + sa.Column('transport_mode', sa.String(length=15), nullable=True), + sa.Column('driver_name', sa.String(length=80), nullable=True), + sa.Column('is_rail', sa.Boolean(), server_default='false', nullable=True), + sa.Column('rail_id', sa.String(length=31), nullable=True), + sa.Column('vehicle_num', sa.String(length=20), nullable=True), + sa.Column('license_plate', sa.String(length=20), nullable=True), + sa.Column('license_plate_complete', sa.String(length=40), nullable=True), + sa.Column('trailer_num', sa.String(length=20), nullable=True), + sa.Column('seal_number', sa.String(length=15), nullable=True), + sa.Column('guide_number', sa.String(length=20), nullable=True), + sa.Column('bill_number', sa.String(length=15), nullable=True), + sa.Column('reference_number', sa.String(length=14), nullable=True), + sa.Column('shipment_number', sa.String(length=19), nullable=True), + sa.Column('incoterm', sa.String(length=5), nullable=True), + sa.Column('identifier_1', sa.String(length=2), nullable=True), + sa.Column('complement_1', sa.String(length=30), nullable=True), + sa.Column('identifier_2', sa.String(length=2), nullable=True), + sa.Column('complement_2', sa.String(length=30), nullable=True), + sa.Column('weight_type', sa.String(length=3), nullable=False), + sa.Column('container_types', sa.String(length=500), nullable=True), + sa.Column('vehicle_data', sa.String(length=500), nullable=True), + sa.Column('origin_location', sa.String(length=200), nullable=True), + sa.Column('destination_location', sa.String(length=200), nullable=True), + sa.Column('transport_itinerary', sa.String(length=1000), nullable=True), + sa.Column('destination_goods', sa.String(length=50), nullable=True), + sa.Column('entry_exit_date', sa.Date(), nullable=True), + sa.Column('delivery_date', sa.Date(), nullable=True), + sa.Column('delivered_status', sa.Boolean(), server_default='false', nullable=True), + sa.Column('received_by', sa.String(length=50), nullable=True), + sa.Column('payment_date', sa.Date(), nullable=True), + sa.Column('payment_receipt_num', sa.String(length=20), nullable=True), + sa.Column('is_ctm_process', sa.Boolean(), server_default='false', nullable=True), + sa.Column('equipment_reviewed', sa.Boolean(), nullable=True), + sa.Column('is_subdivision', sa.Boolean(), nullable=True), + sa.Column('acts_as_cd', sa.Boolean(), nullable=True), + sa.Column('pedimento_arrived', sa.Boolean(), nullable=True), + sa.Column('green_light_mx', sa.Boolean(), nullable=True), + sa.Column('green_light_us', sa.Boolean(), nullable=True), + sa.Column('red_light_mx', sa.Boolean(), nullable=True), + sa.Column('red_light_us', sa.Boolean(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['invoice_id'], ['a76.invoice_header.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_invoice_logistics_company_id'), 'invoice_logistics', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_invoice_logistics_tenant_id'), 'invoice_logistics', ['tenant_id'], unique=False, schema='a76') + op.create_table('invoice_sales_details', + sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column('invoice_id', sa.BigInteger(), nullable=False), + sa.Column('line_number', sa.Integer(), nullable=False), + sa.Column('sales_order', sa.String(length=20), nullable=True), + sa.Column('colors_description', sa.String(length=49), nullable=True), + sa.Column('square_color_code', sa.String(length=1), nullable=True), + sa.Column('line_bundles', sa.Integer(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['invoice_id'], ['a76.invoice_header.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_invoice_sales_details_company_id'), 'invoice_sales_details', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_invoice_sales_details_tenant_id'), 'invoice_sales_details', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimentos', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('year', sa.String(length=2), nullable=False), + sa.Column('customs_office', sa.String(length=3), nullable=False), + sa.Column('license', sa.String(length=4), nullable=False), + sa.Column('pedimento_number', sa.String(length=7), nullable=False), + sa.Column('client_id', sa.Integer(), nullable=True), + sa.Column('operation_type', sa.String(length=3), nullable=False), + sa.Column('pedimento_type', sa.String(length=20), nullable=False), + sa.Column('pedimento_code', sa.String(length=2), nullable=False), + sa.Column('regime', sa.String(length=3), nullable=False), + sa.Column('status', sa.String(length=30), nullable=True), + sa.Column('usd_value', sa.Numeric(precision=17, scale=6), nullable=True), + sa.Column('paid_price', sa.Numeric(precision=17, scale=6), nullable=True), + sa.Column('gross_weight', sa.Numeric(precision=19, scale=3), nullable=True), + sa.Column('exchange_rate', sa.Numeric(precision=9, scale=5), nullable=True), + sa.Column('observations', sa.Text(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['client_id'], ['a76.clients_and_providers.id'], name='fk_pedimentos_client'), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_code'], ['public.pedimento_codes.code'], name='fk_pedimentos_code'), + sa.ForeignKeyConstraint(['regime'], ['public.pedimento_regimens.code'], name='fk_pedimentos_regime'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimentos_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'year', 'customs_office', 'license', 'pedimento_number', name='pedimentos_unique_key'), + schema='a76' + ) + op.create_index('idx_pedimentos_client_id', 'pedimentos', ['client_id'], unique=False, schema='a76') + op.create_index('idx_pedimentos_created_at', 'pedimentos', ['created_at'], unique=False, schema='a76') + op.create_index('idx_pedimentos_status', 'pedimentos', ['status'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimentos_company_id'), 'pedimentos', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimentos_tenant_id'), 'pedimentos', ['tenant_id'], unique=False, schema='a76') + op.create_table('unit_conversions', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('from_unit_code', sa.String(length=5), nullable=False), + sa.Column('to_unit_code', sa.String(length=5), nullable=False), + sa.Column('conversion_factor', sa.Numeric(precision=13, scale=6), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['from_unit_code', 'tenant_id', 'company_id'], ['a76.units_of_measure.code', 'a76.units_of_measure.tenant_id', 'a76.units_of_measure.company_id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.ForeignKeyConstraint(['to_unit_code', 'tenant_id', 'company_id'], ['a76.units_of_measure.code', 'a76.units_of_measure.tenant_id', 'a76.units_of_measure.company_id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('from_unit_code', 'to_unit_code', 'tenant_id', 'company_id', name='uq_unit_conversion_pair'), + schema='a76' + ) + op.create_index(op.f('ix_a76_unit_conversions_company_id'), 'unit_conversions', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_unit_conversions_tenant_id'), 'unit_conversions', ['tenant_id'], unique=False, schema='a76') + op.create_table('role_permissions', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('company_role_id', sa.Integer(), nullable=False), + sa.Column('permission_id', sa.Integer(), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['company_role_id'], ['core.company_roles.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['permission_id'], ['core.permissions.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('company_role_id', 'permission_id', name='uq_role_permission'), + schema='core' + ) + op.create_index(op.f('ix_core_role_permissions_company_id'), 'role_permissions', ['company_id'], unique=False, schema='core') + op.create_index(op.f('ix_core_role_permissions_company_role_id'), 'role_permissions', ['company_role_id'], unique=False, schema='core') + op.create_index(op.f('ix_core_role_permissions_id'), 'role_permissions', ['id'], unique=False, schema='core') + op.create_index(op.f('ix_core_role_permissions_permission_id'), 'role_permissions', ['permission_id'], unique=False, schema='core') + op.create_index(op.f('ix_core_role_permissions_tenant_id'), 'role_permissions', ['tenant_id'], unique=False, schema='core') + op.create_index('ix_role_permissions_composite', 'role_permissions', ['company_role_id', 'permission_id'], unique=False, schema='core') + op.create_table('user_company_roles', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('user_id', sa.String(length=100), nullable=False), + sa.Column('company_role_id', sa.Integer(), nullable=False), + sa.Column('is_active', sa.Boolean(), server_default='true', nullable=False), + sa.Column('assigned_by', sa.String(length=100), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['company_role_id'], ['core.company_roles.id'], ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('user_id', 'company_id', 'tenant_id', 'company_role_id', name='uq_user_company_role'), + schema='core' + ) + op.create_index(op.f('ix_core_user_company_roles_company_id'), 'user_company_roles', ['company_id'], unique=False, schema='core') + op.create_index(op.f('ix_core_user_company_roles_company_role_id'), 'user_company_roles', ['company_role_id'], unique=False, schema='core') + op.create_index(op.f('ix_core_user_company_roles_id'), 'user_company_roles', ['id'], unique=False, schema='core') + op.create_index(op.f('ix_core_user_company_roles_tenant_id'), 'user_company_roles', ['tenant_id'], unique=False, schema='core') + op.create_index(op.f('ix_core_user_company_roles_user_id'), 'user_company_roles', ['user_id'], unique=False, schema='core') + op.create_index('ix_user_company_roles_user_company', 'user_company_roles', ['user_id', 'company_id', 'tenant_id', 'is_active'], unique=False, schema='core') + op.create_table('fa_classes', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('class_id', sa.Integer(), nullable=False), + sa.Column('import_tariff_code', sa.String(length=10), nullable=True), + sa.Column('import_tariff_type', sa.String(length=6), nullable=True), + sa.Column('export_tariff_code', sa.String(length=10), nullable=True), + sa.Column('export_tariff_type', sa.String(length=6), nullable=True), + sa.Column('depreciation_rate', sa.Numeric(precision=5, scale=2), nullable=True), + sa.Column('fda_code', sa.String(length=20), nullable=True), + sa.Column('eccn_code', sa.String(length=20), nullable=True), + sa.Column('class_enabled', sa.Boolean(), server_default='true', nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['class_id'], ['a76.classes.id'], name='fk_qclasses_classes'), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='qclases_pk'), + schema='a24' + ) + op.create_index(op.f('ix_a24_fa_classes_company_id'), 'fa_classes', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_fa_classes_tenant_id'), 'fa_classes', ['tenant_id'], unique=False, schema='a24') + op.create_table('inv_classes', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('class_id', sa.Integer(), nullable=False), + sa.Column('stock_um', sa.String(length=5), nullable=False), + sa.Column('us_tariff_code', sa.String(length=19), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['class_id'], ['a76.classes.id'], name='fk_sclasses_classes'), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='sclases_pk'), + schema='a24' + ) + op.create_index(op.f('ix_a24_inv_classes_company_id'), 'inv_classes', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_inv_classes_tenant_id'), 'inv_classes', ['tenant_id'], unique=False, schema='a24') + op.create_table('doda_container_seals', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('container_id', sa.Integer(), nullable=False), + sa.Column('doda_id', sa.Integer(), nullable=False), + sa.Column('seal_line', sa.Integer(), nullable=False), + sa.Column('seal_value', sa.String(length=21), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['container_id'], ['a76.doda_containers.id'], name='fk_doda_container_seals_container'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='doda_container_seals_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_doda_container_seals_company_id'), 'doda_container_seals', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_doda_container_seals_tenant_id'), 'doda_container_seals', ['tenant_id'], unique=False, schema='a76') + op.create_table('invoice_compliance_mx', + sa.Column('invoice_id', sa.BigInteger(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=True), + sa.Column('pedimento_r1', sa.Integer(), nullable=True), + sa.Column('pedimento_k1', sa.Integer(), nullable=True), + sa.Column('remesa', sa.Integer(), nullable=True), + sa.Column('aduana', sa.String(length=3), nullable=True), + sa.Column('port_of_entry', sa.String(length=6), nullable=True), + sa.Column('destination', sa.String(length=3), nullable=True), + sa.Column('manifest_number', sa.String(length=15), nullable=True), + sa.Column('provider_header', sa.String(length=20), nullable=True), + sa.Column('provider_id', sa.Integer(), nullable=True), + sa.Column('sold_to_header', sa.String(length=20), nullable=True), + sa.Column('sold_to_id', sa.Integer(), nullable=True), + sa.Column('shipped_to_header', sa.String(length=20), nullable=True), + sa.Column('shipped_to_id', sa.Integer(), nullable=True), + sa.Column('shipped_by_header', sa.String(length=20), nullable=True), + sa.Column('shipped_by_id', sa.Integer(), nullable=True), + sa.Column('customs_broker_id', sa.Integer(), nullable=True), + sa.Column('customs_broker_us_id', sa.Integer(), nullable=True), + sa.Column('broker_invoice_num', sa.String(length=20), nullable=True), + sa.Column('broker_invoice_date', sa.Date(), nullable=True), + sa.Column('is_mixed', sa.Boolean(), nullable=True), + sa.Column('waste_type', sa.String(length=1), nullable=True), + sa.Column('scrap_type', sa.String(length=1), nullable=True), + sa.Column('appendix_17', sa.Integer(), nullable=True), + sa.Column('is_regime_change', sa.Boolean(), server_default='false', nullable=True), + sa.Column('which_exchange_rate', sa.String(length=5), nullable=True), + sa.Column('value_method', sa.String(length=2), nullable=True), + sa.Column('act_value', sa.String(length=5), nullable=True), + sa.Column('rule_3121_parties_ii', sa.Boolean(), server_default='false', nullable=True), + sa.Column('is_pedimento_pending', sa.Boolean(), server_default='false', nullable=True), + sa.Column('is_owner_of_goods', sa.Boolean(), server_default='false', nullable=True), + sa.Column('generate_balances', sa.Boolean(), server_default='false', nullable=True), + sa.Column('was_reviewed_by_company', sa.Boolean(), nullable=True), + sa.Column('edocument', sa.String(length=50), nullable=True), + sa.Column('electronic_signature', sa.String(length=999), nullable=True), + sa.Column('certificate_number', sa.String(length=99), nullable=True), + sa.Column('niu_number', sa.String(length=19), nullable=True), + sa.Column('bill_of_lading_count', sa.String(length=12), nullable=True), + sa.Column('addendum_vu', sa.String(length=204), nullable=True), + sa.Column('origin_destination_cove', sa.String(length=20), nullable=True), + sa.Column('vucem_operation_num', sa.String(length=19), nullable=True), + sa.Column('customs_person_line', sa.Integer(), nullable=True), + sa.Column('contingency_mode', sa.Boolean(), nullable=True), + sa.Column('enclosure', sa.String(length=4), nullable=True), + sa.Column('guide_type_to_identify', sa.String(length=1), nullable=True), + sa.Column('location', sa.String(length=200), nullable=True), + sa.Column('dot_code', sa.String(length=20), nullable=True), + sa.Column('subdivision', sa.String(length=20), nullable=True), + sa.Column('acts_as', sa.String(length=20), nullable=True), + sa.Column('movement_type', sa.String(length=31), nullable=True), + sa.Column('office_document', sa.String(length=30), nullable=True), + sa.Column('reason_export', sa.String(length=1), nullable=True), + sa.Column('signature_key', sa.String(length=100), nullable=True), + sa.Column('sem_id', sa.Integer(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['aduana'], ['public.customs_sections.customs_code'], ), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['customs_broker_id'], ['a76.customs_brokers.id'], ), + sa.ForeignKeyConstraint(['customs_broker_us_id'], ['a76.customs_brokers.id'], ), + sa.ForeignKeyConstraint(['invoice_id'], ['a76.invoice_header.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], ), + sa.ForeignKeyConstraint(['pedimento_k1'], ['a76.pedimentos.id'], ), + sa.ForeignKeyConstraint(['pedimento_r1'], ['a76.pedimentos.id'], ), + sa.ForeignKeyConstraint(['provider_id'], ['a76.clients_and_providers.id'], ), + sa.ForeignKeyConstraint(['shipped_by_id'], ['a76.clients_and_providers.id'], ), + sa.ForeignKeyConstraint(['shipped_to_id'], ['a76.clients_and_providers.id'], ), + sa.ForeignKeyConstraint(['sold_to_id'], ['a76.clients_and_providers.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('invoice_id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_invoice_compliance_mx_company_id'), 'invoice_compliance_mx', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_invoice_compliance_mx_tenant_id'), 'invoice_compliance_mx', ['tenant_id'], unique=False, schema='a76') + op.create_table('parts', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('client_id', sa.Integer(), nullable=True), + sa.Column('part_number', sa.String(length=70), nullable=False), + sa.Column('commercial_part_number', sa.String(length=70), 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('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('fraction', sa.String(length=10), 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('is_active', sa.Boolean(), server_default='true', nullable=True), + sa.Column('part_photo', sa.String(length=255), 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(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['currency_key'], ['public.currency_types.code'], name='fk_parts_currency'), + sa.ForeignKeyConstraint(['part_class', 'tenant_id', 'company_id'], ['a76.classes.class_code', 'a76.classes.tenant_id', 'a76.classes.company_id'], name='fk_parts_class'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.ForeignKeyConstraint(['unit_of_measure', 'tenant_id', 'company_id'], ['a76.units_of_measure.code', 'a76.units_of_measure.tenant_id', 'a76.units_of_measure.company_id'], ), + sa.PrimaryKeyConstraint('id', name='parts_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'part_number', name='client_part_ukey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_parts_company_id'), 'parts', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_parts_tenant_id'), 'parts', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_config_additional', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('add_po_identifier', sa.Boolean(), nullable=False), + sa.Column('do_not_exempt_norms_complement_x', sa.Boolean(), nullable=False), + sa.Column('manual_pedimento_year', sa.Integer(), nullable=True), + sa.Column('enable_import_invoice_recipient', sa.Boolean(), nullable=False), + sa.Column('send_502_validation_file_for_consolidated', sa.Boolean(), nullable=False), + sa.Column('add_remove_norms', sa.Boolean(), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_config_additional', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_config_additional_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_config_additional_pedimento_id_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_config_additional_company_id'), 'pedimento_config_additional', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_config_additional_tenant_id'), 'pedimento_config_additional', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_config_calculations', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('dta_type', sa.String(length=1), nullable=True), + sa.Column('dta_operation', sa.Boolean(), server_default='false', nullable=False), + sa.Column('dta_vehicle_count', sa.SmallInteger(), server_default='0', nullable=False), + sa.Column('dta_mixed_rate_8permil', sa.Boolean(), server_default='false', nullable=False), + sa.Column('pays_vat', sa.Boolean(), server_default='false', nullable=False), + sa.Column('pays_prevalidation', sa.Boolean(), server_default='false', nullable=False), + sa.Column('include_sagar_certificate_fee', sa.Boolean(), server_default='false', nullable=False), + sa.Column('fixed_vehicle_dta_fee', sa.Boolean(), server_default='false', nullable=False), + sa.Column('additional_fixed_fee', sa.SmallInteger(), server_default='0', nullable=False), + sa.Column('additional_fixed_fee_payment_method', sa.SmallInteger(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_config_calculations', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_config_calculations_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_config_calculations_pedimento_id_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_config_calculations_company_id'), 'pedimento_config_calculations', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_config_calculations_tenant_id'), 'pedimento_config_calculations', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_config_parameters', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('is_embassy', sa.Boolean(), server_default='false', nullable=False), + sa.Column('embassy_dta', sa.Numeric(precision=11, scale=2), server_default='0.00', nullable=False), + sa.Column('rule_3121_section_ii', sa.Boolean(), server_default='false', nullable=False), + sa.Column('use_previous_tariff', sa.Boolean(), server_default='false', nullable=False), + sa.Column('use_payment_date_fi', sa.Boolean(), server_default='false', nullable=False), + sa.Column('add_state_supplier_record_505', sa.Boolean(), server_default='false', nullable=False), + sa.Column('customs_value_calculation', sa.Boolean(), server_default='false', nullable=False), + sa.Column('two_decimals_unit_value', sa.Boolean(), server_default='false', nullable=False), + sa.Column('customs_value_per_item', sa.Boolean(), server_default='false', nullable=False), + sa.Column('is_national_supplier', sa.Boolean(), server_default='false', nullable=False), + sa.Column('is_consolidated', sa.Boolean(), server_default='false', nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_config_parameters', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_config_parameters_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_config_parameters_pedimento_id_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_config_parameters_company_id'), 'pedimento_config_parameters', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_config_parameters_tenant_id'), 'pedimento_config_parameters', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_config_surcharges', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('surcharge_igi', sa.Boolean(), nullable=False), + sa.Column('surcharge_dta', sa.Boolean(), nullable=False), + sa.Column('surcharge_vat', sa.Boolean(), nullable=False), + sa.Column('surcharge_isan', sa.Boolean(), nullable=False), + sa.Column('surcharge_ieps', sa.Boolean(), nullable=False), + sa.Column('surcharge_cc', sa.Boolean(), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_config_surcharges', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_config_surcharges_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_config_surcharges_pedimento_id_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_config_surcharges_company_id'), 'pedimento_config_surcharges', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_config_surcharges_tenant_id'), 'pedimento_config_surcharges', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_config_update_rectification', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('update_vat', sa.Boolean(), server_default='false', nullable=False), + sa.Column('update_advalorem', sa.Boolean(), server_default='false', nullable=False), + sa.Column('update_dta', sa.Boolean(), server_default='false', nullable=False), + sa.Column('update_cc', sa.Boolean(), server_default='false', nullable=False), + sa.Column('update_ieps', sa.Boolean(), server_default='false', nullable=False), + sa.Column('calculate_surcharge', sa.Boolean(), server_default='false', nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_config_update_rectification', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_config_update_rectification_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_config_update_rectification_pedimento_id_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_config_update_rectification_company_id'), 'pedimento_config_update_rectification', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_config_update_rectification_tenant_id'), 'pedimento_config_update_rectification', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_config_updates', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('update_vat', sa.Boolean(), server_default='false', nullable=False), + sa.Column('update_advalorem', sa.Boolean(), server_default='false', nullable=False), + sa.Column('update_dta', sa.Boolean(), server_default='false', nullable=False), + sa.Column('update_cc', sa.Boolean(), server_default='false', nullable=False), + sa.Column('update_ieps', sa.Boolean(), server_default='false', nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_config_updates', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_config_updates_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_config_updates_pedimento_id_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_config_updates_company_id'), 'pedimento_config_updates', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_config_updates_tenant_id'), 'pedimento_config_updates', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_containers', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('number', sa.String(length=100), nullable=True), + sa.Column('identification', sa.String(length=100), nullable=True), + sa.Column('type', sa.String(length=50), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_containers', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_containers_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_containers_company_id'), 'pedimento_containers', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_containers_tenant_id'), 'pedimento_containers', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_contributions', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('contribucion', sa.String(length=50), nullable=True), + sa.Column('tipo_tasa', sa.String(length=100), nullable=True), + sa.Column('tasa', sa.Numeric(precision=15, scale=8), nullable=True), + sa.Column('forma_pago', sa.String(length=50), nullable=True), + sa.Column('importe', sa.Numeric(precision=17, scale=2), nullable=True), + sa.Column('gravamen', sa.String(length=100), nullable=True), + sa.Column('abreviacion', sa.String(length=50), nullable=True), + sa.Column('forma_pago_2', sa.String(length=50), nullable=True), + sa.Column('importe_2', sa.Numeric(precision=17, scale=2), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_contributions', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_contributions_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_contributions_company_id'), 'pedimento_contributions', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_contributions_tenant_id'), 'pedimento_contributions', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_customs_offices', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('dispatch_customs', sa.String(length=3), nullable=False), + sa.Column('entry_exit_customs', sa.String(length=3), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_customs_offices', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_customs_offices_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_customs_offices_pedimento_id_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_customs_offices_company_id'), 'pedimento_customs_offices', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_customs_offices_tenant_id'), 'pedimento_customs_offices', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_dates', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('entry_date', sa.DateTime(), nullable=False), + sa.Column('pedimento_date', sa.DateTime(), nullable=True), + sa.Column('payment_date', sa.DateTime(), nullable=True), + sa.Column('rectification_payment_date', sa.DateTime(), nullable=True), + sa.Column('extraction_date', sa.DateTime(), nullable=True), + sa.Column('submission_date', sa.DateTime(), nullable=True), + sa.Column('eucan_date', sa.DateTime(), nullable=True), + sa.Column('original_date', sa.DateTime(), nullable=True), + sa.Column('start_date', sa.DateTime(), nullable=True), + sa.Column('end_date', sa.DateTime(), nullable=False), + sa.Column('capture_time', sa.Time(), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_dates', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_dates_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_dates_pedimento_id_key'), + schema='a76' + ) + op.create_index('idx_pedimento_dates_pedimento_id', 'pedimento_dates', ['pedimento_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_dates_company_id'), 'pedimento_dates', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_dates_tenant_id'), 'pedimento_dates', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_decrementables', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('freight', sa.Numeric(precision=13, scale=2), nullable=True), + sa.Column('insurance', sa.Numeric(precision=13, scale=2), nullable=True), + sa.Column('loading', sa.Numeric(precision=13, scale=2), nullable=True), + sa.Column('unloading', sa.Numeric(precision=13, scale=2), nullable=True), + sa.Column('others', sa.Numeric(precision=13, scale=2), nullable=True), + sa.Column('currency', sa.String(length=3), nullable=True), + sa.Column('currency_factor', sa.Numeric(precision=15, scale=8), nullable=True), + sa.Column('not_affect_usd_value', sa.SmallInteger(), nullable=True), + sa.Column('not_affect_customs_value', sa.SmallInteger(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_decrementables', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_decrementables_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_decrementables_pedimento_id_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_decrementables_company_id'), 'pedimento_decrementables', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_decrementables_tenant_id'), 'pedimento_decrementables', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_guides', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('guide', sa.String(length=100), nullable=True), + sa.Column('identifier', sa.String(length=100), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_guides', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_guides_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_guides_company_id'), 'pedimento_guides', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_guides_tenant_id'), 'pedimento_guides', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_incrementables', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('insured_value', sa.Numeric(precision=13, scale=2), nullable=True), + sa.Column('freight', sa.Numeric(precision=13, scale=2), nullable=True), + sa.Column('insurance', sa.Numeric(precision=13, scale=2), nullable=True), + sa.Column('packaging', sa.Numeric(precision=13, scale=2), nullable=True), + sa.Column('others', sa.Numeric(precision=13, scale=3), nullable=True), + sa.Column('deductibles', sa.Numeric(precision=13, scale=3), nullable=True), + sa.Column('currency', sa.String(length=3), nullable=True), + sa.Column('currency_factor', sa.Numeric(precision=15, scale=8), nullable=True), + sa.Column('not_affect_usd_value', sa.SmallInteger(), nullable=True), + sa.Column('not_affect_customs_value', sa.SmallInteger(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_incrementables', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_incrementables_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_incrementables_pedimento_id_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_incrementables_company_id'), 'pedimento_incrementables', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_incrementables_tenant_id'), 'pedimento_incrementables', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_indexes', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('update_factor_type', sa.SmallInteger(), nullable=True), + sa.Column('update_factor', sa.Numeric(precision=7, scale=4), nullable=True), + sa.Column('manual_update_factor', sa.SmallInteger(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_indexes', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_indexes_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_indexes_pedimento_id_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_indexes_company_id'), 'pedimento_indexes', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_indexes_tenant_id'), 'pedimento_indexes', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_packages', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('quantity', sa.Integer(), nullable=True), + sa.Column('brand', sa.String(length=100), nullable=True), + sa.Column('number', sa.String(length=100), nullable=True), + sa.Column('vehicles', sa.Integer(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_packages', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_packages_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_packages_pedimento_id_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_packages_company_id'), 'pedimento_packages', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_packages_tenant_id'), 'pedimento_packages', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_payments', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('acknowledgment', sa.String(length=20), nullable=False), + sa.Column('operation_number', sa.String(length=14), nullable=False), + sa.Column('bank_code', sa.Integer(), nullable=False), + sa.Column('cashier', sa.String(length=2), nullable=False), + sa.Column('date', sa.Date(), nullable=False), + sa.Column('time', sa.Time(), nullable=False), + sa.Column('shift', sa.String(length=1), nullable=False), + sa.Column('total_cash_paid', sa.Integer(), nullable=False), + sa.Column('total_contributions', sa.Integer(), nullable=False), + sa.Column('counter_payment', sa.SmallInteger(), nullable=False), + sa.Column('pece_code', sa.String(length=5), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_payments', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_payments_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_payments_pedimento_id_key'), + schema='a76' + ) + op.create_index('idx_pedimento_payments_pedimento_id', 'pedimento_payments', ['pedimento_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_payments_company_id'), 'pedimento_payments', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_payments_tenant_id'), 'pedimento_payments', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_rectification_destination', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('destination_pedimento_year', sa.String(length=2), nullable=False), + sa.Column('destination_customs_office', sa.String(length=3), nullable=False), + sa.Column('destination_license', sa.String(length=4), nullable=False), + sa.Column('destination_pedimento_number', sa.String(length=7), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_rectification_destination', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_rectification_destination_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_rectification_destination_pedimento_id_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_rectification_destination_company_id'), 'pedimento_rectification_destination', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_rectification_destination_tenant_id'), 'pedimento_rectification_destination', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_rectification_origin', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('original_pedimento_year', sa.String(length=2), nullable=True), + sa.Column('original_customs_office', sa.String(length=3), nullable=True), + sa.Column('original_license', sa.String(length=4), nullable=True), + sa.Column('original_pedimento_number', sa.String(length=7), nullable=True), + sa.Column('original_pedimento_code', sa.String(length=2), nullable=True), + sa.Column('original_payment_date', sa.DateTime(), nullable=True), + sa.Column('total_cash', sa.Integer(), nullable=True), + sa.Column('total_others', sa.Integer(), nullable=True), + sa.Column('reason', sa.String(length=255), nullable=True), + sa.Column('charge_to_client', sa.SmallInteger(), nullable=True), + sa.Column('use_original_payment_date_for_interest_calc', sa.SmallInteger(), nullable=True), + sa.Column('manual_calculation', sa.SmallInteger(), nullable=True), + sa.Column('original_pedimento_norms', sa.SmallInteger(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_rectification_origin', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_rectification_origin_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_rectification_origin_pedimento_id_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_rectification_origin_company_id'), 'pedimento_rectification_origin', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_rectification_origin_tenant_id'), 'pedimento_rectification_origin', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_seals', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('number', sa.String(length=100), nullable=True), + sa.Column('identification', sa.String(length=100), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_seals', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_seals_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_seals_company_id'), 'pedimento_seals', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_seals_tenant_id'), 'pedimento_seals', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_transport_carriers', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('carrier', sa.String(length=200), nullable=True), + sa.Column('rfc', sa.String(length=20), nullable=True), + sa.Column('curp', sa.String(length=20), nullable=True), + sa.Column('name', sa.String(length=200), nullable=True), + sa.Column('address', sa.String(length=300), nullable=True), + sa.Column('city', sa.String(length=100), nullable=True), + sa.Column('state', sa.String(length=100), nullable=True), + sa.Column('country', sa.String(length=3), nullable=True), + sa.Column('tax_id', sa.String(length=50), nullable=True), + sa.Column('total_packages', sa.Integer(), nullable=True), + sa.Column('identification', sa.String(length=100), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_transport_carriers', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_transport_carriers_pkey'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_transport_carriers_company_id'), 'pedimento_transport_carriers', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_transport_carriers_tenant_id'), 'pedimento_transport_carriers', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_transport_means', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('destination', sa.SmallInteger(), nullable=False), + sa.Column('entry_exit', sa.String(length=3), nullable=False), + sa.Column('arrival', sa.String(length=3), nullable=False), + sa.Column('departure', sa.String(length=3), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_transport_means', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_transport_means_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_transport_means_pedimento_id_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_transport_means_company_id'), 'pedimento_transport_means', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_transport_means_tenant_id'), 'pedimento_transport_means', ['tenant_id'], unique=False, schema='a76') + op.create_table('pedimento_validation', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pedimento_id', sa.Integer(), nullable=False), + sa.Column('validator', sa.String(length=3), nullable=False), + sa.Column('validation_ack', sa.String(length=8), nullable=False), + sa.Column('pre_ack', sa.String(length=8), nullable=False), + sa.Column('line_signature', sa.String(length=50), nullable=False), + sa.Column('electronic_signature', sa.String(length=999), nullable=False), + sa.Column('certificate_number', sa.String(length=99), nullable=False), + sa.Column('validator_id', sa.Integer(), nullable=False), + sa.Column('responsible_id', sa.Integer(), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['pedimento_id'], ['a76.pedimentos.id'], name='fk_pedimento_validation', ondelete='CASCADE'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='pedimento_validation_pkey'), + sa.UniqueConstraint('tenant_id', 'company_id', 'pedimento_id', name='pedimento_validation_pedimento_id_key'), + schema='a76' + ) + op.create_index(op.f('ix_a76_pedimento_validation_company_id'), 'pedimento_validation', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_pedimento_validation_tenant_id'), 'pedimento_validation', ['tenant_id'], unique=False, schema='a76') + op.create_table('fa_partes', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('origin_country', sa.String(length=3), nullable=True), + sa.Column('sector', sa.String(length=8), nullable=True), + sa.Column('fraction_type', sa.String(length=7), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['id'], ['a76.parts.id'], name='fk_fa_partes_master'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='fa_partes_pkey'), + schema='a24' + ) + op.create_index(op.f('ix_a24_fa_partes_company_id'), 'fa_partes', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_fa_partes_tenant_id'), 'fa_partes', ['tenant_id'], unique=False, schema='a24') + op.create_table('inv_bom', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('parent_part_id', sa.Integer(), nullable=False), + sa.Column('component_part_id', sa.Integer(), nullable=False), + sa.Column('quantity', sa.Numeric(precision=19, scale=8), nullable=False), + sa.Column('uom_code', sa.String(length=5), nullable=False), + sa.Column('procedure_type', sa.String(length=10), nullable=True), + sa.Column('is_percentage', sa.Boolean(), nullable=False), + sa.Column('raw_material', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('waste', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('merma', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['component_part_id'], ['a76.parts.id'], name='fk_inv_bom_component'), + sa.ForeignKeyConstraint(['parent_part_id'], ['a76.parts.id'], name='fk_inv_bom_parent'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='inv_bom_pkey'), + schema='a24' + ) + op.create_index(op.f('ix_a24_inv_bom_company_id'), 'inv_bom', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_inv_bom_tenant_id'), 'inv_bom', ['tenant_id'], unique=False, schema='a24') + op.create_table('inv_parte_paises', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('part_id', sa.Integer(), nullable=False), + sa.Column('country_code', sa.String(length=3), nullable=False), + sa.Column('fraction', sa.String(length=20), nullable=True), + sa.Column('is_origin', sa.Boolean(), nullable=False), + sa.Column('preference', sa.String(length=15), nullable=False), + sa.Column('has_certificate', sa.Boolean(), nullable=False), + sa.Column('certificate_number', sa.String(length=50), nullable=True), + sa.Column('end_date', sa.DateTime(), nullable=True), + sa.Column('previous_fractions_7m', sa.Boolean(), nullable=False), + sa.Column('omission_import', sa.Boolean(), nullable=False), + sa.Column('omission_export', sa.Boolean(), nullable=False), + sa.Column('import_percentage', sa.Numeric(precision=5, scale=2), nullable=True), + sa.Column('export_percentage', sa.Numeric(precision=5, scale=2), nullable=True), + sa.Column('sector', sa.String(length=10), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['part_id'], ['a76.parts.id'], name='fk_inv_parte_paises_part'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='inv_parte_paises_pkey'), + schema='a24' + ) + op.create_index(op.f('ix_a24_inv_parte_paises_company_id'), 'inv_parte_paises', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_inv_parte_paises_tenant_id'), 'inv_parte_paises', ['tenant_id'], unique=False, schema='a24') + op.create_table('inv_partes', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('part_type', sa.String(length=10), nullable=True), + sa.Column('material_type', sa.String(length=10), nullable=True), + sa.Column('reference_number', sa.String(length=70), nullable=True), + sa.Column('flex_reference_number', sa.String(length=120), nullable=True), + sa.Column('equivalent_uom', sa.String(length=5), nullable=True), + sa.Column('conversion_factor', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('stock_uom', sa.String(length=5), nullable=True), + sa.Column('alternate_uom', sa.String(length=5), nullable=True), + sa.Column('conversion_uom', sa.String(length=9), nullable=True), + sa.Column('added_value', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('added_value_type', sa.String(length=2), nullable=True), + sa.Column('assigned_client', sa.String(length=50), nullable=True), + sa.Column('supplier_code', sa.String(length=8), nullable=True), + sa.Column('is_textile', sa.String(length=2), nullable=True), + sa.Column('bom_version', sa.Integer(), nullable=True), + sa.Column('is_repair', sa.String(length=3), nullable=True), + sa.Column('is_hazardous', sa.String(length=1), nullable=True), + sa.Column('emergency_number', sa.String(length=30), nullable=True), + sa.Column('danger_class', sa.String(length=4), nullable=True), + sa.Column('packaging_group', sa.String(length=3), nullable=True), + sa.Column('width', sa.String(length=50), nullable=True), + sa.Column('thickness', sa.String(length=50), nullable=True), + sa.Column('specification', sa.String(length=50), nullable=True), + sa.Column('total_value', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('direct_labor', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('general_expenses', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('total_expenses', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('depreciation', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('tooling', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('material_consumed', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('profit', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('us_fraction_alt', sa.String(length=13), nullable=True), + sa.Column('ca_fraction', sa.String(length=13), nullable=True), + sa.Column('ad_valorem_us', sa.Numeric(precision=5, scale=2), nullable=True), + sa.Column('nafta_result', sa.String(length=19), nullable=True), + sa.Column('nafta_percentage', sa.Numeric(precision=5, scale=2), nullable=True), + sa.Column('dta', sa.String(length=19), nullable=True), + sa.Column('dtb', sa.String(length=19), nullable=True), + sa.Column('dtg', sa.String(length=19), nullable=True), + sa.Column('substitute_part', sa.String(length=70), nullable=True), + sa.Column('complementary_part', sa.String(length=70), nullable=True), + sa.Column('preference_part', sa.String(length=70), nullable=True), + sa.Column('use_alternate_quantity', sa.Boolean(), nullable=True), + sa.Column('un_number', sa.String(length=30), nullable=True), + sa.Column('shipping_name', sa.String(length=200), nullable=True), + sa.Column('hazard_notes', sa.String(length=500), nullable=True), + sa.Column('repair_unit_cost', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('repair_added_value', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('fraction_9801', sa.String(length=10), nullable=True), + sa.Column('immex_type', sa.String(length=10), nullable=True), + sa.Column('disable_movements', sa.Boolean(), nullable=True), + sa.Column('pga_program_code', sa.String(length=10), nullable=True), + sa.Column('usmca_fraction', sa.String(length=10), nullable=True), + sa.Column('scrap_part_number', sa.String(length=70), nullable=True), + sa.Column('waste_part_number', sa.String(length=70), nullable=True), + sa.Column('scrap_description_en', sa.String(length=500), nullable=True), + sa.Column('scrap_description_es', sa.String(length=500), nullable=True), + sa.Column('scrap_export_fraction', sa.String(length=10), nullable=True), + sa.Column('scrap_us_fraction', sa.String(length=10), nullable=True), + sa.Column('equivalent_uom_2', sa.String(length=5), nullable=True), + sa.Column('conversion_factor_2', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('has_auxiliary', sa.Boolean(), nullable=True), + sa.Column('auxiliary_uom', sa.String(length=5), nullable=True), + sa.Column('auxiliary_conversion', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('auxiliary_unit_cost', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('mex_packing', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('sales_order', sa.String(length=50), nullable=True), + sa.Column('use_rule_8', sa.Boolean(), nullable=True), + sa.Column('sector', sa.String(length=150), nullable=True), + sa.Column('origin_country', sa.String(length=3), nullable=True), + sa.Column('fraction_type', sa.String(length=10), nullable=True), + sa.Column('agency_code_definition', sa.String(length=50), nullable=True), + sa.Column('carta_porte', sa.String(length=100), nullable=True), + sa.Column('client_part_names', postgresql.JSONB(astext_type=sa.Text()), nullable=True), + sa.Column('part_identifiers', postgresql.JSONB(astext_type=sa.Text()), nullable=True), + sa.Column('substitute_parts', postgresql.JSONB(astext_type=sa.Text()), nullable=True), + sa.Column('aphis_data', postgresql.JSONB(astext_type=sa.Text()), nullable=True), + sa.Column('non_discharge_clients', postgresql.JSONB(astext_type=sa.Text()), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['id'], ['a76.parts.id'], name='fk_inv_partes_master'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='inv_partes_pkey'), + schema='a24' + ) + op.create_index(op.f('ix_a24_inv_partes_company_id'), 'inv_partes', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_inv_partes_tenant_id'), 'inv_partes', ['tenant_id'], unique=False, schema='a24') + op.create_table('item_lines', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('invoice_id', sa.BigInteger(), nullable=False), + sa.Column('line_number', sa.Integer(), nullable=False), + sa.Column('part_number_id', sa.Integer(), nullable=True), + sa.Column('component_part_number_id', sa.Integer(), nullable=True), + sa.Column('class_id', sa.Integer(), nullable=True), + sa.Column('unit_of_measure', sa.Integer(), nullable=True), + sa.Column('alternate_unit', sa.Integer(), nullable=True), + sa.Column('uma_key', sa.String(length=2), nullable=True), + sa.Column('auxiliary_unit', sa.String(length=5), nullable=True), + sa.Column('permit_number', sa.String(length=20), nullable=True), + sa.Column('page_line', sa.String(length=10), nullable=True), + sa.Column('has_certificate', sa.Boolean(), nullable=True), + sa.Column('certificate_number', sa.String(length=10), nullable=True), + sa.Column('octave_permit', sa.String(length=20), nullable=True), + sa.Column('permits_ped', sa.String(length=500), nullable=True), + sa.Column('has_fda_code', sa.Boolean(), nullable=True), + sa.Column('fda_key', sa.String(length=10), nullable=True), + sa.Column('is_military_mcia', sa.Boolean(), nullable=True), + sa.Column('iv32_type_key', sa.String(length=5), nullable=True), + sa.Column('iv32_number', sa.String(length=35), nullable=True), + sa.Column('scrap_invoice', sa.String(length=15), nullable=True), + sa.Column('consecutive_destination', sa.Integer(), nullable=True), + sa.Column('ctm_section', sa.String(length=3), nullable=True), + sa.Column('tax_payment', sa.Boolean(), nullable=True), + sa.Column('payment_method', sa.String(length=9), nullable=True), + sa.Column('igi_amount', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('igi_payment_method', sa.String(length=9), nullable=True), + sa.Column('fcc_key', sa.String(length=30), nullable=True), + sa.Column('valuation_method', sa.String(length=2), nullable=True), + sa.Column('valuation_determined_value', sa.Numeric(precision=29, scale=8), nullable=True), + sa.Column('valuation_reason', sa.String(length=500), nullable=True), + sa.Column('container_rule', sa.String(length=50), nullable=True), + sa.Column('container_parts_ii', sa.String(length=50), nullable=True), + sa.Column('consecutive_aphis', sa.Integer(), nullable=True), + sa.Column('bom_version', sa.Integer(), nullable=True), + sa.Column('bill_version', sa.Integer(), nullable=True), + sa.Column('tlcan_value', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('identifier', sa.String(length=2), nullable=True), + sa.Column('validation_zero', sa.Integer(), nullable=True), + sa.Column('validation_one', sa.Integer(), nullable=True), + sa.Column('material_type', sa.String(length=50), nullable=True), + sa.Column('order_type', sa.String(length=50), nullable=True), + sa.Column('line_concept', sa.String(length=50), nullable=True), + sa.Column('review_dispatch', sa.String(length=10), nullable=True), + sa.Column('take_component_pt', sa.Integer(), nullable=True), + sa.Column('pallet2', sa.SmallInteger(), nullable=True), + sa.Column('wildcard_field', sa.String(length=100), nullable=True), + sa.Column('reference_number', sa.String(length=20), nullable=True), + sa.Column('order', sa.String(length=50), nullable=True), + sa.Column('guide_number', sa.String(length=50), nullable=True), + sa.Column('depreciation_date', sa.Date(), nullable=True), + sa.Column('rectification', sa.Boolean(), nullable=True), + sa.Column('warehouse', sa.String(length=30), nullable=True), + sa.Column('location', sa.String(length=200), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['alternate_unit'], ['a76.units_of_measure.id'], ), + sa.ForeignKeyConstraint(['class_id'], ['a76.classes.id'], ), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['component_part_number_id'], ['a76.parts.id'], ), + sa.ForeignKeyConstraint(['invoice_id'], ['a76.invoice_header.id'], ), + sa.ForeignKeyConstraint(['part_number_id'], ['a76.parts.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.ForeignKeyConstraint(['unit_of_measure'], ['a76.units_of_measure.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_item_lines_company_id'), 'item_lines', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_item_lines_tenant_id'), 'item_lines', ['tenant_id'], unique=False, schema='a76') + op.create_table('balance_movement', + sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column('import_invoice_id', sa.BigInteger(), nullable=False, comment='Import invoice (cabecera de importación)'), + sa.Column('import_item_line_id', sa.Integer(), nullable=False, comment='Import line item = the PEPS lot'), + sa.Column('part_number_id', sa.Integer(), nullable=True, comment='Denormalized from item_lines.part_number_id. Enables PEPS index without joins.'), + sa.Column('movement_type', sa.String(length=20), nullable=False, comment='See MovementType enum. Determines sign and whether qty counts as used.'), + sa.Column('quantity', sa.Numeric(precision=19, scale=8), nullable=False, comment='Always positive. Sign is inferred from movement_type via NEGATIVE_MOVEMENTS.'), + sa.Column('value_me', sa.Numeric(precision=23, scale=8), nullable=True, comment='USD'), + sa.Column('value_mn', sa.Numeric(precision=23, scale=8), nullable=True, comment='MXN'), + sa.Column('net_weight', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('source_invoice_id', sa.BigInteger(), nullable=True, comment='Export / SM / CTM invoice. NULL for entries.'), + sa.Column('source_item_line_id', sa.Integer(), nullable=True, comment='Specific line in the export / SM / CTM invoice.'), + sa.Column('order_peps', sa.BigInteger(), nullable=False, comment='PEPS order within this lot. Lower = older = consumed first.'), + sa.Column('operation_date', sa.Date(), nullable=False, comment='Date of the actual business event, not DB insert.'), + sa.Column('notes', sa.String(length=300), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.CheckConstraint('quantity > 0', name='ck_balance_movement_qty_positive'), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['import_invoice_id'], ['a76.invoice_header.id'], ), + sa.ForeignKeyConstraint(['import_item_line_id'], ['a76.item_lines.id'], ), + sa.ForeignKeyConstraint(['part_number_id'], ['a76.parts.id'], ), + sa.ForeignKeyConstraint(['source_invoice_id'], ['a76.invoice_header.id'], ), + sa.ForeignKeyConstraint(['source_item_line_id'], ['a76.item_lines.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('import_item_line_id', 'order_peps', name='uq_balance_movement_lot_peps'), + schema='a24' + ) + op.create_index(op.f('ix_a24_balance_movement_company_id'), 'balance_movement', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_balance_movement_tenant_id'), 'balance_movement', ['tenant_id'], unique=False, schema='a24') + op.create_index('ix_balmov_lot', 'balance_movement', ['import_item_line_id'], unique=False, schema='a24') + op.create_index('ix_balmov_operation_date', 'balance_movement', ['tenant_id', 'operation_date', 'movement_type'], unique=False, schema='a24') + op.create_index('ix_balmov_peps_lookup', 'balance_movement', ['tenant_id', 'part_number_id', 'movement_type', 'order_peps'], unique=False, schema='a24', postgresql_include=['import_item_line_id', 'quantity', 'value_me', 'value_mn']) + op.create_index('ix_balmov_source', 'balance_movement', ['source_invoice_id', 'source_item_line_id'], unique=False, schema='a24') + op.create_table('fa_item_lines', + sa.Column('id', sa.Integer(), autoincrement=False, nullable=False), + sa.Column('asset_number', sa.String(length=25), nullable=True), + sa.Column('asset_photo', sa.String(length=255), nullable=True), + sa.Column('equipment_message', sa.String(length=40), nullable=True), + sa.Column('invoice_type_asset', sa.String(length=6), nullable=True), + sa.Column('return_import_invoice', sa.String(length=15), nullable=True), + sa.Column('return_import_date', sa.Integer(), nullable=True), + sa.Column('movement_type_import', sa.String(length=3), nullable=True), + sa.Column('search_invoice', sa.String(length=15), nullable=True), + sa.Column('search_line', sa.Integer(), nullable=True), + sa.Column('search_type', sa.String(length=10), nullable=True), + sa.Column('is_subitem', sa.Boolean(), nullable=True), + sa.Column('contains_subitems', sa.Boolean(), nullable=True), + sa.Column('subitem_number', sa.Integer(), nullable=True), + sa.Column('discharge', sa.Boolean(), nullable=True), + sa.Column('own_equipment', sa.Boolean(), nullable=True), + sa.Column('omit_annex31', sa.Boolean(), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['id'], ['a76.item_lines.id'], name='fk_fa_item_lines_master'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='fa_item_lines_pkey'), + schema='a24' + ) + op.create_index(op.f('ix_a24_fa_item_lines_company_id'), 'fa_item_lines', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_fa_item_lines_tenant_id'), 'fa_item_lines', ['tenant_id'], unique=False, schema='a24') + op.create_table('inv_aphis_general', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('inv_part_id', sa.Integer(), nullable=False), + sa.Column('program_code', sa.String(length=10), nullable=True), + sa.Column('processing_code', sa.String(length=10), nullable=True), + sa.Column('aphis_type', sa.String(length=10), nullable=True), + sa.Column('disclaimer', sa.String(length=10), nullable=True), + sa.Column('electronic_image', sa.String(length=50), nullable=True), + sa.Column('confidential', sa.String(length=1), nullable=True), + sa.Column('global_product_id', sa.String(length=100), nullable=True), + sa.Column('intended_use_code', sa.String(length=10), nullable=True), + sa.Column('intended_use_description', sa.String(length=200), nullable=True), + sa.Column('item_type', sa.String(length=20), nullable=True), + sa.Column('product_code', sa.String(length=20), nullable=True), + sa.Column('product_code_2', sa.String(length=20), nullable=True), + sa.Column('product_code_3', sa.String(length=20), nullable=True), + sa.Column('scientific_genus_name', sa.String(length=100), nullable=True), + sa.Column('scientific_species_name', sa.String(length=100), nullable=True), + sa.Column('scientific_sub_species_name', sa.String(length=100), nullable=True), + sa.Column('common_name_specific', sa.String(length=200), nullable=True), + sa.Column('common_name_general', sa.String(length=200), nullable=True), + sa.Column('signed_doc', sa.String(length=100), nullable=True), + sa.Column('signed_doc_date', sa.Date(), nullable=True), + sa.Column('signed_doc_id', sa.String(length=50), nullable=True), + sa.Column('invoice_number', sa.String(length=50), nullable=True), + sa.Column('quantity_1', sa.String(length=50), nullable=True), + sa.Column('quantity_2', sa.String(length=50), nullable=True), + sa.Column('quantity_3', sa.String(length=50), nullable=True), + sa.Column('inspection', sa.String(length=200), nullable=True), + sa.Column('inspection_date', sa.Date(), nullable=True), + sa.Column('inspection_loc_date', sa.Date(), nullable=True), + sa.Column('inspection_location', sa.String(length=200), nullable=True), + sa.Column('country_production', sa.String(length=3), nullable=True), + sa.Column('country_source', sa.String(length=3), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['inv_part_id'], ['a24.inv_partes.id'], name='fk_aphis_general_inv_part'), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='inv_aphis_general_pkey'), + schema='a24' + ) + op.create_index(op.f('ix_a24_inv_aphis_general_company_id'), 'inv_aphis_general', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_inv_aphis_general_tenant_id'), 'inv_aphis_general', ['tenant_id'], unique=False, schema='a24') + op.create_table('ctm_receipts', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('receipt_line', sa.Integer(), nullable=False), + sa.Column('option', sa.String(length=3), nullable=True), + sa.Column('exit_invoice', sa.String(length=19), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['receipt_line'], ['a76.item_lines.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_ctm_receipts_company_id'), 'ctm_receipts', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_ctm_receipts_tenant_id'), 'ctm_receipts', ['tenant_id'], unique=False, schema='a76') + op.create_table('identifier_details', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('invoice_consecutive', sa.Integer(), nullable=True), + sa.Column('part_line', sa.Integer(), nullable=True), + sa.Column('identifier_code', sa.String(length=2), nullable=True), + sa.Column('item_line_id', sa.Integer(), nullable=True), + sa.Column('module', sa.String(length=20), nullable=True), + sa.Column('complement1', sa.String(length=50), nullable=True), + sa.Column('complement2', sa.String(length=51), nullable=True), + sa.Column('complement3', sa.String(length=50), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['identifier_code'], ['a76.identifiers.code'], ), + sa.ForeignKeyConstraint(['item_line_id'], ['a76.item_lines.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_identifier_details_company_id'), 'identifier_details', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_identifier_details_tenant_id'), 'identifier_details', ['tenant_id'], unique=False, schema='a76') + op.create_table('item_line_customs', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('item_line_id', sa.Integer(), nullable=False), + sa.Column('fraction', sa.String(length=10), nullable=True), + sa.Column('fraction_type', sa.String(length=7), nullable=True), + sa.Column('american_fraction', sa.String(length=16), nullable=True), + sa.Column('alternate_fraction', sa.String(length=10), nullable=True), + sa.Column('reference_fraction', sa.String(length=10), nullable=True), + sa.Column('octave_fraction', sa.String(length=10), nullable=True), + sa.Column('tlcan_fraction', sa.String(length=13), nullable=True), + sa.Column('extra_american_fraction', sa.String(length=16), nullable=True), + sa.Column('garment_fraction', sa.String(length=19), nullable=True), + sa.Column('advalorem', sa.String(length=10), nullable=True), + sa.Column('advalorem_numeric', sa.Numeric(precision=7, scale=2), nullable=True), + sa.Column('advalorem_american', sa.Numeric(precision=5, scale=2), nullable=True), + sa.Column('advalorem_tlcan', sa.Numeric(precision=5, scale=2), nullable=True), + sa.Column('rate', sa.String(length=10), nullable=True), + sa.Column('depreciation_rate', sa.Numeric(precision=5, scale=2), nullable=True), + sa.Column('origin_country', sa.String(length=3), nullable=True), + sa.Column('destination_country', sa.String(length=3), nullable=True), + sa.Column('optional_country', sa.String(length=3), nullable=True), + sa.Column('origin_procedure', sa.String(length=3), nullable=True), + sa.Column('scrap_procedure', sa.String(length=3), nullable=True), + sa.Column('sector', sa.String(length=8), nullable=True), + sa.ForeignKeyConstraint(['item_line_id'], ['a76.item_lines.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_table('item_line_descriptions', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('item_line_id', sa.Integer(), nullable=False), + sa.Column('description_spanish', sa.String(length=4999), nullable=True), + sa.Column('description_english', sa.String(length=4999), nullable=True), + sa.Column('extra_description', sa.Text(), nullable=True), + sa.Column('part_description', sa.String(length=500), nullable=True), + sa.Column('class_description', sa.String(length=500), nullable=True), + sa.Column('package_description', sa.String(length=500), nullable=True), + sa.Column('brand', sa.String(length=50), nullable=True), + sa.Column('model', sa.String(length=50), nullable=True), + sa.Column('has_serial', sa.Boolean(), nullable=True), + sa.Column('additional_info_spanish', sa.String(length=1000), nullable=True), + sa.Column('additional_info_english', sa.String(length=1000), nullable=True), + sa.Column('lot', sa.String(length=254), nullable=True), + sa.Column('entry_number', sa.String(length=50), nullable=True), + sa.Column('eighth_rule_fraction', sa.String(length=20), nullable=True), + sa.Column('eighth_rule_line', sa.Integer(), nullable=True), + sa.Column('consider_a31', sa.Boolean(), nullable=True), + sa.Column('machinery_location', sa.String(length=200), nullable=True), + sa.ForeignKeyConstraint(['item_line_id'], ['a76.item_lines.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_table('item_line_financials', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('item_line_id', sa.Integer(), nullable=False), + sa.Column('unit_cost_capture', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('unit_cost_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('unit_cost_commercial_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('unit_cost_current_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('unit_cost_depreciated_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('unit_cost_subitem_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('unit_cost_auxiliary_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('sales_cost_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('commercial_unit_cost', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('unit_cost_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('unit_cost_commercial_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('unit_cost_current_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('unit_cost_depreciated_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('unit_cost_subitem_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('sales_cost_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('unit_cost_mc', sa.Numeric(precision=29, scale=8), nullable=True), + sa.Column('value_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_commercial_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_updated_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_subitem_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('sub_import_value_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_returned_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_depreciated_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('customs_value_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_total_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_temp_material_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_def_material_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_added_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_national_packing_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('vat_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('vat_used_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('advalorem_line_mxn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_usd', sa.Numeric(precision=29, scale=8), nullable=True), + sa.Column('value_commercial_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_updated_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_subitem_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('sub_import_value_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_returned_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_depreciated_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('customs_value_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_auxiliary_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_total_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_temp_material_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_def_material_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_added_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_national_packing_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_us_packing_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('vat_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('vat_used_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_non_originating_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_originating_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('igi_amount_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('exempt_amount_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('total_commercial_value', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('advalorem_line_usd', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_mc', sa.Numeric(precision=29, scale=8), nullable=True), + sa.Column('sub_import_value_mc', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('vat_mc', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_added_mc', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_national_packing_mc', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_total_mc', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_temp_material_mc', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_def_material_mc', sa.Numeric(precision=23, scale=8), nullable=True), + sa.ForeignKeyConstraint(['item_line_id'], ['a76.item_lines.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_table('item_line_quantities', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('item_line_id', sa.Integer(), nullable=False), + sa.Column('quantity', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('alternate_quantity', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('quantity_uma', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('auxiliary_quantity', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('quantity_temp_export', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('quantity_existence', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('quantity_returned', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('quantity_returned_temp', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('serial_count', sa.Integer(), nullable=True), + sa.Column('net_weight', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('gross_weight', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('package_id', sa.Integer(), nullable=True), + sa.Column('package_quantity', sa.Integer(), nullable=True), + sa.Column('container_quantity', sa.SmallInteger(), nullable=True), + sa.Column('container_description', sa.String(length=40), nullable=True), + sa.Column('box_count', sa.String(length=30), nullable=True), + sa.ForeignKeyConstraint(['item_line_id'], ['a76.item_lines.id'], ), + sa.ForeignKeyConstraint(['package_id'], ['a76.packages.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_table('item_line_series', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('line_item_id', sa.Integer(), nullable=False), + sa.Column('row', sa.Integer(), nullable=False), + sa.Column('serial_numbers', sa.String(length=50), nullable=True), + sa.Column('model', sa.String(length=50), nullable=True), + sa.Column('sub_model', sa.String(length=50), nullable=True), + sa.Column('brand', sa.String(length=50), nullable=True), + sa.Column('number_id', sa.String(length=25), nullable=True), + sa.Column('discharge', sa.Boolean(), nullable=True), + sa.Column('serie_row', sa.Integer(), nullable=True), + sa.Column('image_path', sa.String(length=255), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['line_item_id'], ['a76.item_lines.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + op.create_index(op.f('ix_a76_item_line_series_company_id'), 'item_line_series', ['company_id'], unique=False, schema='a76') + op.create_index(op.f('ix_a76_item_line_series_tenant_id'), 'item_line_series', ['tenant_id'], unique=False, schema='a76') + op.create_table('discharge_detail', + sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column('discharge_header_id', sa.BigInteger(), nullable=False), + sa.Column('export_item_line_id', sa.Integer(), nullable=True, comment='NULL for waste-only discharges.'), + sa.Column('part_number', sa.String(length=70), nullable=True, comment='NUMPARTE of the export line (denormalized)'), + sa.Column('export_part_number', sa.String(length=70), nullable=True, comment='NUMPARTEEXPO — as it appears in the pedimento'), + sa.Column('export_line_ref', sa.Integer(), nullable=True, comment='LINEAEXPOREF — for rectification references'), + sa.Column('import_item_line_id', sa.Integer(), nullable=False), + sa.Column('movement_id', sa.BigInteger(), nullable=False, comment='The BalanceMovement that records this consumption. Required.'), + sa.Column('quantity_discharged', sa.Numeric(precision=19, scale=8), nullable=False), + sa.Column('unit_of_measure', sa.String(length=5), nullable=True), + sa.Column('value_mn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_me', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('net_weight', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('gross_weight', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('tariff_fraction', sa.String(length=10), nullable=True), + sa.Column('fraction_type', sa.String(length=7), nullable=True), + sa.Column('ad_valorem', sa.String(length=10), nullable=True), + sa.Column('country_of_origin', sa.String(length=3), nullable=True), + sa.Column('sector', sa.String(length=8), nullable=True), + sa.Column('original_part', sa.String(length=70), nullable=True, comment='PARTEORIGINAL'), + sa.Column('equivalent_quantity', sa.Numeric(precision=19, scale=8), nullable=True, comment='CANTEQUIVALENTE'), + sa.Column('equivalent_unit', sa.String(length=5), nullable=True), + sa.Column('returned_quantity_sm', sa.Numeric(precision=19, scale=8), nullable=True, comment='CANTRETORNADASAM'), + sa.Column('waste_type', sa.String(length=1), nullable=True, comment='M=merma, D=desperdicio, S=scrap'), + sa.Column('take_balance_base_pt', sa.String(length=2), nullable=True, comment='TOMARSALDOBASEALPT'), + sa.Column('igi_amount', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('tax_payment', sa.String(length=1), nullable=True), + sa.Column('has_certificate', sa.String(length=1), nullable=True), + sa.Column('iva_mn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('iva_me', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('origin_import_invoice', sa.String(length=15), nullable=True, comment='FACTURAIMPO original (denorm for SM)'), + sa.Column('procedence', sa.String(length=3), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.CheckConstraint('movement_id IS NOT NULL', name='ck_dischdet_movement_required'), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['discharge_header_id'], ['a24.discharge_header.id'], ), + sa.ForeignKeyConstraint(['export_item_line_id'], ['a76.item_lines.id'], ), + sa.ForeignKeyConstraint(['import_item_line_id'], ['a76.item_lines.id'], ), + sa.ForeignKeyConstraint(['movement_id'], ['a24.balance_movement.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a24' + ) + op.create_index(op.f('ix_a24_discharge_detail_company_id'), 'discharge_detail', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_discharge_detail_tenant_id'), 'discharge_detail', ['tenant_id'], unique=False, schema='a24') + op.create_index('ix_dischdet_export_line', 'discharge_detail', ['export_item_line_id'], unique=False, schema='a24') + op.create_index('ix_dischdet_header', 'discharge_detail', ['discharge_header_id'], unique=False, schema='a24') + op.create_index('ix_dischdet_import_lot', 'discharge_detail', ['import_item_line_id'], unique=False, schema='a24') + op.create_index('ix_dischdet_part', 'discharge_detail', ['tenant_id', 'part_number'], unique=False, schema='a24') + op.create_table('discharge_scrap', + sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column('discharge_header_id', sa.BigInteger(), nullable=True, comment='NULL when scrap is registered independently (not tied to an export).'), + sa.Column('import_item_line_id', sa.Integer(), nullable=False), + sa.Column('movement_id', sa.BigInteger(), nullable=True), + sa.Column('scrap_type', sa.String(length=1), nullable=False, comment='M=merma, D=desperdicio, S=scrap, X=destrucción'), + sa.Column('finished_good_line_id', sa.Integer(), nullable=True, comment='Export line of the product whose manufacture created this scrap.'), + sa.Column('finished_good_part', sa.String(length=70), nullable=True), + sa.Column('scrap_export_invoice_id', sa.BigInteger(), nullable=True, comment='If desperdicio has its own export pedimento.'), + sa.Column('part_number', sa.String(length=70), nullable=False), + sa.Column('item_class', sa.String(length=8), nullable=True), + sa.Column('quantity', sa.Numeric(precision=19, scale=8), nullable=False), + sa.Column('unit_of_measure', sa.String(length=5), nullable=False), + sa.Column('value_mn', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('value_me', sa.Numeric(precision=23, scale=8), nullable=True), + sa.Column('net_weight', sa.Numeric(precision=19, scale=8), nullable=True), + sa.Column('procedence', sa.String(length=3), nullable=True), + sa.Column('scrap_date', sa.Date(), nullable=False), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['discharge_header_id'], ['a24.discharge_header.id'], ), + sa.ForeignKeyConstraint(['finished_good_line_id'], ['a76.item_lines.id'], ), + sa.ForeignKeyConstraint(['import_item_line_id'], ['a76.item_lines.id'], ), + sa.ForeignKeyConstraint(['movement_id'], ['a24.balance_movement.id'], ), + sa.ForeignKeyConstraint(['scrap_export_invoice_id'], ['a76.invoice_header.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a24' + ) + op.create_index(op.f('ix_a24_discharge_scrap_company_id'), 'discharge_scrap', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_discharge_scrap_tenant_id'), 'discharge_scrap', ['tenant_id'], unique=False, schema='a24') + op.create_index('ix_dischscrap_date', 'discharge_scrap', ['tenant_id', 'scrap_date'], unique=False, schema='a24') + op.create_index('ix_dischscrap_header', 'discharge_scrap', ['discharge_header_id'], unique=False, schema='a24') + op.create_index('ix_dischscrap_import_lot', 'discharge_scrap', ['import_item_line_id'], unique=False, schema='a24') + op.create_table('inv_aphis_characteristic', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('aphis_general_id', sa.Integer(), nullable=False), + sa.Column('item_id', sa.String(length=50), nullable=True), + sa.Column('number_from', sa.String(length=50), nullable=True), + sa.Column('number_to', sa.String(length=50), nullable=True), + sa.Column('category_type', sa.String(length=50), nullable=True), + sa.Column('commodity_qua', sa.String(length=50), nullable=True), + sa.Column('commodity_char_qua', sa.String(length=50), nullable=True), + sa.Column('description', sa.String(length=200), nullable=True), + sa.Column('category_code', sa.String(length=50), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['aphis_general_id'], ['a24.inv_aphis_general.id'], ), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='inv_aphis_characteristic_pkey'), + schema='a24' + ) + op.create_index(op.f('ix_a24_inv_aphis_characteristic_company_id'), 'inv_aphis_characteristic', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_inv_aphis_characteristic_tenant_id'), 'inv_aphis_characteristic', ['tenant_id'], unique=False, schema='a24') + op.create_table('inv_aphis_containers', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('aphis_general_id', sa.Integer(), nullable=False), + sa.Column('container_number', sa.String(length=50), nullable=True), + sa.Column('length', sa.String(length=20), nullable=True), + sa.Column('type', sa.String(length=50), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['aphis_general_id'], ['a24.inv_aphis_general.id'], ), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='inv_aphis_containers_pkey'), + schema='a24' + ) + op.create_index(op.f('ix_a24_inv_aphis_containers_company_id'), 'inv_aphis_containers', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_inv_aphis_containers_tenant_id'), 'inv_aphis_containers', ['tenant_id'], unique=False, schema='a24') + op.create_table('inv_aphis_entities', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('aphis_general_id', sa.Integer(), nullable=False), + sa.Column('consignee_key', sa.String(length=50), nullable=True), + sa.Column('broker_key', sa.String(length=50), nullable=True), + sa.Column('lpco_auth_party_key', sa.String(length=50), nullable=True), + sa.Column('grower_key', sa.String(length=50), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['aphis_general_id'], ['a24.inv_aphis_general.id'], ), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='inv_aphis_entities_pkey'), + schema='a24' + ) + op.create_index(op.f('ix_a24_inv_aphis_entities_company_id'), 'inv_aphis_entities', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_inv_aphis_entities_tenant_id'), 'inv_aphis_entities', ['tenant_id'], unique=False, schema='a24') + op.create_table('inv_aphis_lpcos', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('aphis_general_id', sa.Integer(), nullable=False), + sa.Column('issuer', sa.String(length=100), nullable=True), + sa.Column('issuer_loc_qua', sa.String(length=50), nullable=True), + sa.Column('issuer_loc', sa.String(length=50), nullable=True), + sa.Column('issuer_loc_desc', sa.String(length=200), nullable=True), + sa.Column('uom', sa.String(length=20), nullable=True), + sa.Column('txn_type', sa.String(length=50), nullable=True), + sa.Column('type', sa.String(length=50), nullable=True), + sa.Column('number', sa.String(length=50), nullable=True), + sa.Column('date_qual', sa.String(length=50), nullable=True), + sa.Column('date', sa.Date(), nullable=True), + sa.Column('qty', sa.String(length=50), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['aphis_general_id'], ['a24.inv_aphis_general.id'], ), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='inv_aphis_lpcos_pkey'), + schema='a24' + ) + op.create_index(op.f('ix_a24_inv_aphis_lpcos_company_id'), 'inv_aphis_lpcos', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_inv_aphis_lpcos_tenant_id'), 'inv_aphis_lpcos', ['tenant_id'], unique=False, schema='a24') + op.create_table('inv_aphis_routing', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('aphis_general_id', sa.Integer(), nullable=False), + sa.Column('type', sa.String(length=50), nullable=True), + sa.Column('country', sa.String(length=50), nullable=True), + sa.Column('name', sa.String(length=100), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['aphis_general_id'], ['a24.inv_aphis_general.id'], ), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='inv_aphis_routing_pkey'), + schema='a24' + ) + op.create_index(op.f('ix_a24_inv_aphis_routing_company_id'), 'inv_aphis_routing', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_inv_aphis_routing_tenant_id'), 'inv_aphis_routing', ['tenant_id'], unique=False, schema='a24') + op.create_table('inv_aphis_stype_pitems', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('aphis_general_id', sa.Integer(), nullable=False), + sa.Column('source_type_code', sa.String(length=50), nullable=True), + sa.Column('country_code', sa.String(length=3), nullable=True), + sa.Column('geo_location', sa.String(length=100), nullable=True), + sa.Column('processing_start', sa.Date(), nullable=True), + sa.Column('processing_end', sa.Date(), nullable=True), + sa.Column('processing_type', sa.String(length=50), nullable=True), + sa.Column('processing_desc', sa.String(length=200), nullable=True), + sa.Column('tenant_id', sa.Integer(), nullable=False), + sa.Column('company_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=False), + sa.Column('deleted_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['aphis_general_id'], ['a24.inv_aphis_general.id'], ), + sa.ForeignKeyConstraint(['company_id'], ['a76.company.id'], ), + sa.ForeignKeyConstraint(['tenant_id'], ['core.tenants.id'], ), + sa.PrimaryKeyConstraint('id', name='inv_aphis_stype_pitems_pkey'), + schema='a24' + ) + op.create_index(op.f('ix_a24_inv_aphis_stype_pitems_company_id'), 'inv_aphis_stype_pitems', ['company_id'], unique=False, schema='a24') + op.create_index(op.f('ix_a24_inv_aphis_stype_pitems_tenant_id'), 'inv_aphis_stype_pitems', ['tenant_id'], unique=False, schema='a24') + op.create_table('item_line_references', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('item_line_id', sa.Integer(), nullable=False), + sa.Column('serie_id', sa.Integer(), nullable=True), + sa.Column('customer_invoice', sa.Integer(), nullable=True), + sa.Column('assigned_client', sa.Integer(), nullable=True), + sa.Column('supplier', sa.Integer(), nullable=True), + sa.Column('requisitioner', sa.Integer(), nullable=True), + sa.Column('sent_to', sa.Integer(), nullable=True), + sa.Column('ped_line', sa.Integer(), nullable=True), + sa.Column('ro_line', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['assigned_client'], ['a76.clients_and_providers.id'], ), + sa.ForeignKeyConstraint(['customer_invoice'], ['a76.clients_and_providers.id'], ), + sa.ForeignKeyConstraint(['item_line_id'], ['a76.item_lines.id'], ), + sa.ForeignKeyConstraint(['requisitioner'], ['a76.clients_and_providers.id'], ), + sa.ForeignKeyConstraint(['sent_to'], ['a76.clients_and_providers.id'], ), + sa.ForeignKeyConstraint(['serie_id'], ['a76.item_line_series.id'], ), + sa.ForeignKeyConstraint(['supplier'], ['a76.clients_and_providers.id'], ), + sa.PrimaryKeyConstraint('id'), + schema='a76' + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('item_line_references', schema='a76') + op.drop_index(op.f('ix_a24_inv_aphis_stype_pitems_tenant_id'), table_name='inv_aphis_stype_pitems', schema='a24') + op.drop_index(op.f('ix_a24_inv_aphis_stype_pitems_company_id'), table_name='inv_aphis_stype_pitems', schema='a24') + op.drop_table('inv_aphis_stype_pitems', schema='a24') + op.drop_index(op.f('ix_a24_inv_aphis_routing_tenant_id'), table_name='inv_aphis_routing', schema='a24') + op.drop_index(op.f('ix_a24_inv_aphis_routing_company_id'), table_name='inv_aphis_routing', schema='a24') + op.drop_table('inv_aphis_routing', schema='a24') + op.drop_index(op.f('ix_a24_inv_aphis_lpcos_tenant_id'), table_name='inv_aphis_lpcos', schema='a24') + op.drop_index(op.f('ix_a24_inv_aphis_lpcos_company_id'), table_name='inv_aphis_lpcos', schema='a24') + op.drop_table('inv_aphis_lpcos', schema='a24') + op.drop_index(op.f('ix_a24_inv_aphis_entities_tenant_id'), table_name='inv_aphis_entities', schema='a24') + op.drop_index(op.f('ix_a24_inv_aphis_entities_company_id'), table_name='inv_aphis_entities', schema='a24') + op.drop_table('inv_aphis_entities', schema='a24') + op.drop_index(op.f('ix_a24_inv_aphis_containers_tenant_id'), table_name='inv_aphis_containers', schema='a24') + op.drop_index(op.f('ix_a24_inv_aphis_containers_company_id'), table_name='inv_aphis_containers', schema='a24') + op.drop_table('inv_aphis_containers', schema='a24') + op.drop_index(op.f('ix_a24_inv_aphis_characteristic_tenant_id'), table_name='inv_aphis_characteristic', schema='a24') + op.drop_index(op.f('ix_a24_inv_aphis_characteristic_company_id'), table_name='inv_aphis_characteristic', schema='a24') + op.drop_table('inv_aphis_characteristic', schema='a24') + op.drop_index('ix_dischscrap_import_lot', table_name='discharge_scrap', schema='a24') + op.drop_index('ix_dischscrap_header', table_name='discharge_scrap', schema='a24') + op.drop_index('ix_dischscrap_date', table_name='discharge_scrap', schema='a24') + op.drop_index(op.f('ix_a24_discharge_scrap_tenant_id'), table_name='discharge_scrap', schema='a24') + op.drop_index(op.f('ix_a24_discharge_scrap_company_id'), table_name='discharge_scrap', schema='a24') + op.drop_table('discharge_scrap', schema='a24') + op.drop_index('ix_dischdet_part', table_name='discharge_detail', schema='a24') + op.drop_index('ix_dischdet_import_lot', table_name='discharge_detail', schema='a24') + op.drop_index('ix_dischdet_header', table_name='discharge_detail', schema='a24') + op.drop_index('ix_dischdet_export_line', table_name='discharge_detail', schema='a24') + op.drop_index(op.f('ix_a24_discharge_detail_tenant_id'), table_name='discharge_detail', schema='a24') + op.drop_index(op.f('ix_a24_discharge_detail_company_id'), table_name='discharge_detail', schema='a24') + op.drop_table('discharge_detail', schema='a24') + op.drop_index(op.f('ix_a76_item_line_series_tenant_id'), table_name='item_line_series', schema='a76') + op.drop_index(op.f('ix_a76_item_line_series_company_id'), table_name='item_line_series', schema='a76') + op.drop_table('item_line_series', schema='a76') + op.drop_table('item_line_quantities', schema='a76') + op.drop_table('item_line_financials', schema='a76') + op.drop_table('item_line_descriptions', schema='a76') + op.drop_table('item_line_customs', schema='a76') + op.drop_index(op.f('ix_a76_identifier_details_tenant_id'), table_name='identifier_details', schema='a76') + op.drop_index(op.f('ix_a76_identifier_details_company_id'), table_name='identifier_details', schema='a76') + op.drop_table('identifier_details', schema='a76') + op.drop_index(op.f('ix_a76_ctm_receipts_tenant_id'), table_name='ctm_receipts', schema='a76') + op.drop_index(op.f('ix_a76_ctm_receipts_company_id'), table_name='ctm_receipts', schema='a76') + op.drop_table('ctm_receipts', schema='a76') + op.drop_index(op.f('ix_a24_inv_aphis_general_tenant_id'), table_name='inv_aphis_general', schema='a24') + op.drop_index(op.f('ix_a24_inv_aphis_general_company_id'), table_name='inv_aphis_general', schema='a24') + op.drop_table('inv_aphis_general', schema='a24') + op.drop_index(op.f('ix_a24_fa_item_lines_tenant_id'), table_name='fa_item_lines', schema='a24') + op.drop_index(op.f('ix_a24_fa_item_lines_company_id'), table_name='fa_item_lines', schema='a24') + op.drop_table('fa_item_lines', schema='a24') + op.drop_index('ix_balmov_source', table_name='balance_movement', schema='a24') + op.drop_index('ix_balmov_peps_lookup', table_name='balance_movement', schema='a24', postgresql_include=['import_item_line_id', 'quantity', 'value_me', 'value_mn']) + op.drop_index('ix_balmov_operation_date', table_name='balance_movement', schema='a24') + op.drop_index('ix_balmov_lot', table_name='balance_movement', schema='a24') + op.drop_index(op.f('ix_a24_balance_movement_tenant_id'), table_name='balance_movement', schema='a24') + op.drop_index(op.f('ix_a24_balance_movement_company_id'), table_name='balance_movement', schema='a24') + op.drop_table('balance_movement', schema='a24') + op.drop_index(op.f('ix_a76_item_lines_tenant_id'), table_name='item_lines', schema='a76') + op.drop_index(op.f('ix_a76_item_lines_company_id'), table_name='item_lines', schema='a76') + op.drop_table('item_lines', schema='a76') + op.drop_index(op.f('ix_a24_inv_partes_tenant_id'), table_name='inv_partes', schema='a24') + op.drop_index(op.f('ix_a24_inv_partes_company_id'), table_name='inv_partes', schema='a24') + op.drop_table('inv_partes', schema='a24') + op.drop_index(op.f('ix_a24_inv_parte_paises_tenant_id'), table_name='inv_parte_paises', schema='a24') + op.drop_index(op.f('ix_a24_inv_parte_paises_company_id'), table_name='inv_parte_paises', schema='a24') + op.drop_table('inv_parte_paises', schema='a24') + op.drop_index(op.f('ix_a24_inv_bom_tenant_id'), table_name='inv_bom', schema='a24') + op.drop_index(op.f('ix_a24_inv_bom_company_id'), table_name='inv_bom', schema='a24') + op.drop_table('inv_bom', schema='a24') + op.drop_index(op.f('ix_a24_fa_partes_tenant_id'), table_name='fa_partes', schema='a24') + op.drop_index(op.f('ix_a24_fa_partes_company_id'), table_name='fa_partes', schema='a24') + op.drop_table('fa_partes', schema='a24') + op.drop_index(op.f('ix_a76_pedimento_validation_tenant_id'), table_name='pedimento_validation', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_validation_company_id'), table_name='pedimento_validation', schema='a76') + op.drop_table('pedimento_validation', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_transport_means_tenant_id'), table_name='pedimento_transport_means', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_transport_means_company_id'), table_name='pedimento_transport_means', schema='a76') + op.drop_table('pedimento_transport_means', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_transport_carriers_tenant_id'), table_name='pedimento_transport_carriers', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_transport_carriers_company_id'), table_name='pedimento_transport_carriers', schema='a76') + op.drop_table('pedimento_transport_carriers', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_seals_tenant_id'), table_name='pedimento_seals', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_seals_company_id'), table_name='pedimento_seals', schema='a76') + op.drop_table('pedimento_seals', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_rectification_origin_tenant_id'), table_name='pedimento_rectification_origin', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_rectification_origin_company_id'), table_name='pedimento_rectification_origin', schema='a76') + op.drop_table('pedimento_rectification_origin', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_rectification_destination_tenant_id'), table_name='pedimento_rectification_destination', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_rectification_destination_company_id'), table_name='pedimento_rectification_destination', schema='a76') + op.drop_table('pedimento_rectification_destination', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_payments_tenant_id'), table_name='pedimento_payments', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_payments_company_id'), table_name='pedimento_payments', schema='a76') + op.drop_index('idx_pedimento_payments_pedimento_id', table_name='pedimento_payments', schema='a76') + op.drop_table('pedimento_payments', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_packages_tenant_id'), table_name='pedimento_packages', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_packages_company_id'), table_name='pedimento_packages', schema='a76') + op.drop_table('pedimento_packages', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_indexes_tenant_id'), table_name='pedimento_indexes', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_indexes_company_id'), table_name='pedimento_indexes', schema='a76') + op.drop_table('pedimento_indexes', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_incrementables_tenant_id'), table_name='pedimento_incrementables', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_incrementables_company_id'), table_name='pedimento_incrementables', schema='a76') + op.drop_table('pedimento_incrementables', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_guides_tenant_id'), table_name='pedimento_guides', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_guides_company_id'), table_name='pedimento_guides', schema='a76') + op.drop_table('pedimento_guides', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_decrementables_tenant_id'), table_name='pedimento_decrementables', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_decrementables_company_id'), table_name='pedimento_decrementables', schema='a76') + op.drop_table('pedimento_decrementables', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_dates_tenant_id'), table_name='pedimento_dates', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_dates_company_id'), table_name='pedimento_dates', schema='a76') + op.drop_index('idx_pedimento_dates_pedimento_id', table_name='pedimento_dates', schema='a76') + op.drop_table('pedimento_dates', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_customs_offices_tenant_id'), table_name='pedimento_customs_offices', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_customs_offices_company_id'), table_name='pedimento_customs_offices', schema='a76') + op.drop_table('pedimento_customs_offices', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_contributions_tenant_id'), table_name='pedimento_contributions', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_contributions_company_id'), table_name='pedimento_contributions', schema='a76') + op.drop_table('pedimento_contributions', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_containers_tenant_id'), table_name='pedimento_containers', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_containers_company_id'), table_name='pedimento_containers', schema='a76') + op.drop_table('pedimento_containers', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_config_updates_tenant_id'), table_name='pedimento_config_updates', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_config_updates_company_id'), table_name='pedimento_config_updates', schema='a76') + op.drop_table('pedimento_config_updates', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_config_update_rectification_tenant_id'), table_name='pedimento_config_update_rectification', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_config_update_rectification_company_id'), table_name='pedimento_config_update_rectification', schema='a76') + op.drop_table('pedimento_config_update_rectification', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_config_surcharges_tenant_id'), table_name='pedimento_config_surcharges', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_config_surcharges_company_id'), table_name='pedimento_config_surcharges', schema='a76') + op.drop_table('pedimento_config_surcharges', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_config_parameters_tenant_id'), table_name='pedimento_config_parameters', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_config_parameters_company_id'), table_name='pedimento_config_parameters', schema='a76') + op.drop_table('pedimento_config_parameters', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_config_calculations_tenant_id'), table_name='pedimento_config_calculations', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_config_calculations_company_id'), table_name='pedimento_config_calculations', schema='a76') + op.drop_table('pedimento_config_calculations', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_config_additional_tenant_id'), table_name='pedimento_config_additional', schema='a76') + op.drop_index(op.f('ix_a76_pedimento_config_additional_company_id'), table_name='pedimento_config_additional', schema='a76') + op.drop_table('pedimento_config_additional', schema='a76') + op.drop_index(op.f('ix_a76_parts_tenant_id'), table_name='parts', schema='a76') + op.drop_index(op.f('ix_a76_parts_company_id'), table_name='parts', schema='a76') + op.drop_table('parts', schema='a76') + op.drop_index(op.f('ix_a76_invoice_compliance_mx_tenant_id'), table_name='invoice_compliance_mx', schema='a76') + op.drop_index(op.f('ix_a76_invoice_compliance_mx_company_id'), table_name='invoice_compliance_mx', schema='a76') + op.drop_table('invoice_compliance_mx', schema='a76') + op.drop_index(op.f('ix_a76_doda_container_seals_tenant_id'), table_name='doda_container_seals', schema='a76') + op.drop_index(op.f('ix_a76_doda_container_seals_company_id'), table_name='doda_container_seals', schema='a76') + op.drop_table('doda_container_seals', schema='a76') + op.drop_index(op.f('ix_a24_inv_classes_tenant_id'), table_name='inv_classes', schema='a24') + op.drop_index(op.f('ix_a24_inv_classes_company_id'), table_name='inv_classes', schema='a24') + op.drop_table('inv_classes', schema='a24') + op.drop_index(op.f('ix_a24_fa_classes_tenant_id'), table_name='fa_classes', schema='a24') + op.drop_index(op.f('ix_a24_fa_classes_company_id'), table_name='fa_classes', schema='a24') + op.drop_table('fa_classes', schema='a24') + op.drop_index('ix_user_company_roles_user_company', table_name='user_company_roles', schema='core') + op.drop_index(op.f('ix_core_user_company_roles_user_id'), table_name='user_company_roles', schema='core') + op.drop_index(op.f('ix_core_user_company_roles_tenant_id'), table_name='user_company_roles', schema='core') + op.drop_index(op.f('ix_core_user_company_roles_id'), table_name='user_company_roles', schema='core') + op.drop_index(op.f('ix_core_user_company_roles_company_role_id'), table_name='user_company_roles', schema='core') + op.drop_index(op.f('ix_core_user_company_roles_company_id'), table_name='user_company_roles', schema='core') + op.drop_table('user_company_roles', schema='core') + op.drop_index('ix_role_permissions_composite', table_name='role_permissions', schema='core') + op.drop_index(op.f('ix_core_role_permissions_tenant_id'), table_name='role_permissions', schema='core') + op.drop_index(op.f('ix_core_role_permissions_permission_id'), table_name='role_permissions', schema='core') + op.drop_index(op.f('ix_core_role_permissions_id'), table_name='role_permissions', schema='core') + op.drop_index(op.f('ix_core_role_permissions_company_role_id'), table_name='role_permissions', schema='core') + op.drop_index(op.f('ix_core_role_permissions_company_id'), table_name='role_permissions', schema='core') + op.drop_table('role_permissions', schema='core') + op.drop_index(op.f('ix_a76_unit_conversions_tenant_id'), table_name='unit_conversions', schema='a76') + op.drop_index(op.f('ix_a76_unit_conversions_company_id'), table_name='unit_conversions', schema='a76') + op.drop_table('unit_conversions', schema='a76') + op.drop_index(op.f('ix_a76_pedimentos_tenant_id'), table_name='pedimentos', schema='a76') + op.drop_index(op.f('ix_a76_pedimentos_company_id'), table_name='pedimentos', schema='a76') + op.drop_index('idx_pedimentos_status', table_name='pedimentos', schema='a76') + op.drop_index('idx_pedimentos_created_at', table_name='pedimentos', schema='a76') + op.drop_index('idx_pedimentos_client_id', table_name='pedimentos', schema='a76') + op.drop_table('pedimentos', schema='a76') + op.drop_index(op.f('ix_a76_invoice_sales_details_tenant_id'), table_name='invoice_sales_details', schema='a76') + op.drop_index(op.f('ix_a76_invoice_sales_details_company_id'), table_name='invoice_sales_details', schema='a76') + op.drop_table('invoice_sales_details', schema='a76') + op.drop_index(op.f('ix_a76_invoice_logistics_tenant_id'), table_name='invoice_logistics', schema='a76') + op.drop_index(op.f('ix_a76_invoice_logistics_company_id'), table_name='invoice_logistics', schema='a76') + op.drop_table('invoice_logistics', schema='a76') + op.drop_index(op.f('ix_a76_invoice_financials_tenant_id'), table_name='invoice_financials', schema='a76') + op.drop_index(op.f('ix_a76_invoice_financials_company_id'), table_name='invoice_financials', schema='a76') + op.drop_table('invoice_financials', schema='a76') + op.drop_index(op.f('ix_a76_invoice_collections_tenant_id'), table_name='invoice_collections', schema='a76') + op.drop_index(op.f('ix_a76_invoice_collections_company_id'), table_name='invoice_collections', schema='a76') + op.drop_table('invoice_collections', schema='a76') + op.drop_index(op.f('ix_a76_fda_specifications_tenant_id'), table_name='fda_specifications', schema='a76') + op.drop_index(op.f('ix_a76_fda_specifications_fda_catalog_id'), table_name='fda_specifications', schema='a76') + op.drop_index(op.f('ix_a76_fda_specifications_company_id'), table_name='fda_specifications', schema='a76') + op.drop_table('fda_specifications', schema='a76') + op.drop_index(op.f('ix_a76_fda_lot_production_tenant_id'), table_name='fda_lot_production', schema='a76') + op.drop_index(op.f('ix_a76_fda_lot_production_fda_catalog_id'), table_name='fda_lot_production', schema='a76') + op.drop_index(op.f('ix_a76_fda_lot_production_company_id'), table_name='fda_lot_production', schema='a76') + op.drop_table('fda_lot_production', schema='a76') + op.drop_index(op.f('ix_a76_fda_constituent_elements_tenant_id'), table_name='fda_constituent_elements', schema='a76') + op.drop_index(op.f('ix_a76_fda_constituent_elements_fda_catalog_id'), table_name='fda_constituent_elements', schema='a76') + op.drop_index(op.f('ix_a76_fda_constituent_elements_company_id'), table_name='fda_constituent_elements', schema='a76') + op.drop_table('fda_constituent_elements', schema='a76') + op.drop_index(op.f('ix_a76_fda_affirmation_codes_tenant_id'), table_name='fda_affirmation_codes', schema='a76') + op.drop_index(op.f('ix_a76_fda_affirmation_codes_fda_catalog_id'), table_name='fda_affirmation_codes', schema='a76') + op.drop_index(op.f('ix_a76_fda_affirmation_codes_company_id'), table_name='fda_affirmation_codes', schema='a76') + op.drop_table('fda_affirmation_codes', schema='a76') + op.drop_index(op.f('ix_a76_fa_location_ext_tenant_id'), table_name='fa_location_ext', schema='a76') + op.drop_index(op.f('ix_a76_fa_location_ext_company_id'), table_name='fa_location_ext', schema='a76') + op.drop_table('fa_location_ext', schema='a76') + op.drop_index(op.f('ix_a76_error_catalogs_tenant_id'), table_name='error_catalogs', schema='a76') + op.drop_index(op.f('ix_a76_error_catalogs_company_id'), table_name='error_catalogs', schema='a76') + op.drop_table('error_catalogs', schema='a76') + op.drop_index(op.f('ix_a76_equivalencies_tenant_id'), table_name='equivalencies', schema='a76') + op.drop_index(op.f('ix_a76_equivalencies_company_id'), table_name='equivalencies', schema='a76') + op.drop_table('equivalencies', schema='a76') + op.drop_index(op.f('ix_a76_driver_tenant_id'), table_name='driver', schema='a76') + op.drop_index(op.f('ix_a76_driver_company_id'), table_name='driver', schema='a76') + op.drop_table('driver', schema='a76') + op.drop_index(op.f('ix_a76_doda_pedimentos_tenant_id'), table_name='doda_pedimentos', schema='a76') + op.drop_index(op.f('ix_a76_doda_pedimentos_company_id'), table_name='doda_pedimentos', schema='a76') + op.drop_table('doda_pedimentos', schema='a76') + op.drop_index(op.f('ix_a76_doda_containers_tenant_id'), table_name='doda_containers', schema='a76') + op.drop_index(op.f('ix_a76_doda_containers_company_id'), table_name='doda_containers', schema='a76') + op.drop_table('doda_containers', schema='a76') + op.drop_index(op.f('ix_a76_doda_american_pedimentos_tenant_id'), table_name='doda_american_pedimentos', schema='a76') + op.drop_index(op.f('ix_a76_doda_american_pedimentos_company_id'), table_name='doda_american_pedimentos', schema='a76') + op.drop_table('doda_american_pedimentos', schema='a76') + op.drop_index(op.f('ix_a76_customs_brokers_vu_tenant_id'), table_name='customs_brokers_vu', schema='a76') + op.drop_index(op.f('ix_a76_customs_brokers_vu_company_id'), table_name='customs_brokers_vu', schema='a76') + op.drop_table('customs_brokers_vu', schema='a76') + op.drop_index(op.f('ix_a76_customs_brokers_personnel_tenant_id'), table_name='customs_brokers_personnel', schema='a76') + op.drop_index(op.f('ix_a76_customs_brokers_personnel_company_id'), table_name='customs_brokers_personnel', schema='a76') + op.drop_table('customs_brokers_personnel', schema='a76') + op.drop_index(op.f('ix_a76_country_rule_oct_tenant_id'), table_name='country_rule_oct', schema='a76') + op.drop_index(op.f('ix_a76_country_rule_oct_company_id'), table_name='country_rule_oct', schema='a76') + op.drop_table('country_rule_oct', schema='a76') + op.drop_index(op.f('ix_a76_concepts_tenant_id'), table_name='concepts', schema='a76') + op.drop_table('concepts', schema='a76') + op.drop_index(op.f('ix_a76_concept_manifestations_tenant_id'), table_name='concept_manifestations', schema='a76') + op.drop_index(op.f('ix_a76_concept_manifestations_company_id'), table_name='concept_manifestations', schema='a76') + op.drop_index('idx_concept_manifestations_value_manifestation_id', table_name='concept_manifestations', schema='a76') + op.drop_table('concept_manifestations', schema='a76') + op.drop_index(op.f('ix_a76_clients_and_providers_programs_tenant_id'), table_name='clients_and_providers_programs', schema='a76') + op.drop_index(op.f('ix_a76_clients_and_providers_programs_company_id'), table_name='clients_and_providers_programs', schema='a76') + op.drop_table('clients_and_providers_programs', schema='a76') + op.drop_index(op.f('ix_a76_clients_and_providers_address_tenant_id'), table_name='clients_and_providers_address', schema='a76') + op.drop_index(op.f('ix_a76_clients_and_providers_address_company_id'), table_name='clients_and_providers_address', schema='a76') + op.drop_table('clients_and_providers_address', schema='a76') + op.drop_index(op.f('ix_a76_classes_tenant_id'), table_name='classes', schema='a76') + op.drop_index(op.f('ix_a76_classes_company_id'), table_name='classes', schema='a76') + op.drop_table('classes', schema='a76') + op.drop_index('ix_dischdr_source', table_name='discharge_header', schema='a24') + op.drop_index('ix_dischdr_date', table_name='discharge_header', schema='a24') + op.drop_index(op.f('ix_a24_discharge_header_tenant_id'), table_name='discharge_header', schema='a24') + op.drop_index(op.f('ix_a24_discharge_header_company_id'), table_name='discharge_header', schema='a24') + op.drop_table('discharge_header', schema='a24') + op.drop_index(op.f('ix_public_warning_fractions_warning_type'), table_name='warning_fractions', schema='public') + op.drop_index(op.f('ix_public_warning_fractions_tenant_id'), table_name='warning_fractions', schema='public') + op.drop_index(op.f('ix_public_warning_fractions_fraction'), table_name='warning_fractions', schema='public') + op.drop_index(op.f('ix_public_warning_fractions_company_id'), table_name='warning_fractions', schema='public') + op.drop_table('warning_fractions', schema='public') + op.drop_index(op.f('ix_core_user_tenants_tenant_id'), table_name='user_tenants', schema='core') + op.drop_index(op.f('ix_core_user_tenants_keycloak_user_id'), table_name='user_tenants', schema='core') + op.drop_index(op.f('ix_core_user_tenants_id'), table_name='user_tenants', schema='core') + op.drop_index(op.f('ix_core_user_tenants_company_id'), table_name='user_tenants', schema='core') + op.drop_table('user_tenants', schema='core') + op.drop_index('ix_user_company_permissions_composite', table_name='user_company_permissions', schema='core') + op.drop_index(op.f('ix_core_user_company_permissions_user_id'), table_name='user_company_permissions', schema='core') + op.drop_index(op.f('ix_core_user_company_permissions_tenant_id'), table_name='user_company_permissions', schema='core') + op.drop_index(op.f('ix_core_user_company_permissions_permission_id'), table_name='user_company_permissions', schema='core') + op.drop_index(op.f('ix_core_user_company_permissions_id'), table_name='user_company_permissions', schema='core') + op.drop_index(op.f('ix_core_user_company_permissions_company_id'), table_name='user_company_permissions', schema='core') + op.drop_table('user_company_permissions', schema='core') + op.drop_index(op.f('ix_core_company_roles_tenant_id'), table_name='company_roles', schema='core') + op.drop_index(op.f('ix_core_company_roles_id'), table_name='company_roles', schema='core') + op.drop_index(op.f('ix_core_company_roles_company_id'), table_name='company_roles', schema='core') + op.drop_index('ix_company_roles_company_id_is_active', table_name='company_roles', schema='core') + op.drop_table('company_roles', schema='core') + op.drop_index(op.f('ix_a76_vehicle_tenant_id'), table_name='vehicle', schema='a76') + op.drop_index(op.f('ix_a76_vehicle_company_id'), table_name='vehicle', schema='a76') + op.drop_table('vehicle', schema='a76') + op.drop_index(op.f('ix_a76_value_manifestations_tenant_id'), table_name='value_manifestations', schema='a76') + op.drop_index(op.f('ix_a76_value_manifestations_company_id'), table_name='value_manifestations', schema='a76') + op.drop_index('idx_value_manifestations_manifestation_number', table_name='value_manifestations', schema='a76') + op.drop_table('value_manifestations', schema='a76') + op.drop_index(op.f('ix_a76_us_tariff_fractions_tenant_id'), table_name='us_tariff_fractions', schema='a76') + op.drop_index(op.f('ix_a76_us_tariff_fractions_id'), table_name='us_tariff_fractions', schema='a76') + op.drop_index(op.f('ix_a76_us_tariff_fractions_company_id'), table_name='us_tariff_fractions', schema='a76') + op.drop_table('us_tariff_fractions', schema='a76') + op.drop_index(op.f('ix_a76_units_of_measure_general_tenant_id'), table_name='units_of_measure_general', schema='a76') + op.drop_index(op.f('ix_a76_units_of_measure_general_company_id'), table_name='units_of_measure_general', schema='a76') + op.drop_table('units_of_measure_general', schema='a76') + op.drop_index(op.f('ix_a76_units_of_measure_tenant_id'), table_name='units_of_measure', schema='a76') + op.drop_index(op.f('ix_a76_units_of_measure_company_id'), table_name='units_of_measure', schema='a76') + op.drop_table('units_of_measure', schema='a76') + op.drop_index(op.f('ix_a76_transporter_tenant_id'), table_name='transporter', schema='a76') + op.drop_index(op.f('ix_a76_transporter_company_id'), table_name='transporter', schema='a76') + op.drop_table('transporter', schema='a76') + op.drop_index(op.f('ix_a76_trailer_tenant_id'), table_name='trailer', schema='a76') + op.drop_index(op.f('ix_a76_trailer_company_id'), table_name='trailer', schema='a76') + op.drop_table('trailer', schema='a76') + op.drop_index(op.f('ix_a76_subassembly_entries_tenant_id'), table_name='subassembly_entries', schema='a76') + op.drop_index(op.f('ix_a76_subassembly_entries_company_id'), table_name='subassembly_entries', schema='a76') + op.drop_table('subassembly_entries', schema='a76') + op.drop_index(op.f('ix_a76_signatures_tenant_id'), table_name='signatures', schema='a76') + op.drop_index(op.f('ix_a76_signatures_company_id'), table_name='signatures', schema='a76') + op.drop_table('signatures', schema='a76') + op.drop_index(op.f('ix_a76_sectors_tenant_id'), table_name='sectors', schema='a76') + op.drop_index(op.f('ix_a76_sectors_company_id'), table_name='sectors', schema='a76') + op.drop_table('sectors', schema='a76') + op.drop_index(op.f('ix_a76_seal_tenant_id'), table_name='seal', schema='a76') + op.drop_index(op.f('ix_a76_seal_company_id'), table_name='seal', schema='a76') + op.drop_table('seal', schema='a76') + op.drop_index(op.f('ix_a76_previous_fractions_tenant_id'), table_name='previous_fractions', schema='a76') + op.drop_index(op.f('ix_a76_previous_fractions_company_id'), table_name='previous_fractions', schema='a76') + op.drop_table('previous_fractions', schema='a76') + op.drop_index(op.f('ix_a76_prevalidators_tenant_id'), table_name='prevalidators', schema='a76') + op.drop_index(op.f('ix_a76_prevalidators_company_id'), table_name='prevalidators', schema='a76') + op.drop_table('prevalidators', schema='a76') + op.drop_index(op.f('ix_a76_ports_tenant_id'), table_name='ports', schema='a76') + op.drop_index(op.f('ix_a76_ports_company_id'), table_name='ports', schema='a76') + op.drop_table('ports', schema='a76') + op.drop_index(op.f('ix_a76_permission_rule_octave_tenant_id'), table_name='permission_rule_octave', schema='a76') + op.drop_index(op.f('ix_a76_permission_rule_octave_company_id'), table_name='permission_rule_octave', schema='a76') + op.drop_table('permission_rule_octave', schema='a76') + op.drop_index(op.f('ix_a76_permission_rule_oct_tenant_id'), table_name='permission_rule_oct', schema='a76') + op.drop_index(op.f('ix_a76_permission_rule_oct_company_id'), table_name='permission_rule_oct', schema='a76') + op.drop_table('permission_rule_oct', schema='a76') + op.drop_index(op.f('ix_a76_packing_lists_tenant_id'), table_name='packing_lists', schema='a76') + op.drop_index(op.f('ix_a76_packing_lists_company_id'), table_name='packing_lists', schema='a76') + op.drop_table('packing_lists', schema='a76') + op.drop_index(op.f('ix_a76_packages_tenant_id'), table_name='packages', schema='a76') + op.drop_index(op.f('ix_a76_packages_company_id'), table_name='packages', schema='a76') + op.drop_table('packages', schema='a76') + op.drop_index(op.f('ix_a76_octave_balance_tenant_id'), table_name='octave_balance', schema='a76') + op.drop_index(op.f('ix_a76_octave_balance_company_id'), table_name='octave_balance', schema='a76') + op.drop_table('octave_balance', schema='a76') + op.drop_index(op.f('ix_a76_multi_currency_types_tenant_id'), table_name='multi_currency_types', schema='a76') + op.drop_index(op.f('ix_a76_multi_currency_types_company_id'), table_name='multi_currency_types', schema='a76') + op.drop_table('multi_currency_types', schema='a76') + op.drop_index(op.f('ix_a76_manifests_tenant_id'), table_name='manifests', schema='a76') + op.drop_index(op.f('ix_a76_manifests_company_id'), table_name='manifests', schema='a76') + op.drop_index('idx_manifests_manifest_number', table_name='manifests', schema='a76') + op.drop_table('manifests', schema='a76') + op.drop_index(op.f('ix_a76_manifest_drivers_tenant_id'), table_name='manifest_drivers', schema='a76') + op.drop_index(op.f('ix_a76_manifest_drivers_company_id'), table_name='manifest_drivers', schema='a76') + op.drop_index('idx_manifest_drivers_manifest_number', table_name='manifest_drivers', schema='a76') + op.drop_table('manifest_drivers', schema='a76') + op.drop_index(op.f('ix_a76_manifest_anexos_tenant_id'), table_name='manifest_anexos', schema='a76') + op.drop_index(op.f('ix_a76_manifest_anexos_company_id'), table_name='manifest_anexos', schema='a76') + op.drop_index('idx_manifest_anexos_consecutive', table_name='manifest_anexos', schema='a76') + op.drop_table('manifest_anexos', schema='a76') + op.drop_index(op.f('ix_a76_location_tenant_id'), table_name='location', schema='a76') + op.drop_index(op.f('ix_a76_location_company_id'), table_name='location', schema='a76') + op.drop_table('location', schema='a76') + op.drop_index(op.f('ix_a76_legends_tenant_id'), table_name='legends', schema='a76') + op.drop_index(op.f('ix_a76_legends_company_id'), table_name='legends', schema='a76') + op.drop_table('legends', schema='a76') + op.drop_index(op.f('ix_a76_item_presets_tenant_id'), table_name='item_presets', schema='a76') + op.drop_index(op.f('ix_a76_item_presets_company_id'), table_name='item_presets', schema='a76') + op.drop_table('item_presets', schema='a76') + op.drop_index(op.f('ix_a76_invoice_settings_tenant_id'), table_name='invoice_settings', schema='a76') + op.drop_index(op.f('ix_a76_invoice_settings_company_id'), table_name='invoice_settings', schema='a76') + op.drop_table('invoice_settings', schema='a76') + op.drop_index(op.f('ix_a76_invoice_header_tenant_id'), table_name='invoice_header', schema='a76') + op.drop_index(op.f('ix_a76_invoice_header_company_id'), table_name='invoice_header', schema='a76') + op.drop_table('invoice_header', schema='a76') + op.drop_index(op.f('ix_a76_inpc_tenant_id'), table_name='inpc', schema='a76') + op.drop_index(op.f('ix_a76_inpc_company_id'), table_name='inpc', schema='a76') + op.drop_table('inpc', schema='a76') + op.drop_index(op.f('ix_a76_identifiers_tenant_id'), table_name='identifiers', schema='a76') + op.drop_index(op.f('ix_a76_identifiers_company_id'), table_name='identifiers', schema='a76') + op.drop_table('identifiers', schema='a76') + op.drop_index(op.f('ix_a76_historical_tariff_fractions_tenant_id'), table_name='historical_tariff_fractions', schema='a76') + op.drop_index(op.f('ix_a76_historical_tariff_fractions_company_id'), table_name='historical_tariff_fractions', schema='a76') + op.drop_table('historical_tariff_fractions', schema='a76') + op.drop_index(op.f('ix_a76_fraction_rule_octave_tenant_id'), table_name='fraction_rule_octave', schema='a76') + op.drop_index(op.f('ix_a76_fraction_rule_octave_company_id'), table_name='fraction_rule_octave', schema='a76') + op.drop_table('fraction_rule_octave', schema='a76') + op.drop_index(op.f('ix_a76_fda_catalog_tenant_id'), table_name='fda_catalog', schema='a76') + op.drop_index(op.f('ix_a76_fda_catalog_fda_key'), table_name='fda_catalog', schema='a76') + op.drop_index(op.f('ix_a76_fda_catalog_description'), table_name='fda_catalog', schema='a76') + op.drop_index(op.f('ix_a76_fda_catalog_company_id'), table_name='fda_catalog', schema='a76') + op.drop_table('fda_catalog', schema='a76') + op.drop_index(op.f('ix_a76_exchange_rate_tenant_id'), table_name='exchange_rate', schema='a76') + op.drop_index(op.f('ix_a76_exchange_rate_company_id'), table_name='exchange_rate', schema='a76') + op.drop_table('exchange_rate', schema='a76') + op.drop_index(op.f('ix_a76_error_classifications_tenant_id'), table_name='error_classifications', schema='a76') + op.drop_index(op.f('ix_a76_error_classifications_company_id'), table_name='error_classifications', schema='a76') + op.drop_table('error_classifications', schema='a76') + op.drop_index(op.f('ix_a76_equivalency_items_tenant_id'), table_name='equivalency_items', schema='a76') + op.drop_index(op.f('ix_a76_equivalency_items_company_id'), table_name='equivalency_items', schema='a76') + op.drop_table('equivalency_items', schema='a76') + op.drop_index(op.f('ix_a76_electronic_notices_tenant_id'), table_name='electronic_notices', schema='a76') + op.drop_index(op.f('ix_a76_electronic_notices_company_id'), table_name='electronic_notices', schema='a76') + op.drop_table('electronic_notices', schema='a76') + op.drop_index(op.f('ix_a76_doda_tenant_id'), table_name='doda', schema='a76') + op.drop_index(op.f('ix_a76_doda_company_id'), table_name='doda', schema='a76') + op.drop_table('doda', schema='a76') + op.drop_index(op.f('ix_a76_document_types_digitization_tenant_id'), table_name='document_types_digitization', schema='a76') + op.drop_index(op.f('ix_a76_document_types_digitization_company_id'), table_name='document_types_digitization', schema='a76') + op.drop_index(op.f('ix_a76_document_types_digitization_code'), table_name='document_types_digitization', schema='a76') + op.drop_table('document_types_digitization', schema='a76') + op.drop_index(op.f('ix_a76_depreciation_catalog_tenant_id'), table_name='depreciation_catalog', schema='a76') + op.drop_index(op.f('ix_a76_depreciation_catalog_fraction'), table_name='depreciation_catalog', schema='a76') + op.drop_index(op.f('ix_a76_depreciation_catalog_description'), table_name='depreciation_catalog', schema='a76') + op.drop_index(op.f('ix_a76_depreciation_catalog_company_id'), table_name='depreciation_catalog', schema='a76') + op.drop_table('depreciation_catalog', schema='a76') + op.drop_index(op.f('ix_a76_customs_brokers_tenant_id'), table_name='customs_brokers', schema='a76') + op.drop_index(op.f('ix_a76_customs_brokers_company_id'), table_name='customs_brokers', schema='a76') + op.drop_table('customs_brokers', schema='a76') + op.drop_index(op.f('ix_a76_company_prevalidator_company_id'), table_name='company_prevalidator', schema='a76') + op.drop_table('company_prevalidator', schema='a76') + op.drop_index(op.f('ix_a76_company_electronic_agent_company_id'), table_name='company_electronic_agent', schema='a76') + op.drop_table('company_electronic_agent', schema='a76') + op.drop_index(op.f('ix_a76_company_digital_certificate_company_id'), table_name='company_digital_certificate', schema='a76') + op.drop_table('company_digital_certificate', schema='a76') + op.drop_index(op.f('ix_a76_company_cfdi_company_id'), table_name='company_cfdi', schema='a76') + op.drop_table('company_cfdi', schema='a76') + op.drop_index(op.f('ix_a76_company_certification_company_id'), table_name='company_certification', schema='a76') + op.drop_table('company_certification', schema='a76') + op.drop_index(op.f('ix_a76_company_address_company_id'), table_name='company_address', schema='a76') + op.drop_table('company_address', schema='a76') + op.drop_index(op.f('ix_a76_clients_and_providers_tenant_id'), table_name='clients_and_providers', schema='a76') + op.drop_index(op.f('ix_a76_clients_and_providers_company_id'), table_name='clients_and_providers', schema='a76') + op.drop_table('clients_and_providers', schema='a76') + op.drop_index(op.f('ix_a76_classification_concepts_tenant_id'), table_name='classification_concepts', schema='a76') + op.drop_index(op.f('ix_a76_classification_concepts_company_id'), table_name='classification_concepts', schema='a76') + op.drop_table('classification_concepts', schema='a76') + op.drop_index(op.f('ix_a76_canadian_tariff_fractions_tenant_id'), table_name='canadian_tariff_fractions', schema='a76') + op.drop_index(op.f('ix_a76_canadian_tariff_fractions_id'), table_name='canadian_tariff_fractions', schema='a76') + op.drop_index(op.f('ix_a76_canadian_tariff_fractions_fraction'), table_name='canadian_tariff_fractions', schema='a76') + op.drop_index(op.f('ix_a76_canadian_tariff_fractions_country_code'), table_name='canadian_tariff_fractions', schema='a76') + op.drop_index(op.f('ix_a76_canadian_tariff_fractions_company_id'), table_name='canadian_tariff_fractions', schema='a76') + op.drop_table('canadian_tariff_fractions', schema='a76') + op.drop_index(op.f('ix_a76_audit_logs_username'), table_name='audit_logs', schema='a76') + op.drop_index(op.f('ix_a76_audit_logs_timestamp'), table_name='audit_logs', schema='a76') + op.drop_index(op.f('ix_a76_audit_logs_tenant_id'), table_name='audit_logs', schema='a76') + op.drop_index(op.f('ix_a76_audit_logs_table_name'), table_name='audit_logs', schema='a76') + op.drop_index(op.f('ix_a76_audit_logs_system'), table_name='audit_logs', schema='a76') + op.drop_index(op.f('ix_a76_audit_logs_session_id'), table_name='audit_logs', schema='a76') + op.drop_index(op.f('ix_a76_audit_logs_reference'), table_name='audit_logs', schema='a76') + op.drop_index(op.f('ix_a76_audit_logs_record_id'), table_name='audit_logs', schema='a76') + op.drop_index(op.f('ix_a76_audit_logs_procedure'), table_name='audit_logs', schema='a76') + op.drop_index(op.f('ix_a76_audit_logs_operation_type'), table_name='audit_logs', schema='a76') + op.drop_index(op.f('ix_a76_audit_logs_date'), table_name='audit_logs', schema='a76') + op.drop_index(op.f('ix_a76_audit_logs_company_id'), table_name='audit_logs', schema='a76') + op.drop_index('idx_audit_username_date', table_name='audit_logs', schema='a76') + op.drop_index('idx_audit_table_record', table_name='audit_logs', schema='a76') + op.drop_index('idx_audit_system_timestamp', table_name='audit_logs', schema='a76') + op.drop_index('idx_audit_procedure_date', table_name='audit_logs', schema='a76') + op.drop_table('audit_logs', schema='a76') + op.drop_index(op.f('ix_a76_CompanyVU_company_id'), table_name='CompanyVU', schema='a76') + op.drop_table('CompanyVU', schema='a76') + op.drop_table('states', schema='public') + op.drop_table('code_pedimento_regimens', schema='public') + op.drop_index(op.f('ix_core_licenses_tenant_id'), table_name='licenses', schema='core') + op.drop_index(op.f('ix_core_licenses_id'), table_name='licenses', schema='core') + op.drop_table('licenses', schema='core') + op.drop_index(op.f('ix_core_license_usage_tenant_id'), table_name='license_usage', schema='core') + op.drop_index(op.f('ix_core_license_usage_id'), table_name='license_usage', schema='core') + op.drop_table('license_usage', schema='core') + op.drop_index(op.f('ix_a76_customs_broker_concepts_tenant_id'), table_name='customs_broker_concepts', schema='a76') + op.drop_table('customs_broker_concepts', schema='a76') + op.drop_index(op.f('ix_a76_company_tenant_id'), table_name='company', schema='a76') + op.drop_table('company', schema='a76') + op.drop_table('valuation_methods', schema='public') + op.drop_table('transport_types', schema='public') + op.drop_table('transport_modes', schema='public') + op.drop_table('trailer_type', schema='public') + op.drop_table('pedimento_transport_catalog', schema='public') + op.drop_table('pedimento_regimens', schema='public') + op.drop_table('pedimento_codes', schema='public') + op.drop_table('payment_methods', schema='public') + op.drop_table('material_types', schema='public') + op.drop_table('license_exceptions', schema='public') + op.drop_table('invoice_types', schema='public') + op.drop_table('incoterms', schema='public') + op.drop_table('identifiers', schema='public') + op.drop_table('customs_warehouses', schema='public') + op.drop_table('customs_sections', schema='public') + op.drop_table('currency_types', schema='public') + op.drop_index('ak_country_ame', table_name='countries', schema='public') + op.drop_table('countries', schema='public') + op.drop_index(op.f('ix_public_carta_porte_code'), table_name='carta_porte_codes', schema='public') + op.drop_table('carta_porte_codes', schema='public') + op.drop_index(op.f('ix_public_agency_tariff_codes_tariff_flag_code'), table_name='agency_tariff_codes', schema='public') + op.drop_index(op.f('ix_public_agency_tariff_codes_program_code'), table_name='agency_tariff_codes', schema='public') + op.drop_index(op.f('ix_public_agency_tariff_codes_agency_code'), table_name='agency_tariff_codes', schema='public') + op.drop_table('agency_tariff_codes', schema='public') + op.drop_index(op.f('ix_help_articles_uuid'), table_name='help_articles') + op.drop_index(op.f('ix_help_articles_slug'), table_name='help_articles') + op.drop_table('help_articles') + op.drop_index(op.f('ix_core_tenants_slug'), table_name='tenants', schema='core') + op.drop_index(op.f('ix_core_tenants_name'), table_name='tenants', schema='core') + op.drop_index(op.f('ix_core_tenants_id'), table_name='tenants', schema='core') + op.drop_table('tenants', schema='core') + op.drop_index(op.f('ix_core_permissions_module'), table_name='permissions', schema='core') + op.drop_index(op.f('ix_core_permissions_id'), table_name='permissions', schema='core') + op.drop_index(op.f('ix_core_permissions_code'), table_name='permissions', schema='core') + op.drop_table('permissions', schema='core') + op.drop_table('containers') + op.drop_table('unit_of_measure_oma', schema='a76') + op.drop_table('unit_of_measure_customs', schema='a76') + op.drop_table('unit_of_measure_american', schema='a76') + op.drop_table('unit_of_measure_ace', schema='a76') + op.drop_index(op.f('ix_a76_tariff_fractions_fraction'), table_name='tariff_fractions', schema='a76') + op.drop_index(op.f('ix_a76_tariff_fractions_code'), table_name='tariff_fractions', schema='a76') + op.drop_table('tariff_fractions', schema='a76') + op.drop_index(op.f('ix_a24_inv_aphis_catalog_company_id'), table_name='inv_aphis_catalog', schema='a24') + op.drop_table('inv_aphis_catalog', schema='a24') + # ### end Alembic commands ### diff --git a/backend/alembic/versions/7937209f9718_seed_initial_data.py b/backend/alembic/versions/7937209f9718_seed_initial_data.py index 0b11ab6c..84bafc11 100644 --- a/backend/alembic/versions/7937209f9718_seed_initial_data.py +++ b/backend/alembic/versions/7937209f9718_seed_initial_data.py @@ -44,7 +44,6 @@ from api.v1.modules.public.reference_data.pedimento_codes.seed import ( from api.v1.modules.public.reference_data.pedimento_regimens.seed import ( seed as pedimento_regimens_seed, ) -from api.v1.modules.a76.general_catalogs.sectors.seed import seed as sectors_seed from api.v1.modules.public.reference_data.states.seed import seed as states_seed from api.v1.modules.public.reference_data.transport_modes.seed import ( seed as transport_modes_seed, @@ -89,14 +88,14 @@ from api.v1.modules.core.permissions.seed import ( from api.v1.modules.public.reference_data.license_exceptions.seed import seed_license_exceptions from api.v1.modules.public.reference_data.agency_tariff_codes.seed import seed_agency_tariff_codes from api.v1.modules.public.reference_data.identifiers.seed import seed_identifiers -from api.v1.modules.public.reference_data.carta_porte.seed import seed_carta_porte +from api.v1.modules.public.reference_data.carta_porte_codes.seed import seed_carta_porte from sqlalchemy.orm import Session # revision identifiers, used by Alembic. revision: str = "7937209f9718" -down_revision: Union[str, Sequence[str], None] = None +down_revision: Union[str, Sequence[str], None] = "4ad64605fad2" branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = "4ad64605fad2" def upgrade() -> None: @@ -302,9 +301,6 @@ def upgrade() -> None: """ ) - # Sectors se siembran por compañía en _seed_company_data - # (a76.sectors requiere tenant_id/company_id — no aplica en seed global) - values_tm = ", ".join( [ f"('{key}', '{name.replace(chr(39), chr(39)*2)}')" @@ -547,8 +543,7 @@ def downgrade() -> None: op.drop_table("valuation_methods", schema="public") op.drop_table("transport_types", schema="public") op.drop_table("trailer_types", schema="public") - op.drop_table("transport_modes", schema="public") - op.drop_table("sectors", schema="a76") + op.drop_table("transport_modes", schema="public") op.drop_table("payment_methods", schema="public") op.drop_table("material_types", schema="public") op.drop_table("invoice_types", schema="public") diff --git a/backend/alembic/versions/9f3c2d1b7a11_drop_legacy_return_counters.py b/backend/alembic/versions/9f3c2d1b7a11_drop_legacy_return_counters.py new file mode 100644 index 00000000..416503e5 --- /dev/null +++ b/backend/alembic/versions/9f3c2d1b7a11_drop_legacy_return_counters.py @@ -0,0 +1,45 @@ +"""drop_legacy_return_counters + +Revision ID: 9f3c2d1b7a11 +Revises: 4ad64605fad2 +Create Date: 2026-03-20 15:10:00.000000 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = "9f3c2d1b7a11" +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: + """Drop legacy quantity counters replaced by balance/discharge ledger.""" + op.drop_column("item_line_quantities", "quantity_returned_temp", schema="a76") + op.drop_column("item_line_quantities", "quantity_returned", schema="a76") + op.drop_column("item_line_quantities", "quantity_existence", schema="a76") + + +def downgrade() -> None: + """Restore legacy quantity counters.""" + op.add_column( + "item_line_quantities", + sa.Column("quantity_existence", sa.Numeric(precision=19, scale=8), nullable=True), + schema="a76", + ) + op.add_column( + "item_line_quantities", + sa.Column("quantity_returned", sa.Numeric(precision=19, scale=8), nullable=True), + schema="a76", + ) + op.add_column( + "item_line_quantities", + sa.Column("quantity_returned_temp", sa.Numeric(precision=19, scale=8), nullable=True), + schema="a76", + ) diff --git a/backend/alembic/versions/bccb7f8986c7_iva_factor.py b/backend/alembic/versions/bccb7f8986c7_iva_factor.py new file mode 100644 index 00000000..2c31ffb5 --- /dev/null +++ b/backend/alembic/versions/bccb7f8986c7_iva_factor.py @@ -0,0 +1,45 @@ +"""iva_factor + +Revision ID: bccb7f8986c7 +Revises: 9f3c2d1b7a11 +Create Date: 2026-03-23 09:44:02.275257 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'bccb7f8986c7' +down_revision: Union[str, Sequence[str], None] = '9f3c2d1b7a11' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_public_carta_porte_code'), table_name='carta_porte_codes') + op.create_index(op.f('ix_public_carta_porte_codes_code'), 'carta_porte_codes', ['code'], unique=False, schema='public') + op.alter_column('invoice_financials', 'iva_factor', + existing_type=sa.VARCHAR(length=10), + type_=sa.Numeric(precision=23, scale=8), + postgresql_using='iva_factor::numeric(23,8)', + existing_nullable=True, + schema='a76') + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('invoice_financials', 'iva_factor', + existing_type=sa.Numeric(precision=23, scale=8), + type_=sa.VARCHAR(length=10), + existing_nullable=True, + schema='a76') + op.drop_index(op.f('ix_public_carta_porte_codes_code'), table_name='carta_porte_codes', schema='public') + op.create_index(op.f('ix_public_carta_porte_code'), 'carta_porte_codes', ['code'], unique=False) + # ### end Alembic commands ### diff --git a/backend/api/v1/modules/a24/inv/inv_aphis/inv_aphis_catalog/__init__.py b/backend/api/v1/modules/a24/inv/inv_aphis/inv_aphis_catalog/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/backend/api/v1/modules/a24/inv/inv_aphis/inv_aphis_catalog/dto.py b/backend/api/v1/modules/a24/inv/inv_aphis/inv_aphis_catalog/dto.py new file mode 100644 index 00000000..7ece8ee1 --- /dev/null +++ b/backend/api/v1/modules/a24/inv/inv_aphis/inv_aphis_catalog/dto.py @@ -0,0 +1,57 @@ +from pydantic import BaseModel, ConfigDict, field_validator +from typing import Optional, List, Dict, Any +from datetime import date + + +class AphisCatalogDTO(BaseModel): + id: Optional[int] = None + + # --- Pestaña 1: General --- + program_code: Optional[str] = None + processing_code: Optional[str] = None + aphis_type: Optional[str] = None + disclaimer: Optional[str] = None + electronic_image: Optional[str] = None + confidential: Optional[str] = None + global_product_id: Optional[str] = None + intended_use_code: Optional[str] = None + intended_use_description: Optional[str] = None + item_type: Optional[str] = None + product_code: Optional[str] = None + product_code_2: Optional[str] = None + product_code_3: Optional[str] = None + scientific_genus_name: Optional[str] = None + scientific_species_name: Optional[str] = None + scientific_sub_species_name: Optional[str] = None + common_name_specific: Optional[str] = None + common_name_general: Optional[str] = None + signed_doc: Optional[str] = None + signed_doc_date: Optional[date] = None + signed_doc_id: Optional[str] = None + invoice_number: Optional[str] = None + quantity_1: Optional[str] = None + quantity_2: Optional[str] = None + quantity_3: Optional[str] = None + inspection: Optional[str] = None + inspection_date: Optional[date] = None + inspection_loc_date: Optional[date] = None + inspection_location: Optional[str] = None + country_production: Optional[str] = None + country_source: Optional[str] = None + + # --- Pestañas 2-7: Detalles (Listas de objetos) --- + characteristics: Optional[List[Dict[str, Any]]] = [] + pitems: Optional[List[Dict[str, Any]]] = [] + lpcos: Optional[List[Dict[str, Any]]] = [] + entities: Optional[List[Dict[str, Any]]] = [] + containers: Optional[List[Dict[str, Any]]] = [] + routing: Optional[List[Dict[str, Any]]] = [] + + model_config = ConfigDict(from_attributes=True) + + @field_validator("signed_doc_date", "inspection_date", "inspection_loc_date", mode="before") + @classmethod + def empty_to_none(cls, v): + if v == "": + return None + return v diff --git a/backend/api/v1/modules/a24/inv/inv_aphis/inv_aphis_catalog/models.py b/backend/api/v1/modules/a24/inv/inv_aphis/inv_aphis_catalog/models.py new file mode 100644 index 00000000..ef494b44 --- /dev/null +++ b/backend/api/v1/modules/a24/inv/inv_aphis/inv_aphis_catalog/models.py @@ -0,0 +1,61 @@ +from typing import Optional, List, Dict, Any +from datetime import date +from sqlalchemy import Integer, String, Date, PrimaryKeyConstraint, JSON +from sqlalchemy.orm import Mapped, mapped_column +from core.database import Base + + +class AphisCatalog(Base): + """ + Catálogo global de registros APHIS por empresa. + Soporta las 7 pestañas de información (General + 6 detalles via JSON). + """ + __tablename__ = "inv_aphis_catalog" + __table_args__ = ( + PrimaryKeyConstraint("id", name="inv_aphis_catalog_pkey"), + {"schema": "a24", "extend_existing": True}, + ) + + id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) + company_id: Mapped[int] = mapped_column(Integer, nullable=False, index=True) + + # --- Pestaña 1: General (Campos principales) --- + program_code: Mapped[Optional[str]] = mapped_column(String(10)) + processing_code: Mapped[Optional[str]] = mapped_column(String(10)) + aphis_type: Mapped[Optional[str]] = mapped_column(String(10)) + disclaimer: Mapped[Optional[str]] = mapped_column(String(10)) + electronic_image: Mapped[Optional[str]] = mapped_column(String(50)) + confidential: Mapped[Optional[str]] = mapped_column(String(1)) + global_product_id: Mapped[Optional[str]] = mapped_column(String(100)) + intended_use_code: Mapped[Optional[str]] = mapped_column(String(10)) + intended_use_description: Mapped[Optional[str]] = mapped_column(String(200)) + item_type: Mapped[Optional[str]] = mapped_column(String(20)) + product_code: Mapped[Optional[str]] = mapped_column(String(20)) + product_code_2: Mapped[Optional[str]] = mapped_column(String(20)) + product_code_3: Mapped[Optional[str]] = mapped_column(String(20)) + scientific_genus_name: Mapped[Optional[str]] = mapped_column(String(100)) + scientific_species_name: Mapped[Optional[str]] = mapped_column(String(100)) + scientific_sub_species_name: Mapped[Optional[str]] = mapped_column(String(100)) + common_name_specific: Mapped[Optional[str]] = mapped_column(String(200)) + common_name_general: Mapped[Optional[str]] = mapped_column(String(200)) + signed_doc: Mapped[Optional[str]] = mapped_column(String(100)) + signed_doc_date: Mapped[Optional[date]] = mapped_column(Date) + signed_doc_id: Mapped[Optional[str]] = mapped_column(String(50)) + invoice_number: Mapped[Optional[str]] = mapped_column(String(50)) + quantity_1: Mapped[Optional[str]] = mapped_column(String(50)) + quantity_2: Mapped[Optional[str]] = mapped_column(String(50)) + quantity_3: Mapped[Optional[str]] = mapped_column(String(50)) + inspection: Mapped[Optional[str]] = mapped_column(String(200)) + inspection_date: Mapped[Optional[date]] = mapped_column(Date) + inspection_loc_date: Mapped[Optional[date]] = mapped_column(Date) + inspection_location: Mapped[Optional[str]] = mapped_column(String(200)) + country_production: Mapped[Optional[str]] = mapped_column(String(3)) + country_source: Mapped[Optional[str]] = mapped_column(String(3)) + + # --- Pestañas 2-7: Detalles (Almacenados como JSON por flexibilidad) --- + characteristics: Mapped[Optional[List[Dict[str, Any]]]] = mapped_column(JSON, default=list) + pitems: Mapped[Optional[List[Dict[str, Any]]]] = mapped_column(JSON, default=list) + lpcos: Mapped[Optional[List[Dict[str, Any]]]] = mapped_column(JSON, default=list) + entities: Mapped[Optional[List[Dict[str, Any]]]] = mapped_column(JSON, default=list) + containers: Mapped[Optional[List[Dict[str, Any]]]] = mapped_column(JSON, default=list) + routing: Mapped[Optional[List[Dict[str, Any]]]] = mapped_column(JSON, default=list) diff --git a/backend/api/v1/modules/a24/inv/inv_aphis/inv_aphis_catalog/router.py b/backend/api/v1/modules/a24/inv/inv_aphis/inv_aphis_catalog/router.py new file mode 100644 index 00000000..3e5e21d5 --- /dev/null +++ b/backend/api/v1/modules/a24/inv/inv_aphis/inv_aphis_catalog/router.py @@ -0,0 +1,62 @@ +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session +from typing import List +from core.database import get_core_db +from .models import AphisCatalog +from .dto import AphisCatalogDTO + +router = APIRouter(prefix="/aphis-catalog", tags=["APHIS Catalog"]) + + +@router.get("/", response_model=List[AphisCatalogDTO]) +def list_aphis_catalog(company_id: int, db: Session = Depends(get_core_db)): + records = ( + db.query(AphisCatalog) + .filter(AphisCatalog.company_id == company_id) + .order_by(AphisCatalog.id) + .all() + ) + return records + + +@router.post("/", response_model=AphisCatalogDTO) +def create_aphis_catalog(data: AphisCatalogDTO, company_id: int, db: Session = Depends(get_core_db)): + payload = data.model_dump(exclude={"id"}) + record = AphisCatalog(**payload, company_id=company_id) + db.add(record) + db.commit() + db.refresh(record) + return record + + +@router.put("/{record_id}", response_model=AphisCatalogDTO) +def update_aphis_catalog( + record_id: int, data: AphisCatalogDTO, company_id: int, db: Session = Depends(get_core_db) +): + record = ( + db.query(AphisCatalog) + .filter(AphisCatalog.id == record_id, AphisCatalog.company_id == company_id) + .first() + ) + if not record: + raise HTTPException(status_code=404, detail="Registro no encontrado") + + for field, value in data.model_dump(exclude={"id"}).items(): + setattr(record, field, value) + + db.commit() + db.refresh(record) + return record + + +@router.delete("/{record_id}", status_code=204) +def delete_aphis_catalog(record_id: int, company_id: int, db: Session = Depends(get_core_db)): + record = ( + db.query(AphisCatalog) + .filter(AphisCatalog.id == record_id, AphisCatalog.company_id == company_id) + .first() + ) + if not record: + raise HTTPException(status_code=404, detail="Registro no encontrado") + db.delete(record) + db.commit() diff --git a/backend/api/v1/modules/a24/inv/inv_parts/models.py b/backend/api/v1/modules/a24/inv/inv_parts/models.py index 5ae08e67..48bd6002 100644 --- a/backend/api/v1/modules/a24/inv/inv_parts/models.py +++ b/backend/api/v1/modules/a24/inv/inv_parts/models.py @@ -138,6 +138,7 @@ class InvPart(Base, TenantScopedMixin, TimestampMixin): # --- NUEVOS CAMPOS EXTENSION --- agency_code_definition: Mapped[Optional[str]] = mapped_column(String(50)) # Fila 7 carta_porte: Mapped[Optional[str]] = mapped_column(String(100)) # Fila 10 + client_part_names: Mapped[Optional[list]] = mapped_column(JSONB, nullable=True, default=[]) # Fila 5 part_identifiers: Mapped[Optional[list]] = mapped_column(JSONB, nullable=True, default=[]) # Fila 9 substitute_parts: Mapped[Optional[list]] = mapped_column(JSONB, nullable=True, default=[]) # Pestaña Continuación diff --git a/backend/api/v1/modules/a24/router.py b/backend/api/v1/modules/a24/router.py index f011e110..dca3877f 100644 --- a/backend/api/v1/modules/a24/router.py +++ b/backend/api/v1/modules/a24/router.py @@ -8,6 +8,10 @@ from fastapi import APIRouter from .fa.fa_classes.routes import router as fa_classes_router from .fa.fa_item_lines.routes import router as fa_item_lines_router from .inv.part_countries.routes import router as part_countries_router +from .inv.inv_aphis.inv_aphis_catalog.router import router as aphis_catalog_router + +# Importar modelo para que SQLAlchemy cree la tabla automáticamente +import api.v1.modules.a24.inv.inv_aphis.inv_aphis_catalog.models # noqa: F401 # Router principal de A24 @@ -21,3 +25,5 @@ router.include_router( # Registrar routers de INV (Inventory) router.include_router(part_countries_router, prefix="/a24", tags=["a24 / inv / part-countries"]) +router.include_router(aphis_catalog_router, prefix="/a24", tags=["a24 / inv / aphis-catalog"]) + diff --git a/backend/api/v1/modules/a76/audit_log/register.py b/backend/api/v1/modules/a76/audit_log/register.py new file mode 100644 index 00000000..054dfb39 --- /dev/null +++ b/backend/api/v1/modules/a76/audit_log/register.py @@ -0,0 +1,150 @@ +# Importar modelos para Audit Log +from api.v1.modules.a76.pedmientos.models.pedimentos import Pedimentos +from api.v1.modules.a76.invoices.models import InvoiceHeader, InvoiceSalesDetails +from api.v1.modules.a76.audit_log.events import register_audit_listeners + +# Core Modules +from api.v1.modules.a76.clients_and_providers.models import ClientProvider +from api.v1.modules.a76.customs_brokers.models import CustomsBroker +from api.v1.modules.a76.parts.models import Part +from api.v1.modules.a76.items.models import LineItem +from api.v1.modules.a76.general_catalogs.company.models import Company + +# Reference Data +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.public.reference_data.customs_sections.models import CustomsSection +from api.v1.modules.public.reference_data.customs_warehouses.models import ( + CustomsWarehouse, +) +from api.v1.modules.public.reference_data.incoterms.models import Incoterm +from api.v1.modules.public.reference_data.invoice_types.models import InvoiceType +from api.v1.modules.public.reference_data.material_types.models import MaterialType +from api.v1.modules.public.reference_data.payment_methods.models import PaymentMethod +from api.v1.modules.public.reference_data.pedimento_transport_catalog.models import ( + PedimentoTransportCatalog, +) +from api.v1.modules.public.reference_data.pedimento_codes.models import PedimentoCode +from api.v1.modules.public.reference_data.pedimento_regimens.models import ( + RegimenPedimento, +) +from api.v1.modules.public.reference_data.states.models import State +from api.v1.modules.public.reference_data.transport_modes.models import TransportMode +from api.v1.modules.public.reference_data.transport_types.models import TransportType +from api.v1.modules.public.reference_data.valuation_methods.models import ( + ValuationMethod, +) +from api.v1.modules.public.reference_data.license_exceptions.models import LicenseException +from api.v1.modules.public.reference_data.agency_tariff_codes.models import AgencyTariffCode +from api.v1.modules.public.reference_data.identifiers.models import IdentifierCatalog +from api.v1.modules.public.reference_data.carta_porte_codes.models import CartaPorte +from api.v1.modules.a76.general_catalogs.units_of_measure.models import UnitOfMeasure +from api.v1.modules.a76.general_catalogs.exchange_rate.models import ExchangeRate +from api.v1.modules.a76.general_catalogs.identifiers.models import Identifier +from api.v1.modules.a76.classes.models import Class +from api.v1.modules.a76.general_catalogs.classification_concepts.models import ( + ClassificationConcept, +) +from api.v1.modules.a76.general_catalogs.concepts.models import Concept +from api.v1.modules.a76.general_catalogs.customs_broker_concepts.models import ( + CustomsBrokerConcept, +) +from api.v1.modules.a76.general_catalogs.depreciation_catalog.models import ( + DepreciationCatalog, +) +from api.v1.modules.a76.general_catalogs.doda.models import Doda +from api.v1.modules.a76.general_catalogs.electronic_notices.models import ( + ElectronicNotice, +) +from api.v1.modules.a76.general_catalogs.equivalencies.models import Equivalency +from api.v1.modules.a76.general_catalogs.error_catalogs.models import ErrorCatalog +from api.v1.modules.a76.general_catalogs.fda_catalog.models import FDACatalog +from api.v1.modules.a76.general_catalogs.inpc.models import INPC +from api.v1.modules.a76.general_catalogs.legends.models import Legend +from api.v1.modules.a76.general_catalogs.multi_currency_types.models import ( + MultiCurrencyType, +) +from api.v1.modules.a76.general_catalogs.packages.models import Package +from api.v1.modules.a76.general_catalogs.ports.models import Port +from api.v1.modules.a76.general_catalogs.location.models import Location, FaLocationExt +from api.v1.modules.a76.general_catalogs.prevalidators.models import Prevalidator +from api.v1.modules.a76.general_catalogs.seal.models import Seal +from api.v1.modules.a76.general_catalogs.signatures.models import Signature +from api.v1.modules.a76.general_catalogs.fractions.tariff_fractions.models import ( + TariffFraction, +) +from api.v1.modules.a76.general_catalogs.unit_conversions.models import UnitConversion +from api.v1.modules.a76.general_catalogs.fractions.us_tariff_fractions.models import ( + USTariffFraction, +) +from api.v1.modules.a76.general_catalogs.sectors.models import Sector +from api.v1.modules.a76.transportation.trailers.models import Trailer +from api.v1.modules.a76.transportation.transporters.models import Transporter +from api.v1.modules.a76.transportation.vehicles.models import Vehicle + + +# Registrar Listeners de Auditoría +def register_audit(): + register_audit_listeners( + [ + # Core Transactions + Pedimentos, + InvoiceHeader, + InvoiceSalesDetails, + LineItem, + # Sidebar Core Modules + ClientProvider, + CustomsBroker, + Part, + Company, + # Transportation Modules + Trailer, + Transporter, + Vehicle, + # Reference Data + Country, + CurrencyType, + CustomsSection, + CustomsWarehouse, + Incoterm, + InvoiceType, + MaterialType, + PaymentMethod, + PedimentoTransportCatalog, + PedimentoCode, + RegimenPedimento, + Sector, + State, + TransportMode, + TransportType, + ValuationMethod, + LicenseException, + AgencyTariffCode, + IdentifierCatalog, + CartaPorte, + UnitOfMeasure, + ExchangeRate, + Identifier, + Class, + ClassificationConcept, + Concept, + CustomsBrokerConcept, + DepreciationCatalog, + Doda, + ElectronicNotice, + Equivalency, + ErrorCatalog, + FDACatalog, + INPC, + Legend, + MultiCurrencyType, + Package, + Port, + Prevalidator, + Seal, + Signature, + TariffFraction, + UnitConversion, + USTariffFraction, + ] + ) \ No newline at end of file diff --git a/backend/api/v1/modules/a76/invoices/common/common_validators.py b/backend/api/v1/modules/a76/invoices/common/common_validators.py index 5447dd48..70f7bb5f 100644 --- a/backend/api/v1/modules/a76/invoices/common/common_validators.py +++ b/backend/api/v1/modules/a76/invoices/common/common_validators.py @@ -395,7 +395,7 @@ def validate_common( value=invoice.document_type, ) else: - if invoice.document_type == "IMD": + if invoice.document_type == "IMD" and invoice.invoice_type.upper() != "DEF": errors.add_error( field="document_type", message="El Tipo de Documento no puede ser 'IMD' a menos que sea un Cambio de Régimen.", diff --git a/backend/api/v1/modules/a76/invoices/exports/process/sub_process/finalize_invoice.py b/backend/api/v1/modules/a76/invoices/exports/process/sub_process/finalize_invoice.py index 6aea9c0a..7ea735dc 100644 --- a/backend/api/v1/modules/a76/invoices/exports/process/sub_process/finalize_invoice.py +++ b/backend/api/v1/modules/a76/invoices/exports/process/sub_process/finalize_invoice.py @@ -277,7 +277,7 @@ def finalize_invoice_with_discharge( if to_discharge: # Write BalanceMovement (CONSUMPTION) + DischargeHeader + DischargeDetail register_discharge_ledger(db, invoice, to_discharge) - # Update quantity_returned / value_returned on the import lines + # Update returned values on the import lines register_import_discharge(db, invoice, to_discharge) register_discharge_series(db, invoice, to_discharge) diff --git a/backend/api/v1/modules/a76/invoices/exports/process/sub_process/register_discharge_ledger.py b/backend/api/v1/modules/a76/invoices/exports/process/sub_process/register_discharge_ledger.py index 9f86550f..b48f4a8f 100644 --- a/backend/api/v1/modules/a76/invoices/exports/process/sub_process/register_discharge_ledger.py +++ b/backend/api/v1/modules/a76/invoices/exports/process/sub_process/register_discharge_ledger.py @@ -167,6 +167,13 @@ def register_discharge_ledger( # lot_consumed_total == consume for single-lot entries (most cases) value_me = _proportional_value(consume, consume, lot.value_me) value_mn = _proportional_value(consume, consume, lot.value_mn) + imp_qty = import_line_obj.quantity if import_line_obj else None + imp_total_qty = imp_qty.quantity if imp_qty else None + net_weight = _proportional_qty( + consume, + imp_qty.net_weight if imp_qty else None, + imp_total_qty, + ) movement = BalanceMovement( tenant_id=export_invoice.tenant_id, @@ -178,6 +185,7 @@ def register_discharge_ledger( quantity=consume, value_me=value_me, value_mn=value_mn, + net_weight=net_weight, source_invoice_id=export_invoice.id, source_item_line_id=export_line_id, order_peps=0, # placeholder — set after flush (rule 4) @@ -194,10 +202,7 @@ def register_discharge_ledger( # ── 3. DischargeDetail ───────────────────────────────────────── # Denormalized fields expected by reports: imp_cust = import_line_obj.customs if import_line_obj else None - imp_qty = import_line_obj.quantity if import_line_obj else None - imp_total_qty = imp_qty.quantity if imp_qty else None - - net_weight = _proportional_qty(consume, imp_qty.net_weight if imp_qty else None, imp_total_qty) + # Reuse already computed net_weight for consistency with movement. gross_weight = _proportional_qty(consume, imp_qty.gross_weight if imp_qty else None, imp_total_qty) detail = DischargeDetail( diff --git a/backend/api/v1/modules/a76/invoices/exports/process/sub_process/register_import_discharge.py b/backend/api/v1/modules/a76/invoices/exports/process/sub_process/register_import_discharge.py index d9409bd1..96cbf04b 100644 --- a/backend/api/v1/modules/a76/invoices/exports/process/sub_process/register_import_discharge.py +++ b/backend/api/v1/modules/a76/invoices/exports/process/sub_process/register_import_discharge.py @@ -6,8 +6,8 @@ quantities and values consumed by this export invoice. For each entry in ``to_discharge`` (QSaldoActual in the legacy) the routine: · Looks up the source import invoice header (TEM → QFacImp, DEF → QFacImpDef). · Looks up the corresponding import line item. - · Increments quantity_returned, value_returned_mxn, value_returned_usd on the - import line's quantity/financial sub-records. + · Increments value_returned_mxn, value_returned_usd on the import line's + financial sub-record. · For TEM invoices, also calculates vat_used_mxn / vat_used_usd when the import invoice date is on or after 2014-12-31 (Clarion date 78165). @@ -148,9 +148,7 @@ def register_import_discharge( returned_mn = qty_used * value_mn / original_qty returned_usd = qty_used * value_usd / original_qty - # ── Accumulate returned qty and value ───────────────────────────────── - qty_rec.quantity_returned = (qty_rec.quantity_returned or Decimal(0)) + qty_used - + # ── Accumulate returned value ────────────────────────────────────────── fin.value_returned_mxn = (fin.value_returned_mxn or Decimal(0)) + returned_mn fin.value_returned_usd = (fin.value_returned_usd or Decimal(0)) + returned_usd diff --git a/backend/api/v1/modules/a76/invoices/exports/process/sub_process/review_qty_vs_weight.py b/backend/api/v1/modules/a76/invoices/exports/process/sub_process/review_qty_vs_weight.py index 7dad03d8..3aa3b74e 100644 --- a/backend/api/v1/modules/a76/invoices/exports/process/sub_process/review_qty_vs_weight.py +++ b/backend/api/v1/modules/a76/invoices/exports/process/sub_process/review_qty_vs_weight.py @@ -61,7 +61,7 @@ def review_qty_vs_weight( continue qty = line.quantity.quantity or Decimal(0) - net_weight = getattr(line.quantity.quantity, None) or Decimal(0) + net_weight = line.quantity.net_weight or Decimal(0) if net_weight != qty: errors.add_error( diff --git a/backend/api/v1/modules/a76/invoices/exports/revert/main_process.py b/backend/api/v1/modules/a76/invoices/exports/revert/main_process.py new file mode 100644 index 00000000..f264ea27 --- /dev/null +++ b/backend/api/v1/modules/a76/invoices/exports/revert/main_process.py @@ -0,0 +1,373 @@ +import datetime +from decimal import Decimal +from typing import List, Optional + +from sqlalchemy import select +from sqlalchemy.orm import Session + +from api.v1.modules.a24.discharges.models import ( + DischargeDetail, + DischargeHeader, + DischargeStatus, +) +from api.v1.modules.a24.balance_movements.models import BalanceMovement, MovementType +from api.v1.modules.a76.invoices.models import ( + InvoiceHeader, + InvoiceStatus, + OperationType, +) +from api.v1.modules.a76.items.models import LineItem +from api.v1.modules.a76.items.series.models import Serie +from core.exceptions import ErrorCollector + +_VAT_CUTOFF = datetime.date(2014, 12, 31) # Clarion day 78165 + + +def _validate_regime_change_definitive_invoice_exists( + db: Session, + invoice: InvoiceHeader, + errors: ErrorCollector, +) -> None: + """ + Clarion mapping: + If EqiFex:EsCambioRegimen='S' then count QFacImpDef where + FacturaImpoDef = FacturaExpo and ProvImpoDefCR='C'. + + Python approximation: + Search an import invoice with same invoice_number and invoice_type='IMD'. + """ + if not (invoice.compliance_mx and invoice.compliance_mx.is_regime_change): + return + + if not invoice.invoice_number: + return + + exists = ( + db.query(InvoiceHeader.id) + .filter( + InvoiceHeader.tenant_id == invoice.tenant_id, + InvoiceHeader.company_id == invoice.company_id, + InvoiceHeader.operation_type == OperationType.IMP, + InvoiceHeader.invoice_type == "DEF", # Importación Definitiva generada + InvoiceHeader.invoice_number == invoice.invoice_number, + ) + .first() + is not None + ) + + if exists: + errors.add_error( + field="invoice_number", + message=( + "Error: Existe una Factura de Importación Definitiva a partir " + "de esta Factura." + ), + solution=[ + f"Desactualizar y borrar la Factura: {invoice.invoice_number} " + "de Importación Definitiva." + ], + code="DEFINITIVE_IMPORT_ALREADY_EXISTS", + value=invoice.invoice_number, + ) + + +def _todo_check_access_lock(invoice: InvoiceHeader) -> None: + # TODO: DO VALIDACION_USO_FACTURA_OTRO_USUARIO + # Clarion block against GAccesosModulos (security lock by terminal/user). + _ = invoice + + + +def _return_discharged_quantities( + db: Session, + export_invoice: InvoiceHeader, + lines: List[LineItem], +) -> None: + """ + REGRESA_CANT_RETORNADAS + Returns discharged quantities/values to their source import invoice lines. + + Uses the new specialized discharge tables: + a24.discharge_header + a24.discharge_detail + + We only revert details that belong to this export invoice and are applied. + This guarantees parity with the actual discharge ledger (instead of relying + on editable UI references in fa_data). + """ + _ = lines # source of truth is discharge tables + + details: List[DischargeDetail] = ( + db.query(DischargeDetail) + .join(DischargeHeader, DischargeHeader.id == DischargeDetail.discharge_header_id) + .filter( + DischargeHeader.source_invoice_id == export_invoice.id, + DischargeHeader.status == DischargeStatus.APPLIED, + DischargeDetail.tenant_id == export_invoice.tenant_id, + DischargeDetail.company_id == export_invoice.company_id, + ) + .all() + ) + + for detail in details: + qty_exported = Decimal(str(detail.quantity_discharged or 0)) + if qty_exported <= 0: + continue + + import_line = db.get(LineItem, detail.import_item_line_id) + if ( + import_line is None + or import_line.quantity is None + or import_line.financial is None + ): + continue + + import_invoice = db.get(InvoiceHeader, import_line.invoice_id) + if import_invoice is None: + continue + + qty_rec = import_line.quantity + fin = import_line.financial + original_qty = Decimal(str(qty_rec.quantity or 0)) + if original_qty == 0: + continue + + value_mxn = Decimal(str(fin.value_mxn or 0)) + value_usd = Decimal(str(fin.value_usd or 0)) + returned_mxn = qty_exported * value_mxn / original_qty + returned_usd = qty_exported * value_usd / original_qty + + # Reverse monetary returned values + # Clarion shows '-' for TEM and '+' for DEF; in the current ledger migration, + # register_import_discharge adds both TEM/DEF, so revert subtracts both. + fin.value_returned_mxn = (fin.value_returned_mxn or Decimal(0)) - returned_mxn + fin.value_returned_usd = (fin.value_returned_usd or Decimal(0)) - returned_usd + + # TEM VAT recalculation by header date cutoff + if (detail.procedence or "").upper() == "TEM": + inv_date = import_invoice.invoice_date + if isinstance(inv_date, datetime.datetime): + inv_date = inv_date.date() + if inv_date and inv_date >= _VAT_CUTOFF: + iva_factor = Decimal(0) + if import_invoice.financials and import_invoice.financials.iva_factor: + iva_factor = Decimal(str(import_invoice.financials.iva_factor)) + fin.vat_used_mxn = (fin.value_returned_mxn or Decimal(0)) * iva_factor / 100 + fin.vat_used_usd = (fin.value_returned_usd or Decimal(0)) * iva_factor / 100 + else: + fin.vat_used_mxn = Decimal(0) + fin.vat_used_usd = Decimal(0) + + +def _unmark_returned_series( + db: Session, + export_invoice: InvoiceHeader, + lines: List[LineItem], +) -> None: + """ + DESMARCA_SERIES_RETORNADAS + Resets import serie discharge marks that were set by this export invoice. + + New data-source logic: + - Uses a24.discharge_header/detail to identify which import lines were + consumed by this export invoice. + - Uses a76.item_line_series on export lines (discharge=True) to resolve the + import serie row (serie_row) or by serial number fallback. + """ + line_ids = [ln.id for ln in lines if ln.id is not None] + if not line_ids: + return + + # All discharge details belonging to this export invoice, grouped by export line. + details: List[DischargeDetail] = ( + db.query(DischargeDetail) + .join(DischargeHeader, DischargeHeader.id == DischargeDetail.discharge_header_id) + .filter( + DischargeHeader.source_invoice_id == export_invoice.id, + DischargeHeader.status == DischargeStatus.APPLIED, + DischargeDetail.export_item_line_id.in_(line_ids), + DischargeDetail.tenant_id == export_invoice.tenant_id, + DischargeDetail.company_id == export_invoice.company_id, + ) + .all() + ) + + import_lines_by_export: dict[int, set[int]] = {} + for d in details: + if d.export_item_line_id is None: + continue + import_lines_by_export.setdefault(d.export_item_line_id, set()).add(d.import_item_line_id) + + if not import_lines_by_export: + return + + # Export series marked for discharge (equivalent to SerExpo.Marca = 1) + export_series: List[Serie] = ( + db.execute( + select(Serie).where( + Serie.line_item_id.in_(line_ids), + Serie.discharge == True, # noqa: E712 + ) + ) + .scalars() + .all() + ) + + for ex_serie in export_series: + candidate_import_lines = import_lines_by_export.get(ex_serie.line_item_id, set()) + if not candidate_import_lines: + continue + + import_serie: Optional[Serie] = None + # 1) Prefer explicit mapped row from export serie + if ex_serie.serie_row is not None: + import_serie = db.execute( + select(Serie).where( + Serie.line_item_id.in_(candidate_import_lines), + Serie.row == ex_serie.serie_row, + ) + ).scalar_one_or_none() + + # 2) Fallback by serial number if row is absent + if import_serie is None and ex_serie.serial_numbers: + import_serie = db.execute( + select(Serie).where( + Serie.line_item_id.in_(candidate_import_lines), + Serie.serial_numbers == ex_serie.serial_numbers, + ) + ).scalar_one_or_none() + + if import_serie is None: + continue + + # Clarion equivalent: SerImp:SerieExpo = 0 / SerDef:SerieExpo = 0 + import_serie.discharge = False + + +def _cancel_discharge_records( + db: Session, + export_invoice: InvoiceHeader, + cancelled_by: Optional[str] = None, +) -> None: + """ + Cancels discharge records created by this export invoice in the new + specialized tables: + - a24.discharge_header: status -> CANCELLED + - a24.balance_movement: insert RETURN per discharge_detail row + + This is the ledger-safe equivalent of undoing "Descarga=1" effects. + """ + headers: List[DischargeHeader] = ( + db.query(DischargeHeader) + .filter( + DischargeHeader.source_invoice_id == export_invoice.id, + DischargeHeader.tenant_id == export_invoice.tenant_id, + DischargeHeader.company_id == export_invoice.company_id, + DischargeHeader.status.in_([DischargeStatus.APPLIED, DischargeStatus.PARTIAL]), + ) + .all() + ) + if not headers: + return + + op_date = ( + export_invoice.invoice_date.date() + if hasattr(export_invoice.invoice_date, "date") + else export_invoice.invoice_date + ) + + for header in headers: + details: List[DischargeDetail] = ( + db.query(DischargeDetail) + .filter( + DischargeDetail.discharge_header_id == header.id, + DischargeDetail.tenant_id == export_invoice.tenant_id, + DischargeDetail.company_id == export_invoice.company_id, + ) + .all() + ) + + for detail in details: + # Reverse each consumed lot with a RETURN movement (append-only ledger) + ret_mov = BalanceMovement( + tenant_id=export_invoice.tenant_id, + company_id=export_invoice.company_id, + import_invoice_id=detail.import_line.invoice_id if detail.import_line else None, + import_item_line_id=detail.import_item_line_id, + part_number_id=None, + movement_type=MovementType.RETURN, + quantity=detail.quantity_discharged or Decimal(0), + value_me=detail.value_me, + value_mn=detail.value_mn, + net_weight=detail.net_weight, + source_invoice_id=export_invoice.id, + source_item_line_id=detail.export_item_line_id, + order_peps=0, # set post-flush + operation_date=op_date, + notes=f"Reversa descargo factura {export_invoice.invoice_number} (header {header.id})", + ) + db.add(ret_mov) + db.flush() + ret_mov.order_peps = ret_mov.id + + header.status = DischargeStatus.CANCELLED + header.cancelled_by = cancelled_by + header.cancellation_reason = ( + f"Des-actualización de factura de exportación {export_invoice.invoice_number}" + ) + + +def _set_invoice_unprocessed(invoice: InvoiceHeader, line_count: int) -> None: + """ + Clarion mapping: + EqiFex:ComofueProcesada='' + EqiFex:Estatus='NA' + EqiFex:Cant_Partidas = Loc:PartidasExpo + """ + invoice.process_log = None + invoice.status = InvoiceStatus.REVERSED + invoice.party_count = line_count + + +def revert_process( + db: Session, + invoice: InvoiceHeader, + lines: List[LineItem], + tenant_id: str, + company_id: str, + errors: ErrorCollector, + cancelled_by: Optional[str] = None, +) -> list: + """ + Des-actualización de factura de exportación (paridad Clarion). + + Notes: + - BEGIN/COMMIT/ROLLBACK SQL explícitos del Clarion se controlan con la + transacción de SQLAlchemy en el task (commit/rollback externo). + - Las rutinas Clarion invocadas con DO se dejan como TODO por ahora. + """ + _ = (db, tenant_id, company_id) # reserved for future TO DO implementations + sql_errors: list = [] + + # INICIALIZA QUEUES (Python: collector ya llega limpio por tarea) + # TODO: Compartir QSisGen / parámetros globales del Clarion. + + # TODO: BEGIN TRAN (managed by SQLAlchemy session in task) + _todo_check_access_lock(invoice) + + # VERIFICAR SI HAY PARTIDAS DE EXPORTACION + line_count = len(lines) + + # VALIDACION DEL CAMBIO DE REGIMEN + _validate_regime_change_definitive_invoice_exists(db, invoice, errors) + errors.raise_if_errors() + + # VALIDACIONES DE PARTIDAS NORMAL <> REPARACION + if (invoice.invoice_type or "").upper() != "NODES": + _return_discharged_quantities(db, invoice, lines) + _unmark_returned_series(db, invoice, lines) + _cancel_discharge_records(db, invoice, cancelled_by) + + _set_invoice_unprocessed(invoice, line_count) + + # TODO: COMMIT/ROLLBACK TRAN + QueueErrorSQL file handling + GBitacora + return sql_errors diff --git a/backend/api/v1/modules/a76/invoices/exports/revert/pre_validators.py b/backend/api/v1/modules/a76/invoices/exports/revert/pre_validators.py new file mode 100644 index 00000000..cc9b6f8e --- /dev/null +++ b/backend/api/v1/modules/a76/invoices/exports/revert/pre_validators.py @@ -0,0 +1,42 @@ +from typing import List + +from sqlalchemy.orm import Session + +from api.v1.modules.a76.invoices.models import InvoiceHeader, InvoiceStatus +from api.v1.modules.a76.items.models import LineItem +from core.exceptions import ErrorCollector + + +def pre_validators( + db: Session, + invoice: InvoiceHeader, + tenant_id: str, + company_id: str, + errors: ErrorCollector, +) -> List[LineItem]: + """ + Validaciones previas a la reversión de una factura de importación temporal. + + - Verifica que la factura esté en estatus PROCESSED. + - Carga y retorna las partidas asociadas a la factura. + """ + if invoice.status != InvoiceStatus.PROCESSED: + errors.add_error( + "status", + "La factura no fue procesada y no puede ser revertida", + solution=["Verifique el estatus de la factura antes de intentar deshacer el proceso"], + code="NOT_PROCESSED", + value=invoice.status, + ) + + lines: List[LineItem] = ( + db.query(LineItem) + .filter( + LineItem.invoice_id == invoice.id, + LineItem.tenant_id == tenant_id, + LineItem.company_id == company_id, + ) + .all() + ) + + return lines diff --git a/backend/api/v1/modules/a76/invoices/exports/revert/routes.py b/backend/api/v1/modules/a76/invoices/exports/revert/routes.py new file mode 100644 index 00000000..1419eb96 --- /dev/null +++ b/backend/api/v1/modules/a76/invoices/exports/revert/routes.py @@ -0,0 +1,83 @@ +from typing import Any, Dict + +from fastapi import APIRouter, Depends, HTTPException, Query +from sqlalchemy.orm import Session + +from core.celery_app import celery_app +from core.database import get_core_db +from core.security import get_current_user, validate_access_to_resource + +from .task import revert_invoice_task + +router = APIRouter() + + +@router.post("/invoices/{invoice_id}/revert") +def trigger_invoice_revert( + invoice_id: int, + company_id: int = Query(..., description="Company ID"), + db: Session = Depends(get_core_db), + current_user: Dict[str, Any] = Depends(get_current_user), +): + """ + Inicia la des-actualización de una factura de importación temporal como + tarea Celery. + Retorna el task_id para hacer polling del progreso. + """ + tenant_id = validate_access_to_resource(db, company_id, current_user) + cancelled_by = ( + current_user.get("username") + or current_user.get("user_name") + or current_user.get("preferred_username") + or current_user.get("email") + or "system" + ) + + task = revert_invoice_task.apply_async( + args=[invoice_id, str(tenant_id), str(company_id), str(cancelled_by)] + ) + + return {"task_id": task.id} + + +@router.get("/invoices/revert/{task_id}/status") +def get_invoice_revert_status(task_id: str): + """ + Consulta el estado de progreso de una tarea de des-actualización de + factura. + + Retorna: + - state: 'PROCESSING' | 'SUCCESS' | 'FAILURE' + - info: { current: int, status: str } (cuando state == 'PROCESSING') + - result: dict (cuando state == 'SUCCESS' o 'FAILURE') + """ + task_result = celery_app.AsyncResult(task_id) + + if task_result.state in ("PENDING", "STARTED"): + return { + "state": "PROCESSING", + "info": {"current": 0, "status": "Iniciando..."}, + } + + if task_result.state == "PROGRESS": + return { + "state": "PROCESSING", + "info": task_result.info or {"current": 0, "status": "Procesando..."}, + } + + if task_result.state == "SUCCESS": + return { + "state": "SUCCESS", + "result": task_result.result, + } + + error_info = task_result.result + if isinstance(error_info, Exception): + error_msg = str(error_info) + else: + error_msg = str(error_info) if error_info else "Error desconocido" + + return { + "state": "FAILURE", + "result": error_msg, + } diff --git a/backend/api/v1/modules/a76/invoices/exports/revert/task.py b/backend/api/v1/modules/a76/invoices/exports/revert/task.py new file mode 100644 index 00000000..ef21e509 --- /dev/null +++ b/backend/api/v1/modules/a76/invoices/exports/revert/task.py @@ -0,0 +1,89 @@ +from celery import Task + +from core.celery_app import celery_app +from core.database import CoreSessionLocal +from core.exceptions import ErrorCollector, ValidationException + +from api.v1.modules.a76.invoices.models import InvoiceHeader +from .pre_validators import pre_validators +from .main_process import revert_process + + +def _progress(task: Task, current: int, status: str) -> None: + task.update_state(state="PROGRESS", meta={"current": current, "status": status}) + + +@celery_app.task(bind=True, name="revert_export_invoice_task") +def revert_invoice_task( + self: Task, + invoice_id: int, + tenant_id: str, + company_id: str, + cancelled_by: str | None = None, +) -> dict: + """ + Des-actualiza una factura de importación temporal ejecutando todas las + validaciones y reversiones del proceso principal (revert/main_process) con + reporte de progreso. + """ + db = CoreSessionLocal() + try: + # ── Paso 1: Cargar factura ──────────────────────────────────────────── + _progress(self, 5, "Cargando factura...") + invoice: InvoiceHeader | None = db.get(InvoiceHeader, invoice_id) + if invoice is None: + return { + "status": "error", + "message": f"Factura con id {invoice_id} no encontrada.", + "errors": [], + } + + errors = ErrorCollector() + + # ── Paso 2: Pre-validaciones ────────────────────────────────────────── + _progress(self, 10, "Validando estatus de la factura...") + lines = pre_validators(db, invoice, tenant_id, company_id, errors) + if not lines: + errors.add_error( + field="line_items", + message="La factura no contiene partidas para revertir", + solution=["Verifique que la factura tenga partidas antes de intentar revertirla"], + code="NO_LINE_ITEMS", + ) + errors.raise_if_errors() + + # ── Paso 3: Validar cantidades y ejecutar reversión ─────────────────── + _progress(self, 40, "Verificando saldos de partidas...") + sql_errors = revert_process( + db=db, + invoice=invoice, + lines=lines, + tenant_id=tenant_id, + company_id=company_id, + errors=errors, + cancelled_by=cancelled_by, + ) + + # ── Paso 4: Confirmar transacción ───────────────────────────────────── + _progress(self, 95, "Anulando saldos de inventario y confirmando...") + db.flush() + db.commit() + + return { + "status": "success", + "invoice_id": invoice_id, + "sql_errors": sql_errors, + } + + except ValidationException as exc: + db.rollback() + return { + "status": "validation_error", + "message": exc.message, + "errors": exc.errors, + } + except Exception as exc: + db.rollback() + raise exc + finally: + db.close() diff --git a/backend/api/v1/modules/a76/invoices/imports/balance/void_balance_entries.py b/backend/api/v1/modules/a76/invoices/imports/balance/void_balance_entries.py index 45bb113c..4ece361a 100644 --- a/backend/api/v1/modules/a76/invoices/imports/balance/void_balance_entries.py +++ b/backend/api/v1/modules/a76/invoices/imports/balance/void_balance_entries.py @@ -80,8 +80,13 @@ def void_balance_entries( (BalanceMovement.movement_type.in_(NEGATIVE_MOVEMENTS), -1), else_=1, ) - used_expr = case( + # "used" must be NET of returns: + # + consumption/waste/scrap/destruction + # - return + # so an already reverted discharge does not block import un-processing. + used_net_expr = case( (BalanceMovement.movement_type.in_(USED_MOVEMENTS), BalanceMovement.quantity), + (BalanceMovement.movement_type == MovementType.RETURN, -BalanceMovement.quantity), else_=Decimal(0), ) @@ -90,7 +95,7 @@ def void_balance_entries( select( BalanceMovement.import_item_line_id, func.sum(sign_expr * BalanceMovement.quantity).label("balance"), - func.sum(used_expr).label("used"), + func.sum(used_net_expr).label("used_net"), ) .where( BalanceMovement.import_item_line_id.in_(import_line_ids), @@ -100,7 +105,7 @@ def void_balance_entries( .all() ) - consumed_lots = [row for row in lot_summary if (row.used or 0) > 0] + consumed_lots = [row for row in lot_summary if (row.used_net or 0) > 0] if consumed_lots: lot_ids = ", ".join(str(r.import_item_line_id) for r in consumed_lots) raise ValueError( @@ -109,13 +114,18 @@ def void_balance_entries( f"cancelarse primero (item_line ids: {lot_ids})." ) - # ── 3. Build the ENTRY_VOID map: one void per ENTRY ────────────────────── + # ── 3. Build the ENTRY_VOID map: one void per LOT ──────────────────────── # Map lot_id → open balance (should equal the original ENTRY qty since no # consumptions exist, but we use the actual net balance to be safe). balance_map: dict[int, Decimal] = { row.import_item_line_id: Decimal(str(row.balance or 0)) for row in lot_summary } + # Pick one representative ENTRY per lot to copy informational fields. + entry_by_lot: dict[int, BalanceMovement] = {} + for e in entries: + if e.import_item_line_id not in entry_by_lot: + entry_by_lot[e.import_item_line_id] = e operation_date = ( invoice.invoice_date.date() @@ -124,8 +134,8 @@ def void_balance_entries( ) voids: List[BalanceMovement] = [] - for entry in entries: - open_qty = balance_map.get(entry.import_item_line_id, Decimal(0)) + for lot_id, entry in entry_by_lot.items(): + open_qty = balance_map.get(lot_id, Decimal(0)) if open_qty <= 0: continue @@ -133,7 +143,7 @@ def void_balance_entries( tenant_id=invoice.tenant_id, company_id=invoice.company_id, import_invoice_id=invoice.id, - import_item_line_id=entry.import_item_line_id, + import_item_line_id=lot_id, part_number_id=entry.part_number_id, movement_type=MovementType.ENTRY_VOID, quantity=open_qty, diff --git a/backend/api/v1/modules/a76/invoices/imports/process/main_process.py b/backend/api/v1/modules/a76/invoices/imports/process/main_process.py index df03b322..c4127ee9 100644 --- a/backend/api/v1/modules/a76/invoices/imports/process/main_process.py +++ b/backend/api/v1/modules/a76/invoices/imports/process/main_process.py @@ -25,6 +25,10 @@ from .sub_process.review_rule_octave import ( ) from ...common.process.review_uma import revisa_uma from .sub_process.assing_values import assign_values_lines, assign_values_invoice +from .sub_process.assing_values_def_mex import ( + assign_values_iva_lines, + assign_values_invoice_totals, +) from ..balance.create_balance_entries import create_balance_entries @@ -150,7 +154,7 @@ def _validate_sisimp_limits( pass -def _update_invoice_totals(invoice: InvoiceHeader) -> None: +def _update_invoice_totals(invoice: InvoiceHeader, lines: List[LineItem]) -> None: """ Copia los totales calculados de financials/logistics al encabezado de la factura y calcula IVA, incrementables y valores de aduanas. @@ -198,7 +202,7 @@ def _update_invoice_totals(invoice: InvoiceHeader) -> None: # Marcar la factura como procesada invoice.status = InvoiceStatus.PROCESSED - invoice.party_count = len(invoice.financials.__dict__) # se sobreescribirá con el conteo real + invoice.party_count = len(lines) # TODO: SSisGen:ActSeguridad = 1 → invoice.updated_by = current_user # TODO: SSisGen:CalValBaseTCPed = 1 → @@ -246,8 +250,14 @@ def main_process(db: Session, invoice: InvoiceHeader, tenant_id: str, company_id review_weights_lbs(db, lines, tenant_id, company_id, errors) # Paso 3: Asignación de valores por partida y totalización de factura - assign_values_lines(invoice, lines) - assign_values_invoice(invoice, lines) + # Para IMPO DEF / Compras Mexicanas se usa la versión con IVA por partida. + invoice_type = (invoice.invoice_type or "").strip().upper() + if invoice_type in {"DEF", "MEX"}: + assign_values_iva_lines(invoice, lines) + assign_values_invoice_totals(invoice, lines) + else: + assign_values_lines(invoice, lines) + assign_values_invoice(invoice, lines) # Paso 4: Validaciones per-línea octave_desc, octave_available = _validate_lines(db, invoice, lines, tenant_id, company_id, errors) @@ -281,9 +291,10 @@ def main_process(db: Session, invoice: InvoiceHeader, tenant_id: str, company_id ) # Paso 7: Actualizar totales, IVA e incrementables y marcar como procesada - _update_invoice_totals(invoice) + _update_invoice_totals(invoice, lines) # Paso 8: Generar saldos en a24.balance_movement (una entrada por partida) - create_balance_entries(db, invoice, lines) + if invoice_type not in {"DEF", "MEX"}: + create_balance_entries(db, invoice, lines) db.flush() \ No newline at end of file diff --git a/backend/api/v1/modules/a76/invoices/imports/process/sub_process/assing_values.py b/backend/api/v1/modules/a76/invoices/imports/process/sub_process/assing_values.py index b4983497..66a59e64 100644 --- a/backend/api/v1/modules/a76/invoices/imports/process/sub_process/assing_values.py +++ b/backend/api/v1/modules/a76/invoices/imports/process/sub_process/assing_values.py @@ -15,8 +15,8 @@ def assign_values_lines( Calculates and assigns unit costs and values in all currency types for every line item, based on the invoice currency and exchange rates. - Also resets inventory counters (quantity_returned, quantity_returned_temp, - quantity_existence) to zero, as done in the legacy ASIGNAVALORES_PARTIDA routine. + Legacy returned/existence counters were removed from item_line_quantities; + balances are now derived from a24.balance_movement and discharges. Currency mapping (legacy -> current enum): ME (moneda extranjera / foreign) -> Currency.FOREIGN @@ -53,11 +53,7 @@ def assign_values_lines( line.financial.value_mxn = capture * tc_mm * tc * qty line.financial.value_mc = capture * qty - # Reset inventory counters - if line.quantity is not None: - line.quantity.quantity_returned = Decimal(0) - line.quantity.quantity_returned_temp = Decimal(0) - line.quantity.quantity_existence = Decimal(0) + # NOTE: no legacy returned/existence counters to reset. def assign_values_invoice( diff --git a/backend/api/v1/modules/a76/invoices/imports/process/sub_process/assing_values_def_mex.py b/backend/api/v1/modules/a76/invoices/imports/process/sub_process/assing_values_def_mex.py new file mode 100644 index 00000000..fec9e4d6 --- /dev/null +++ b/backend/api/v1/modules/a76/invoices/imports/process/sub_process/assing_values_def_mex.py @@ -0,0 +1,159 @@ +from decimal import Decimal +from typing import List + +from api.v1.modules.a76.invoices.models import Currency, InvoiceHeader +from api.v1.modules.a76.items.models import LineItem + + +def assign_values_iva_lines( + invoice: InvoiceHeader, + lines: List[LineItem], +) -> None: + """ + ASIGNAVALORES_IVA_PARTIDA + Asigna valores por partida para IMPO DEFINITIVA / COMPRAS MEXICANAS + calculando subtotal + IVA + total en ME/MN/MC. + + Nota: + - Los contadores legacy CantRetornada/CantRetornadaTemp ya no existen. + - La trazabilidad de saldos vive en balance_movement + discharges. + """ + if not invoice.financials: + return + + currency = invoice.financials.currency + tc = Decimal(str(invoice.financials.exchange_rate or 0)) + tc_mm = Decimal(str(invoice.financials.exchange_rate_mm or 0)) + iva_factor = Decimal(str(invoice.financials.iva_factor or 0)) + + for line in lines: + fin = line.financial + qty_rec = line.quantity + if fin is None or qty_rec is None: + continue + + qty = Decimal(str(qty_rec.quantity or 0)) + capture = Decimal(str(fin.unit_cost_capture or 0)) + if qty <= 0: + continue + + if currency == Currency.FOREIGN: # ME + # ME base + fin.unit_cost_usd = capture + fin.sub_import_value_usd = capture * qty + fin.vat_usd = fin.sub_import_value_usd * iva_factor / 100 + fin.value_usd = fin.sub_import_value_usd + fin.vat_usd + + # MN converted from ME + fin.unit_cost_mxn = capture * tc + fin.sub_import_value_mxn = fin.unit_cost_mxn * qty + fin.vat_mxn = fin.sub_import_value_mxn * iva_factor / 100 + fin.value_mxn = fin.sub_import_value_mxn + fin.vat_mxn + + # MC mirrors capture currency in legacy + fin.unit_cost_mc = capture + fin.sub_import_value_mc = capture * qty + fin.vat_mc = fin.sub_import_value_mc * iva_factor / 100 + fin.value_mc = fin.sub_import_value_mc + fin.vat_mc + + elif currency == Currency.LOCAL: # MN + # MN base + fin.unit_cost_mxn = capture + fin.sub_import_value_mxn = capture * qty + fin.vat_mxn = fin.sub_import_value_mxn * iva_factor / 100 + fin.value_mxn = fin.sub_import_value_mxn + fin.vat_mxn + + # ME converted from MN + fin.unit_cost_usd = (capture / tc) if tc else Decimal(0) + fin.sub_import_value_usd = fin.unit_cost_usd * qty + fin.vat_usd = fin.sub_import_value_usd * iva_factor / 100 + fin.value_usd = fin.sub_import_value_usd + fin.vat_usd + + # MC mirrors capture currency in legacy + fin.unit_cost_mc = capture + fin.sub_import_value_mc = capture * qty + fin.vat_mc = fin.sub_import_value_mc * iva_factor / 100 + fin.value_mc = fin.sub_import_value_mc + fin.vat_mc + + elif currency == Currency.MANUAL: # MC + # ME from MC * tc_mm + fin.unit_cost_usd = capture * tc_mm + fin.sub_import_value_usd = fin.unit_cost_usd * qty + fin.vat_usd = fin.sub_import_value_usd * iva_factor / 100 + fin.value_usd = fin.sub_import_value_usd + fin.vat_usd + + # MN from ME * tc + fin.unit_cost_mxn = fin.unit_cost_usd * tc + fin.sub_import_value_mxn = fin.unit_cost_mxn * qty + fin.vat_mxn = fin.sub_import_value_mxn * iva_factor / 100 + fin.value_mxn = fin.sub_import_value_mxn + fin.vat_mxn + + # MC base + fin.unit_cost_mc = capture + fin.sub_import_value_mc = capture * qty + fin.vat_mc = fin.sub_import_value_mc * iva_factor / 100 + fin.value_mc = fin.sub_import_value_mc + fin.vat_mc + + +def assign_values_invoice_totals( + invoice: InvoiceHeader, + lines: List[LineItem], +) -> None: + """ + ASIGNAVALORES_FACTURA + Totaliza cantidades, pesos y valores/IVA en encabezado para IMPO DEF/MEX. + """ + if not invoice.financials: + return + + total_qty = Decimal(0) + total_net = Decimal(0) + total_gross = Decimal(0) + total_packages = 0 + + total_val_mn = Decimal(0) + total_val_me = Decimal(0) + total_val_mc = Decimal(0) + total_iva_mn = Decimal(0) + total_iva_me = Decimal(0) + total_iva_mc = Decimal(0) + total_sub_mn = Decimal(0) + total_sub_me = Decimal(0) + total_sub_mc = Decimal(0) + + for line in lines: + q = line.quantity + f = line.financial + if q: + total_qty += Decimal(str(q.quantity or 0)) + total_net += Decimal(str(q.net_weight or 0)) + total_gross += Decimal(str(q.gross_weight or 0)) + total_packages += int(q.package_quantity or 0) + if f: + total_val_mn += Decimal(str(f.value_mxn or 0)) + total_val_me += Decimal(str(f.value_usd or 0)) + total_val_mc += Decimal(str(f.value_mc or 0)) + total_iva_mn += Decimal(str(f.vat_mxn or 0)) + total_iva_me += Decimal(str(f.vat_usd or 0)) + total_iva_mc += Decimal(str(f.vat_mc or 0)) + total_sub_mn += Decimal(str(f.sub_import_value_mxn or 0)) + total_sub_me += Decimal(str(f.sub_import_value_usd or 0)) + total_sub_mc += Decimal(str(f.sub_import_value_mc or 0)) + + fin = invoice.financials + fin.total_quantity = float(total_qty) + fin.net_weight = float(total_net) + fin.gross_weight = float(total_gross) + fin.total_packages = total_packages + + fin.value_mn = float(total_val_mn) + fin.value_me = float(total_val_me) + fin.value_mc = float(total_val_mc) + + fin.iva_mn = float(total_iva_mn) + fin.iva_me = float(total_iva_me) + fin.iva_mc = float(total_iva_mc) + + # No existe subtotal a nivel encabezado en el modelo actual. + # Se conserva en partidas (sub_import_value_*), de donde se agrega cuando se necesite. + _ = (total_sub_mn, total_sub_me, total_sub_mc) diff --git a/backend/api/v1/modules/a76/invoices/imports/process/task.py b/backend/api/v1/modules/a76/invoices/imports/process/task.py index b269e129..4ca0360f 100644 --- a/backend/api/v1/modules/a76/invoices/imports/process/task.py +++ b/backend/api/v1/modules/a76/invoices/imports/process/task.py @@ -1,3 +1,5 @@ +import logging + from celery import Task from core.celery_app import celery_app @@ -12,9 +14,15 @@ from .sub_process.review_exchange_rate import review_exchange_rate from .sub_process.review_weights import review_weights_kgs, review_weights_lbs from .sub_process.review_rule_octave import valida_imp_regla_octava, descuenta_cupo_r_octava from .sub_process.assing_values import assign_values_lines, assign_values_invoice +from .sub_process.assing_values_def_mex import ( + assign_values_iva_lines, + assign_values_invoice_totals, +) from ..balance.create_balance_entries import create_balance_entries from .main_process import _validate_sisimp_limits, _update_invoice_totals, _validate_lines +logger = logging.getLogger(__name__) + def _progress(task: Task, current: int, status: str) -> None: task.update_state(state="PROGRESS", meta={"current": current, "status": status}) @@ -64,8 +72,21 @@ def process_invoice_task(self: Task, invoice_id: int, tenant_id: str, company_id # ── Paso 4: Asignación de valores ───────────────────────────────────── _progress(self, 50, "Calculando valores por partida...") - assign_values_lines(invoice, lines) - assign_values_invoice(invoice, lines) + raw_type = invoice.invoice_type + invoice_type = (raw_type or "").strip().upper() + logger.info( + "celery import process invoice_type: invoice_id=%s raw=%r normalized=%r document_type=%r", + invoice.id, + raw_type, + invoice_type, + getattr(invoice, "document_type", None), + ) + if invoice_type in {"DEF", "MEX"}: + assign_values_iva_lines(invoice, lines) + assign_values_invoice_totals(invoice, lines) + else: + assign_values_lines(invoice, lines) + assign_values_invoice(invoice, lines) # ── Paso 5: Validaciones por partida ────────────────────────────────── _progress(self, 70, "Validando partidas...") @@ -99,11 +120,12 @@ def process_invoice_task(self: Task, invoice_id: int, tenant_id: str, company_id company_id=company_id, sql_errors=sql_errors, ) - _update_invoice_totals(invoice) + _update_invoice_totals(invoice, lines) # ── Paso 8: Generar saldos en a24.balance_movement ─────────────────── _progress(self, 98, "Generando saldos de inventario...") - create_balance_entries(db, invoice, lines) + if invoice_type not in {"DEF", "MEX"}: + create_balance_entries(db, invoice, lines) db.flush() db.commit() diff --git a/backend/api/v1/modules/a76/invoices/imports/revert/main_process.py b/backend/api/v1/modules/a76/invoices/imports/revert/main_process.py index f39e51ef..3b1824ff 100644 --- a/backend/api/v1/modules/a76/invoices/imports/revert/main_process.py +++ b/backend/api/v1/modules/a76/invoices/imports/revert/main_process.py @@ -43,22 +43,12 @@ def _validate_returned_quantities( una factura de exportación procesada y se reporta qué factura debe desactualizarse primero. """ - lines_with_balance = [ - line for line in lines - if line.quantity is not None and ( - (line.quantity.quantity_returned_temp or Decimal(0)) - + (line.quantity.quantity_returned or Decimal(0)) - + (line.quantity.quantity_existence or Decimal(0)) - ) != Decimal(0) - ] - - if not lines_with_balance: - return - - for line in lines_with_balance: - qty_ret_temp = line.quantity.quantity_returned_temp or Decimal(0) - qty_ret = line.quantity.quantity_returned or Decimal(0) - qty_exist = line.quantity.quantity_existence or Decimal(0) + # Importante: + # Con la migración a tablas especializadas (discharge_* + balance_movement), + # los campos legacy de cantidades retornadas pueden quedar desfasados. + # Para bloquear una des-actualización solo debe considerarse descarga ACTIVA + # real (DischargeHeader.status=APPLIED y factura fuente procesada). + for line in lines: # Buscar DischargeDetail vinculados a esta partida de importación # cuya factura de exportación esté activa (PROCESSED). @@ -110,22 +100,7 @@ def _validate_returned_quantities( ], code="LINE_HAS_ACTIVE_DISCHARGE", ) - else: - # La partida tiene saldo pero no hay descarga activa rastreable — - # reportar el saldo directamente para que el usuario lo investigue. - errors.add_error( - field=f"line[{line.line_number}].quantities", - message=( - f"La Línea: {line.line_number} tiene saldos pendientes " - f"(retornada: {qty_ret}, retornada temp: {qty_ret_temp}, " - f"existencia: {qty_exist}) y no se puede desactualizar." - ), - solution=[ - "Verifique las exportaciones que afectan a esta partida " - "y desactualícelas primero." - ], - code="LINE_HAS_BALANCE", - ) + # Si no hay discharge activo, NO bloquear por contadores legacy. # ───────────────────────────────────────────────────────────────────────────── @@ -156,6 +131,7 @@ def _reset_invoice_financials(invoice: InvoiceHeader) -> None: fin.customs_value_me = 0.0 fin.iva_mn = 0.0 fin.iva_me = 0.0 + fin.iva_mc = 0.0 invoice.status = InvoiceStatus.PENDING invoice.process_method = None @@ -169,11 +145,6 @@ def _reset_line_quantities(lines: List[LineItem]) -> None: ValorIVAMNUsado=0, ValorIVAMEUsado=0 (Clarion SCAII). """ for line in lines: - if line.quantity is not None: - line.quantity.quantity_returned = Decimal(0) - line.quantity.quantity_returned_temp = Decimal(0) - line.quantity.quantity_existence = Decimal(0) - if line.financial is not None: line.financial.value_returned_mxn = Decimal(0) line.financial.value_returned_usd = Decimal(0) diff --git a/backend/api/v1/modules/a76/invoices/imports/revert/routes.py b/backend/api/v1/modules/a76/invoices/imports/revert/routes.py index e380d746..8990f34e 100644 --- a/backend/api/v1/modules/a76/invoices/imports/revert/routes.py +++ b/backend/api/v1/modules/a76/invoices/imports/revert/routes.py @@ -7,7 +7,9 @@ from core.celery_app import celery_app from core.database import get_core_db from core.security import get_current_user, validate_access_to_resource -from .task import revert_invoice_task +from api.v1.modules.a76.invoices.models import InvoiceHeader, OperationType +from .task import revert_invoice_task as revert_import_invoice_task +from ...exports.revert.task import revert_invoice_task as revert_export_invoice_task router = APIRouter() @@ -20,16 +22,32 @@ def trigger_invoice_revert( current_user: Dict[str, Any] = Depends(get_current_user), ): """ - Inicia la des-actualización de una factura de importación temporal como - tarea Celery. - Retorna el task_id para hacer polling del progreso. + Endpoint unificado para des-actualizar facturas. + - operation_type=imp -> task de importación + - operation_type=exp -> task de exportación """ tenant_id = validate_access_to_resource(db, company_id, current_user) - - task = revert_invoice_task.apply_async( - args=[invoice_id, str(tenant_id), str(company_id)] + cancelled_by = ( + current_user.get("username") + or current_user.get("user_name") + or current_user.get("preferred_username") + or current_user.get("email") + or "system" ) + invoice: InvoiceHeader | None = db.get(InvoiceHeader, invoice_id) + if invoice is None: + raise HTTPException(status_code=404, detail=f"Factura {invoice_id} no encontrada.") + + if invoice.operation_type == OperationType.EXP: + task = revert_export_invoice_task.apply_async( + args=[invoice_id, str(tenant_id), str(company_id), str(cancelled_by)] + ) + else: + task = revert_import_invoice_task.apply_async( + args=[invoice_id, str(tenant_id), str(company_id), str(cancelled_by)] + ) + return {"task_id": task.id} diff --git a/backend/api/v1/modules/a76/invoices/imports/revert/task.py b/backend/api/v1/modules/a76/invoices/imports/revert/task.py index fbf2b056..e787c998 100644 --- a/backend/api/v1/modules/a76/invoices/imports/revert/task.py +++ b/backend/api/v1/modules/a76/invoices/imports/revert/task.py @@ -13,8 +13,14 @@ def _progress(task: Task, current: int, status: str) -> None: task.update_state(state="PROGRESS", meta={"current": current, "status": status}) -@celery_app.task(bind=True, name="revert_invoice_task") -def revert_invoice_task(self: Task, invoice_id: int, tenant_id: str, company_id: str) -> dict: +@celery_app.task(bind=True, name="revert_import_invoice_task") +def revert_invoice_task( + self: Task, + invoice_id: int, + tenant_id: str, + company_id: str, + cancelled_by: str | None = None, +) -> dict: """ Des-actualiza una factura de importación temporal ejecutando todas las validaciones y reversiones del proceso principal (revert/main_process) con diff --git a/backend/api/v1/modules/a76/invoices/models.py b/backend/api/v1/modules/a76/invoices/models.py index 2bda119c..a10be8c7 100644 --- a/backend/api/v1/modules/a76/invoices/models.py +++ b/backend/api/v1/modules/a76/invoices/models.py @@ -517,9 +517,9 @@ class InvoiceFinancials(Base, TenantScopedMixin, TimestampMixin): iva_mc: Mapped[Optional[float]] = mapped_column( Numeric(23, 8), default=0, server_default="0" ) # IVAEXPOMC / IVA en MC - iva_factor: Mapped[Optional[str]] = mapped_column( - String(10) - ) # FACTORIVA / Factor IVA (puede ser varchar en imports) + iva_factor: Mapped[Optional[float]] = mapped_column( + Numeric(23, 8), default=0, server_default="0" + ) # FACTORIVA / Factor IVA tax_value_me: Mapped[Optional[float]] = mapped_column( Numeric(23, 8), default=0, server_default="0" ) # VALORIMPUESTOME / Valor impuesto ME diff --git a/backend/api/v1/modules/a76/invoices/services.py b/backend/api/v1/modules/a76/invoices/services.py index 63304a24..a9871ad6 100644 --- a/backend/api/v1/modules/a76/invoices/services.py +++ b/backend/api/v1/modules/a76/invoices/services.py @@ -10,6 +10,7 @@ from .imports.validators.update import validate_update as validate_update_import from .exports.validators.create import validate_create as validate_create_export from .exports.validators.update import validate_update as validate_update_export from .common.common_validators import invoice_exists +from api.v1.modules.a76.items.models import LineItem from . import models, schemas @@ -166,6 +167,25 @@ class InvoiceService: total = query.count() items = query.offset(skip).limit(limit).all() + + # Keep party_count aligned with the real number of line items. + # This avoids stale values stored in invoice_header.party_count. + if items: + invoice_ids = [inv.id for inv in items] + counts = ( + db.query(LineItem.invoice_id, func.count(LineItem.id)) + .filter( + LineItem.invoice_id.in_(invoice_ids), + LineItem.tenant_id == tenant_id, + LineItem.company_id == company_id, + ) + .group_by(LineItem.invoice_id) + .all() + ) + count_map = {invoice_id: int(count) for invoice_id, count in counts} + for inv in items: + inv.party_count = count_map.get(inv.id, 0) + return items, total @staticmethod diff --git a/backend/api/v1/modules/a76/items/exports/validators/create.py b/backend/api/v1/modules/a76/items/exports/validators/create.py index 9c19d6bf..4446bc4c 100644 --- a/backend/api/v1/modules/a76/items/exports/validators/create.py +++ b/backend/api/v1/modules/a76/items/exports/validators/create.py @@ -248,18 +248,18 @@ def validate_create( # Calcular peso neto en kilogramos (estándar interno) if unit_is_kgs: - if invoice_weight_type == "KGS": + if invoice_weight_type.lower() == "kgs": line.quantity.net_weight = quantity else: # invoice en libras line.quantity.net_weight = quantity * Decimal("2.204624") elif unit_is_lbs: - if invoice_weight_type == "KGS": + if invoice_weight_type.lower() == "kgs": line.quantity.net_weight = quantity / Decimal("2.204624") else: # invoice en libras line.quantity.net_weight = quantity else: # Otra unidad de medida - usar peso capturado y convertir si es necesario - if invoice_weight_type == "KGS": + if invoice_weight_type.lower() == "kgs": # El peso capturado está en kilos line.quantity.net_weight = net_weight_input else: @@ -289,7 +289,7 @@ def validate_create( # Si no se proporcionó peso bruto, calcularlo if not gross_weight_input or gross_weight_input == 0: - if invoice_weight_type == "KGS": + if invoice_weight_type.lower() == "kgs": line.quantity.gross_weight = line.quantity.net_weight + ( package_weight_unit * package_quantity ) @@ -299,7 +299,7 @@ def validate_create( ) else: # Convertir peso bruto capturado según tipo de factura - if invoice_weight_type == "KGS": + if invoice_weight_type.lower() == "kgs": line.quantity.gross_weight = gross_weight_input else: # libras line.quantity.gross_weight = gross_weight_input / Decimal("2.204624") diff --git a/backend/api/v1/modules/a76/items/exports/validators/update.py b/backend/api/v1/modules/a76/items/exports/validators/update.py index 4f8e2c9d..6f9ad8a1 100644 --- a/backend/api/v1/modules/a76/items/exports/validators/update.py +++ b/backend/api/v1/modules/a76/items/exports/validators/update.py @@ -95,21 +95,19 @@ def validate_update( # Se proporcionó nuevo peso neto, convertir según tipo net_weight_input = line.quantity.net_weight - if invoice_weight_type == "KGS": + if invoice_weight_type.lower() == "kgs": line.quantity.net_weight = net_weight_input else: # libras, convertir a kilos line.quantity.net_weight = net_weight_input / Decimal("2.204624") else: # Mantener peso existente - line.quantity.net_weight = existing_line.quantity.net_weight - - print(f"After weight conversion: net_weight={line.quantity.net_weight}, gross_weight={line.quantity.gross_weight}, weight_type={invoice_weight_type}") + line.quantity.net_weight = existing_line.quantity.net_weight # Convertir peso bruto si se proporcionó if line.quantity.gross_weight is not None: gross_weight_input = line.quantity.gross_weight - if invoice_weight_type == "KGS": + if invoice_weight_type.lower() == "kgs": line.quantity.gross_weight = gross_weight_input else: # libras, convertir a kilos line.quantity.gross_weight = gross_weight_input / Decimal("2.204624") diff --git a/backend/api/v1/modules/a76/items/imports/validators/common.py b/backend/api/v1/modules/a76/items/imports/validators/common.py index b1d0dc2f..ad62f477 100644 --- a/backend/api/v1/modules/a76/items/imports/validators/common.py +++ b/backend/api/v1/modules/a76/items/imports/validators/common.py @@ -9,6 +9,9 @@ from ...common.fractions import search_fraction_preference from ...common.common_validators import item_exists from ...models import LineItem from ...line_customs.models import FractionType, LineCustom +from api.v1.modules.a76.general_catalogs.fractions.us_tariff_fractions.models import ( + USTariffFraction, +) from api.v1.modules.a76.items.schemas import LineItemCreate from api.v1.modules.a76.invoices.models import InvoiceHeader from api.v1.modules.a76.classes.models import Class @@ -22,6 +25,8 @@ from api.v1.modules.public.reference_data.valuation_methods.models import ( from api.v1.modules.a76.parts.models import Part from api.v1.modules.a76.general_catalogs.company.models import Company +import re + def validate_common( db: Session, @@ -284,18 +289,75 @@ def validate_common( ) if line.customs.american_fraction: - american_fraction_exists = db.query( - exists().where( - LineCustom.american_fraction == line.customs.american_fraction + def _normalize_american_fraction_code(raw_code: str) -> list[str]: + """ + Attempts to map user input to the canonical USTariffFraction.code. + + The catalog commonly stores dotted HTS codes (e.g. 3802.20.00.00), + but users may paste/enter digits-only or use different separators. + """ + + normalized_raw = (raw_code or "").strip() + if not normalized_raw: + return [] + + digits_only = re.sub(r"[.\s\-]", "", normalized_raw) + + candidates: list[str] = [] + + # 1) Exact input + candidates.append(normalized_raw) + + # 2) Canonical with dots if length matches common patterns + if len(digits_only) == 10: + candidates.append( + f"{digits_only[:4]}.{digits_only[4:6]}.{digits_only[6:8]}.{digits_only[8:10]}" + ) + elif len(digits_only) == 8: + candidates.append( + f"{digits_only[:4]}.{digits_only[4:6]}.{digits_only[6:8]}" + ) + + # 3) Digits-only (if catalog stores without dots) + candidates.append(digits_only) + + # De-duplicate while preserving order + seen: set[str] = set() + deduped: list[str] = [] + for c in candidates: + if not c or c in seen: + continue + seen.add(c) + deduped.append(c) + return deduped + + raw_american_fraction = str(line.customs.american_fraction) + candidates = _normalize_american_fraction_code(raw_american_fraction) + + us_fraction: USTariffFraction | None = None + for candidate in candidates: + us_fraction = ( + db.query(USTariffFraction) + .filter( + USTariffFraction.code == candidate, + USTariffFraction.tenant_id == tenant_id, + USTariffFraction.company_id == company_id, + ) + .first() ) - ).scalar() - if not american_fraction_exists: + if us_fraction: + break + + if not us_fraction: errors.add_error( field=f"line[{line_number}].customs.american_fraction", message="La fracción americana especificada no existe.", solution=["Proporciona una fracción americana valida."], code="AMERICAN_FRACTION_NOT_FOUND", ) + else: + # Keep canonical value so downstream validators can use it safely. + line.customs.american_fraction = us_fraction.code if line.order: if len(line.order) > 20: diff --git a/backend/api/v1/modules/a76/items/imports/validators/create.py b/backend/api/v1/modules/a76/items/imports/validators/create.py index 14780846..7dc00b1c 100644 --- a/backend/api/v1/modules/a76/items/imports/validators/create.py +++ b/backend/api/v1/modules/a76/items/imports/validators/create.py @@ -232,18 +232,18 @@ def validate_create( # Calcular peso neto en kilogramos (estándar interno) if unit_is_kgs: - if invoice_weight_type == "KGS": + if invoice_weight_type.lower() == "kgs": line.quantity.net_weight = quantity else: # invoice en libras line.quantity.net_weight = quantity * Decimal("2.204624") elif unit_is_lbs: - if invoice_weight_type == "KGS": + if invoice_weight_type.lower() == "kgs": line.quantity.net_weight = quantity / Decimal("2.204624") else: # invoice en libras line.quantity.net_weight = quantity else: # Otra unidad de medida - usar peso capturado y convertir si es necesario - if invoice_weight_type == "KGS": + if invoice_weight_type.lower() == "kgs": # El peso capturado está en kilos line.quantity.net_weight = net_weight_input else: @@ -273,7 +273,7 @@ def validate_create( # Si no se proporcionó peso bruto, calcularlo if not gross_weight_input or gross_weight_input == 0: - if invoice_weight_type == "KGS": + if invoice_weight_type.lower() == "kgs": line.quantity.gross_weight = line.quantity.net_weight + ( package_weight_unit * package_quantity ) @@ -283,7 +283,7 @@ def validate_create( ) else: # Convertir peso bruto capturado según tipo de factura - if invoice_weight_type == "KGS": + if invoice_weight_type.lower() == "kgs": line.quantity.gross_weight = gross_weight_input else: # libras line.quantity.gross_weight = gross_weight_input / Decimal("2.204624") diff --git a/backend/api/v1/modules/a76/items/imports/validators/update.py b/backend/api/v1/modules/a76/items/imports/validators/update.py index c1343a0d..c5bd5e7a 100644 --- a/backend/api/v1/modules/a76/items/imports/validators/update.py +++ b/backend/api/v1/modules/a76/items/imports/validators/update.py @@ -94,21 +94,19 @@ def validate_update( # Se proporcionó nuevo peso neto, convertir según tipo net_weight_input = line.quantity.net_weight - if invoice_weight_type == "KGS": + if invoice_weight_type.lower() == "kgs": line.quantity.net_weight = net_weight_input else: # libras, convertir a kilos line.quantity.net_weight = net_weight_input / Decimal("2.204624") else: # Mantener peso existente - line.quantity.net_weight = existing_line.quantity.net_weight - - print(f"After weight conversion: net_weight={line.quantity.net_weight}, gross_weight={line.quantity.gross_weight}, weight_type={invoice_weight_type}") + line.quantity.net_weight = existing_line.quantity.net_weight # Convertir peso bruto si se proporcionó if line.quantity.gross_weight is not None: gross_weight_input = line.quantity.gross_weight - if invoice_weight_type == "KGS": + if invoice_weight_type.lower() == "kgs": line.quantity.gross_weight = gross_weight_input else: # libras, convertir a kilos line.quantity.gross_weight = gross_weight_input / Decimal("2.204624") diff --git a/backend/api/v1/modules/a76/items/line_quantities/models.py b/backend/api/v1/modules/a76/items/line_quantities/models.py index d21e8835..8dbee5ec 100644 --- a/backend/api/v1/modules/a76/items/line_quantities/models.py +++ b/backend/api/v1/modules/a76/items/line_quantities/models.py @@ -30,9 +30,6 @@ class LineQuantity(Base): # Quantities - Special (SCAF specific) quantity_temp_export: Mapped[Optional[Decimal]] = mapped_column(Numeric(19, 8)) # CANTEXPOTEMP - quantity_existence: Mapped[Optional[Decimal]] = mapped_column(Numeric(19, 8)) # CANTEXISTENCIA - quantity_returned: Mapped[Optional[Decimal]] = mapped_column(Numeric(19, 8)) # CANTRETORNADA - quantity_returned_temp: Mapped[Optional[Decimal]] = mapped_column(Numeric(19, 8)) # CANTRETORNADATEMP serial_count: Mapped[Optional[int]] = mapped_column(Integer) # CANT_SERIES/CANT_SERIESDEF # Weight diff --git a/backend/api/v1/modules/a76/items/line_quantities/schemas.py b/backend/api/v1/modules/a76/items/line_quantities/schemas.py index f8582b99..4aeed5db 100644 --- a/backend/api/v1/modules/a76/items/line_quantities/schemas.py +++ b/backend/api/v1/modules/a76/items/line_quantities/schemas.py @@ -16,9 +16,6 @@ class LineQuantityBase(BaseModel): # Quantities - Special (SCAF specific) quantity_temp_export: Optional[Decimal] = Field(None, description="Temporary export quantity (CANTEXPOTEMP)") - quantity_existence: Optional[Decimal] = Field(None, description="Existence quantity (CANTEXISTENCIA)") - quantity_returned: Optional[Decimal] = Field(None, description="Returned quantity (CANTRETORNADA)") - quantity_returned_temp: Optional[Decimal] = Field(None, description="Returned temporary quantity (CANTRETORNADATEMP)") serial_count: Optional[int] = Field(None, description="Serial count (CANT_SERIES/CANT_SERIESDEF)") # Weight diff --git a/backend/api/v1/modules/a76/items/service.py b/backend/api/v1/modules/a76/items/service.py index dbbab8e3..411c8393 100644 --- a/backend/api/v1/modules/a76/items/service.py +++ b/backend/api/v1/modules/a76/items/service.py @@ -44,6 +44,7 @@ from api.v1.modules.a76.invoices.models import InvoiceHeader from api.v1.modules.a76.parts.models import Part from api.v1.modules.a76.general_catalogs.identifiers.models import IdentifierDetail from api.v1.modules.a24.balance_movements.models import BalanceMovement, NEGATIVE_MOVEMENTS +from api.v1.modules.a24.discharges.models import DischargeDetail, DischargeHeader, DischargeStatus logger = logging.getLogger(__name__) @@ -733,12 +734,19 @@ class ItemService: ) result = [] + used_map = ItemService._used_quantities_by_procedure( + db=db, + import_line_ids=[line.id for line in lines], + as_of_date=as_of_date, + ) for line in lines: available_balance = ItemService._compute_balance(db, line.id, as_of_date) qty = line.quantity desc = line.description fa = line.fa_data inv = line.invoice + qty_used_temp = used_map.get((line.id, "TEM"), Decimal(0)) + qty_used_def = used_map.get((line.id, "DEF"), Decimal(0)) # Count subitems (lines that reference this line as parent via subitem_number) subitem_count = 0 @@ -770,8 +778,8 @@ class ItemService: "unit_of_measure_code": line.unit_of_measure_info.code if line.unit_of_measure_info else None, # Quantities "quantity": float(qty.quantity) if qty and qty.quantity is not None else None, - "quantity_returned_temp": float(qty.quantity_returned_temp) if qty and qty.quantity_returned_temp is not None else None, - "quantity_returned": float(qty.quantity_returned) if qty and qty.quantity_returned is not None else None, + "quantity_used_temp": float(qty_used_temp), + "quantity_used_def": float(qty_used_def), # Balance "available_balance": float(available_balance), "has_balance": available_balance > Decimal(0), @@ -811,3 +819,39 @@ class ItemService: ) ).scalar() return Decimal(str(result or 0)) + + @staticmethod + def _used_quantities_by_procedure( + db: Session, + import_line_ids: List[int], + as_of_date: Optional[datetime.date], + ) -> dict[tuple[int, str], Decimal]: + """ + Returns net used quantity by import line and procedence (TEM/DEF), + based on active discharge records only (new ledger logic). + """ + if not import_line_ids: + return {} + + query = ( + select( + DischargeDetail.import_item_line_id, + DischargeDetail.procedence, + func.sum(DischargeDetail.quantity_discharged), + ) + .join(DischargeHeader, DischargeHeader.id == DischargeDetail.discharge_header_id) + .where( + DischargeDetail.import_item_line_id.in_(import_line_ids), + DischargeHeader.status == DischargeStatus.APPLIED, + DischargeDetail.procedence.in_(["TEM", "DEF"]), + ) + .group_by(DischargeDetail.import_item_line_id, DischargeDetail.procedence) + ) + if as_of_date is not None: + query = query.where(DischargeHeader.discharge_date <= as_of_date) + + rows = db.execute(query).all() + out: dict[tuple[int, str], Decimal] = {} + for line_id, procedence, qty in rows: + out[(int(line_id), str(procedence))] = Decimal(str(qty or 0)) + return out diff --git a/backend/api/v1/modules/a76/parts/dto.py b/backend/api/v1/modules/a76/parts/dto.py index aa241e22..a6815a01 100644 --- a/backend/api/v1/modules/a76/parts/dto.py +++ b/backend/api/v1/modules/a76/parts/dto.py @@ -6,9 +6,9 @@ from api.v1.modules.a24.inv.inv_aphis.dto import InvPartAphisGeneralDTO # --- SUB-DTO: DATOS ADUANALES (FaData) --- class FaDataDTO(BaseModel): - origin_country: Optional[str] = Field(default=None, pattern=r"^[A-Z]{3}$") - sector: Optional[str] = Field(default=None, pattern=r"^[A-Za-z0-9]{1,8}$") - fraction_type: Optional[Literal["GENERAL", "PROSEC", "ALADI", "TLCS"]] = None + origin_country: Optional[str] = None + sector: Optional[str] = None + fraction_type: Optional[str] = None model_config = ConfigDict(from_attributes=True) diff --git a/backend/api/v1/modules/a76/parts/service.py b/backend/api/v1/modules/a76/parts/service.py index 6779d13f..334f7ffd 100644 --- a/backend/api/v1/modules/a76/parts/service.py +++ b/backend/api/v1/modules/a76/parts/service.py @@ -22,8 +22,7 @@ logger = logging.getLogger(__name__) class PartService: """Servicio para gestión de Partes (Anexo 76 + Anexo 24)""" - # Estos campos están en el modelo pero NO en la DB todavía (faltan las migraciones del usuario) - # Los diferimos en SELECT y los filtramos en INSERT/UPDATE para que el sistema no truene. + # Estos campos están en el modelo pero NO en la DB todavía, faltan las migraciones del usuario MISSING_INV_COLUMNS = [] # Campos que SÍ existen en la DB (Verificados con \d a24.inv_partes) diff --git a/backend/api/v1/modules/a76/reports/movements/invoices/services_old.py b/backend/api/v1/modules/a76/reports/movements/invoices/services_old.py deleted file mode 100644 index 66c888df..00000000 --- a/backend/api/v1/modules/a76/reports/movements/invoices/services_old.py +++ /dev/null @@ -1,2115 +0,0 @@ -import logging -from sqlalchemy import text -from sqlalchemy.orm import Session -import configparser -import os -from typing import List, Optional -from datetime import datetime -from decimal import Decimal - -from .schemas import ( - ImportTemporaryFilter, - ImportDefinitiveFilter, - ImportRepairFilter, - MovementItem, - MovementItemDetailed, - RangeType -) - -logger = logging.getLogger(__name__) - - -class MovementService: - """ - Service for handling movement operations, particularly temporary import movements. - Integrates with legacy SQL Server databases for data extraction. - """ - - def _get_met_trans_config(self) -> int: - """ - Read MetTrans configuration from Scaii.ini file. - - Returns: - MetTrans value (0 or 1) - """ - try: - config = configparser.ConfigParser() - config.read('Scaii.ini') - met_trans = config.getint('METTRANS', 'TipoCambio', fallback=0) - logger.debug(f"INI met_trans value: {met_trans}") - return met_trans - except Exception as e: - logger.warning(f"Could not read Scaii.ini, using default met_trans=0: {e}") - return 0 - - def _build_where_clause_temporary(self, filters: ImportTemporaryFilter) -> tuple: - """ - Build WHERE clause and parameters for temporary imports query. - - Returns: - Tuple of (where_string, params_dict) - """ - where_clauses = [] - params = {} - - # Date range filter - if filters.range_type.value == 'FF': - where_clauses.append("EqiFim.FechaFactura >= :start_date AND EqiFim.FechaFactura <= :end_date") - else: - where_clauses.append("EqiPed.Fecha_Pago >= :start_date AND EqiPed.Fecha_Pago <= :end_date") - - params['start_date'] = filters.start_date - params['end_date'] = filters.end_date - - # Status filter - if not filters.include_cancelled: - where_clauses.append("EqiFim.Estatus = 'AC'") - - # Provider filter - if filters.provider: - where_clauses.append("EqiFim.Proveedor = :provider") - params['provider'] = filters.provider - - # Buyer filter - if filters.buyer: - where_clauses.append("EqiFim.VendidoA = :buyer") - params['buyer'] = filters.buyer - - # Pedimento code filter - if filters.pedimento_code: - where_clauses.append("EqiPed.ClavePed = :pedimento_code") - params['pedimento_code'] = filters.pedimento_code - - return " AND ".join(where_clauses), params - - def _build_where_clause_definitive(self, filters: ImportDefinitiveFilter) -> str: - """ - Build WHERE clause for definitive imports query. - - Returns: - WHERE clause string - """ - where_conditions = [] - - # Date range filter - if filters.range_type.value == "FF": - where_conditions.append(f"EqiFid.FechaFactura >= '{filters.start_date}' AND EqiFid.FechaFactura <= '{filters.end_date}'") - else: - where_conditions.append(f"EqiPed.Fecha_Pago >= '{filters.start_date}' AND EqiPed.Fecha_Pago <= '{filters.end_date}'") - - # Status filter - if not filters.include_cancelled: - where_conditions.append("EqiFid.Estatus = 'AC'") - - # Provider filter - if filters.provider: - where_conditions.append(f"EqiFid.Proveedor = '{filters.provider}'") - - # Buyer filter - if filters.buyer: - where_conditions.append(f"EqiFid.VendidoA = '{filters.buyer}'") - - # Pedimento code filter - if filters.pedimento_code: - where_conditions.append(f"EqiPed.ClavePed = '{filters.pedimento_code}'") - - # Movement type filter - if filters.movement_type.value == "COMEX": - where_conditions.append("EqiFid.ProvImpoDefCR = 'P'") - elif filters.movement_type.value == "IMPDF": - where_conditions.append("EqiFid.ProvImpoDefCR != 'P'") - - return " AND ".join(where_conditions) - - def _calculate_exchange_rate_and_value( - self, - db: Session, - db_name: str, - valor_me: float, - valor_mn: float, - tipo_cambio_db: float, - fecha_pago, - fecha_inicio, - tipo_pedimento: str, - currency_type: str, - exchange_rate_type: str, - is_shelter: bool, - use_transport_method: bool, - met_trans: int - ) -> tuple: - """ - Unified method to calculate exchange rate and commercial value. - Eliminates duplicated logic across temporary and definitive imports. - - Args: - db: Database session - db_name: Database name - valor_me: Value in foreign currency - valor_mn: Value in local currency - tipo_cambio_db: Exchange rate from database - fecha_pago: Payment date - fecha_inicio: Start date - tipo_pedimento: Pedimento type - currency_type: "ME" or "MN" - exchange_rate_type: "FP" or "FF" - is_shelter: Shelter company flag - use_transport_method: Use transport method flag - met_trans: MetTrans value from config - - Returns: - Tuple of (valor_comercial_mn, tipo_cambio) - """ - # Foreign currency case - simpler - if currency_type == "ME": - valor_comercial = valor_me - tipo_cambio = tipo_cambio_db - - # Try to get exchange rate if using payment date - if exchange_rate_type == "FP" and fecha_pago: - fecha_tc = self._get_fecha_tipo_cambio( - fecha_pago, fecha_inicio, tipo_pedimento, use_transport_method, met_trans - ) - tc_value = self._obtener_tipo_cambio(db, db_name, fecha_tc, is_shelter) - if tc_value: - tipo_cambio = tc_value - - return valor_comercial, tipo_cambio - - # Local currency case - more complex - if exchange_rate_type == "FP" and fecha_pago: - fecha_tc = self._get_fecha_tipo_cambio( - fecha_pago, fecha_inicio, tipo_pedimento, use_transport_method, met_trans - ) - tc_value = self._obtener_tipo_cambio(db, db_name, fecha_tc, is_shelter) - - if tc_value: - return valor_me * tc_value, tc_value - else: - logger.warning(f"Exchange rate not found for date {fecha_tc}, using DB values") - return valor_mn, tipo_cambio_db - else: - return valor_mn, tipo_cambio_db - - def _get_fecha_tipo_cambio( - self, - fecha_pago, - fecha_inicio, - tipo_pedimento: str, - use_transport_method: bool, - met_trans: int - ): - """ - Determine which date to use for exchange rate lookup. - - Returns: - Date to use for exchange rate - """ - fecha = fecha_pago - if use_transport_method and met_trans == 1: - if tipo_pedimento in ('1', '4', '98E'): - fecha = fecha_inicio - return fecha - - def get_temporary_import_movements( - self, - db: Session, - filters: ImportTemporaryFilter - ) -> List[MovementItem]: - """ - Retrieve temporary import movements from legacy database. - - Args: - db: Database session - filters: Filter criteria for the query - - Returns: - List of movement items matching the criteria - - Raises: - Exception: If database query fails - """ - logger.info(f"Fetching temporary import movements with filters: {filters.model_dump()}") - - # Read INI configuration for exchange rate logic - met_trans = 0 - try: - config = configparser.ConfigParser() - config.read('Scaii.ini') - met_trans = config.getint('METTRANS', 'TipoCambio', fallback=0) - logger.debug(f"INI met_trans value: {met_trans}") - except Exception as e: - logger.warning(f"Could not read Scaii.ini, using default met_trans=0: {e}") - - # Build WHERE clause dynamically based on filters - where_clauses = [] - params = {} - - # Date range filter - if filters.range_type == 'FF': - where_clauses.append( - "EqiFim.FechaFactura >= :start_date AND EqiFim.FechaFactura <= :end_date" - ) - else: - where_clauses.append( - "EqiPed.Fecha_Pago >= :start_date AND EqiPed.Fecha_Pago <= :end_date" - ) - - params['start_date'] = filters.start_date - params['end_date'] = filters.end_date - - # Status filter - if not filters.include_cancelled: - where_clauses.append("EqiFim.Estatus = 'AC'") - - # Provider filter - if filters.provider: - where_clauses.append("EqiFim.Proveedor = :provider") - params['provider'] = filters.provider - - # Buyer filter - if filters.buyer: - where_clauses.append("EqiFim.VendidoA = :buyer") - params['buyer'] = filters.buyer - - # Pedimento code filter - if filters.pedimento_code: - where_clauses.append("EqiPed.ClavePed = :pedimento_code") - params['pedimento_code'] = filters.pedimento_code - - where_str = " AND ".join(where_clauses) - db_name = filters.database_name - - logger.debug(f"WHERE clause: {where_str}") - logger.debug(f"Query params: {params}") - - # Main Query (using shared query builder) - sql_query = text(self._build_main_query(db_name, where_str)) - - try: - result = db.execute(sql_query, params) - rows = result.fetchall() - logger.info(f"Query returned {len(rows)} rows") - except Exception as e: - logger.error(f"Error executing main query: {e}") - raise Exception(f"Database query failed: {str(e)}") - - movements = [] - processed_facturas = set() - - for row in rows: - if filters.report_type == 'Normal': - C1_Factura = row[0] - - if C1_Factura in processed_facturas: - continue - - qcsv_ie = {} - processed_facturas.add(C1_Factura) - - C39_Consecutivo = row[38] - - # Totalize Items (sum values for main partidas only) - sql_total = text(f""" - SELECT - COALESCE(SUM(EqiPim.ValorImpoME), 0), - COALESCE(SUM(EqiPim.ValorImpoMN), 0) - FROM [{db_name}].dbo.QEqiMaq EqiPim - WHERE EqiPim.Consecutivo = :consecutivo - AND EqiPim.EsSubpartida = 'P' - """) - - try: - res_total = db.execute(sql_total, {"consecutivo": C39_Consecutivo}).fetchone() - val_total_me = float(res_total[0]) if res_total and res_total[0] is not None else 0.0 - val_total_mn = float(res_total[1]) if res_total and res_total[1] is not None else 0.0 - except Exception as e: - logger.warning(f"Error calculating totals for consecutivo {C39_Consecutivo}: {e}") - val_total_me = 0.0 - val_total_mn = 0.0 - - qcsv_ie['Factura'] = row[0] - qcsv_ie['Pedimento'] = row[1] - qcsv_ie['FechaFactura'] = row[2] - qcsv_ie['Estatus'] = row[3] - qcsv_ie['ClavePed'] = row[4] - qcsv_ie['TipoMovTemDef'] = 'IMTEM' - qcsv_ie['EsCambioRegimen'] = 'N' - - row_c13 = row[12] # Fecha_Pago - row_c11 = row[10] # Fecha_Inicio - row_c58 = row[57] # TIPOPEDIMENTOTRANSPORTEE - row_c50 = row[49] # TipoCambio - row_c41 = row[40] # PedRectifica - row_c2 = row[1] # PedimentoImpo - - calculated_tc = row_c50 - - # Calculate values based on currency type - if filters.currency_type == 'ME': - qcsv_ie['ValorMPTemp'] = val_total_me - qcsv_ie['ValorComercialMN'] = val_total_me - - if filters.exchange_rate_type == 'FP' and row_c13: - calculated_tc = self._obtener_tipo_cambio( - db, db_name, row_c13, row_c11, row_c58, met_trans - ) - qcsv_ie['TipoCambio'] = calculated_tc if calculated_tc > 0 else row_c50 - else: - qcsv_ie['TipoCambio'] = row_c50 - else: - if filters.is_shelter: - if filters.exchange_rate_type == 'FP' and row_c13: - calculated_tc = self._obtener_tipo_cambio_mn(db, db_name, row_c13, row_c11, row_c58, met_trans) - qcsv_ie['ValorMPTemp'] = val_total_me * calculated_tc - qcsv_ie['ValorComercialMN'] = val_total_me * calculated_tc - qcsv_ie['TipoCambio'] = calculated_tc - else: - qcsv_ie['ValorMPTemp'] = val_total_mn - qcsv_ie['ValorComercialMN'] = val_total_mn - qcsv_ie['TipoCambio'] = row_c50 - else: - if filters.exchange_rate_type == 'FP' and row_c13: - calculated_tc = self._obtener_tipo_cambio(db, db_name, row_c13, row_c11, row_c58, met_trans) - qcsv_ie['ValorMPTemp'] = val_total_me * calculated_tc - qcsv_ie['ValorComercialMN'] = val_total_me * calculated_tc - qcsv_ie['TipoCambio'] = calculated_tc - else: - qcsv_ie['ValorMPTemp'] = val_total_mn - qcsv_ie['ValorComercialMN'] = val_total_mn - qcsv_ie['TipoCambio'] = row_c50 - - qcsv_ie['ValorAgre'] = 0.0 - qcsv_ie['TipoExpo'] = '' - - if filters.is_shelter: - qcsv_ie['PedimentoR1'] = row_c41 - else: - qcsv_ie['PedimentoR1'] = self._buscar_rectificacion(db, db_name, row_c2, row_c41) - - qcsv_ie['EDocument'] = row[41] - qcsv_ie['NumOperacionVU'] = row[42] - qcsv_ie['BaseDeDatos'] = db_name - - # Get driver badge number (gafete) - sql_gafete = text(f""" - SELECT TOP 1 NUMGAFETEUNICO - FROM [{db_name}].dbo.GConductor - LEFT JOIN [{db_name}].dbo.QFacImp - ON QFacImp.CONDUCTOR = GConductor.CONDUCTOR - WHERE FacturaImpo = :factura - """) - try: - res_gafete = db.execute(sql_gafete, {"factura": row[0]}).fetchone() - qcsv_ie['NumGafUni'] = res_gafete[0] if res_gafete and res_gafete[0] else None - except Exception as e: - logger.debug(f"Could not retrieve badge for invoice {row[0]}: {e}") - qcsv_ie['NumGafUni'] = None - - qcsv_ie['UsuarioCap'] = row[51] - qcsv_ie['UsuarioAcr'] = row[52] - qcsv_ie['Fecha_Pago'] = row[12] - qcsv_ie['NumCaja'] = row[54] - qcsv_ie['Pedimento18'] = row[55] - qcsv_ie['AduanaCru'] = row[37] - qcsv_ie['Lote'] = row[56] - - movements.append(MovementItem(**qcsv_ie)) - - return movements - - def _obtener_tipo_cambio( - self, - db: Session, - db_name: str, - fecha, - is_shelter: bool - ) -> Optional[float]: - """ - Get exchange rate for the given date. - Simplified version that works with both shelter and non-shelter logic. - - Args: - db: Database session - db_name: Legacy database name - fecha: Date for exchange rate lookup - is_shelter: Shelter company flag (currently not used but kept for compatibility) - - Returns: - Exchange rate as float, or None if not found - """ - if not fecha: - return None - - try: - sql_tc = text(f""" - SELECT TOP 1 Valor - FROM [{db_name}].dbo.GTipoCambio - WHERE Fecha = :fecha - ORDER BY Fecha DESC - """) - res = db.execute(sql_tc, {"fecha": fecha}).fetchone() - if res and res[0]: - return float(res[0]) - else: - logger.warning(f"Exchange rate not found for date {fecha}") - return None - except Exception as e: - logger.error(f"Error fetching exchange rate for date {fecha}: {e}") - return None - - def _buscar_rectificacion( - self, - pedimento: str, - ped_rectifica: Optional[str] - ) -> Optional[str]: - """ - Search for pedimento rectification. - Simplified version - returns the rectification value from database. - - Args: - pedimento: Original pedimento number - ped_rectifica: Rectification pedimento from query - - Returns: - Rectification pedimento number or None - """ - # TODO: Implement full rectification search logic if needed - # For now, returning the value from the query - return ped_rectifica - - def get_temporary_import_movements_detailed( - self, - db: Session, - filters: ImportTemporaryFilter - ) -> List[MovementItemDetailed]: - """ - Retrieve detailed temporary import movements (line by line) from legacy database. - - Args: - db: Database session - filters: Filter criteria for the query - - Returns: - List of detailed movement items (one per line/partida) - """ - logger.info(f"Fetching DETAILED temporary import movements with filters: {filters.model_dump()}") - - # Read INI configuration - met_trans = 0 - try: - config = configparser.ConfigParser() - config.read('Scaii.ini') - met_trans = config.getint('METTRANS', 'TipoCambio', fallback=0) - logger.debug(f"INI met_trans value: {met_trans}") - except Exception as e: - logger.warning(f"Could not read Scaii.ini, using default met_trans=0: {e}") - - # Build WHERE clause (same as normal report) - where_clauses = [] - params = {} - - if filters.range_type == 'FF': - where_clauses.append( - "EqiFim.FechaFactura >= :start_date AND EqiFim.FechaFactura <= :end_date" - ) - else: - where_clauses.append( - "EqiPed.Fecha_Pago >= :start_date AND EqiPed.Fecha_Pago <= :end_date" - ) - - params['start_date'] = filters.start_date - params['end_date'] = filters.end_date - - if not filters.include_cancelled: - where_clauses.append("EqiFim.Estatus = 'AC'") - - if filters.provider: - where_clauses.append("EqiFim.Proveedor = :provider") - params['provider'] = filters.provider - - if filters.buyer: - where_clauses.append("EqiFim.VendidoA = :buyer") - params['buyer'] = filters.buyer - - if filters.pedimento_code: - where_clauses.append("EqiPed.ClavePed = :pedimento_code") - params['pedimento_code'] = filters.pedimento_code - - where_str = " AND ".join(where_clauses) - db_name = filters.database_name - - logger.debug(f"WHERE clause: {where_str}") - logger.debug(f"Query params: {params}") - - # Same main query as normal report - sql_query = text(self._build_main_query(db_name, where_str)) - - try: - result = db.execute(sql_query, params) - rows = result.fetchall() - logger.info(f"Query returned {len(rows)} rows for detailed processing") - except Exception as e: - logger.error(f"Error executing main query: {e}") - raise Exception(f"Database query failed: {str(e)}") - - movements = [] - - # Process each row individually (detailed mode) - for row in rows: - item = {} - - # Basic invoice info - item['Linea'] = row[43] # C44 - LineaImpo - item['Factura'] = row[0] # C1 - item['Pedimento'] = row[1] # C2 - item['FechaFactura'] = row[2] # C3 - item['Estatus'] = row[3] # C4 - item['ClavePed'] = row[4] # C5 - item['TipoMovTemDef'] = 'IMTEM' - item['EsCambioRegimen'] = 'N' - item['Regimen'] = row[9] # C10 - item['Fecha_Inicio'] = row[10] # C11 - item['Fecha_Fin'] = row[11] # C12 - item['Fecha_Pago'] = row[12] # C13 - item['Remesa'] = row[13] # C14 - - # Get provider information - provider_code = row[15] # C16 - Proveedor - provider_info = self._get_client_provider_info(db, db_name, provider_code, filters.is_shelter) - item['Proveedor'] = provider_info.get('nombre') - item['RFCProveedor'] = provider_info.get('rfc') - item['ProveedorTaxID'] = provider_info.get('tax_id') - - # Get buyer information - buyer_code = row[16] # C17 - VendidoA - buyer_info = self._get_client_buyer_info(db, db_name, buyer_code, filters.is_shelter) - item['VendidoA'] = buyer_info.get('nombre') - item['VendidoARFC'] = buyer_info.get('rfc') - item['VendidoATaxID'] = buyer_info.get('tax_id') - - # Get customs broker info - customs_broker_code = row[17] # C18 - AAduanal - broker_info = self._get_customs_broker_info(db, db_name, customs_broker_code) - item['AgenteAduanal'] = broker_info.get('nombre') - item['Patente'] = broker_info.get('patente') - - # Item details - item['NumParte'] = row[19] # C20 - Clase (NumParte) - item['DescripcionE'] = self._clean_text(row[20]) # C21 - Already cleaned in query - item['DescripcionI'] = self._clean_text(row[21]) # C22 - Already cleaned in query - item['CantidadIE'] = float(row[22]) if row[22] else 0.0 # C23 - item['UniMed'] = row[23] # C24 - - # Values and exchange rate (only for main partidas, not subpartidas) - if row[39] == 'P': # C40 - EsSubPartida == 'P' - # Calculate value based on currency type - if filters.currency_type == 'MN': - if filters.is_shelter: - if filters.exchange_rate_type == 'FP' and row[12]: # C13 - Fecha_Pago - tc = self._obtener_tipo_cambio(db, db_name, row[12], row[10], row[57], met_trans) - item['ValorComercialMN'] = float(row[26]) * tc if row[26] else 0.0 # C27 * TC - item['TipoCambio'] = tc - else: - item['ValorComercialMN'] = float(row[24]) if row[24] else 0.0 # C25 - item['TipoCambio'] = float(row[49]) if row[49] else 0.0 # C50 - else: - if filters.exchange_rate_type == 'FP' and row[12]: - tc = self._obtener_tipo_cambio(db, db_name, row[12], row[10], row[57], met_trans) - item['ValorComercialMN'] = float(row[26]) * tc if row[26] else 0.0 # C27 * TC - item['TipoCambio'] = tc - else: - item['ValorComercialMN'] = float(row[24]) if row[24] else 0.0 # C25 - item['TipoCambio'] = float(row[49]) if row[49] else 0.0 # C50 - elif filters.currency_type == 'ME': - item['ValorComercialMN'] = float(row[26]) if row[26] else 0.0 # C27 - if filters.exchange_rate_type == 'FP' and row[12]: - tc = self._obtener_tipo_cambio(db, db_name, row[12], row[10], row[57], met_trans) - item['TipoCambio'] = tc if tc > 0 else float(row[49]) if row[49] else 0.0 - else: - item['TipoCambio'] = float(row[49]) if row[49] else 0.0 # C50 - - item['PesoNeto'] = float(row[28]) if row[28] else 0.0 # C29 - item['PesoBruto'] = float(row[29]) if row[29] else 0.0 # C30 - elif row[39] == 'S': # Subpartida - item['ValorComercialMN'] = 0.0 - item['PesoNeto'] = 0.0 - item['PesoBruto'] = 0.0 - item['TipoCambio'] = 0.0 - - # Additional fields - item['OrdenCompraVenta'] = row[30] # C31 - item['FraccionArancelaria'] = row[31] # C32 - item['Preferencia'] = row[32] # C33 - item['Sector'] = row[34] # C35 - item['PaisOrigen'] = row[36] # C37 - - # Get customs office name - aduana_code = row[37] # C38 - Aduana_Cruce - aduana_name = self._get_customs_office_name(db, db_name, aduana_code) - item['Aduana'] = aduana_name - - item['Advalorem'] = row[39] # C40 - item['TipoExpo'] = '' - - # Rectification - if filters.is_shelter: - item['PedimentoR1'] = row[40] # C41 - else: - item['PedimentoR1'] = self._buscar_rectificacion(db, db_name, row[1], row[40]) - - item['EDocument'] = row[41] # C42 - item['NumOperacionVU'] = row[42] # C43 - - # Get series information - consecutivo = row[38] # C39 - linea_impo = row[43] # C44 - series_info = self._get_series_info(db, db_name, consecutivo, linea_impo, filters.is_shelter) - item['Series'] = series_info - - item['Marca'] = row[44] # C45 - item['Modelo'] = row[45] # C46 - item['FraccionAmericana'] = row[46] # C47 - item['ECCN'] = row[47] # C48 - - # Get export symbol from parts - num_parte = row[48] # C49 - if num_parte: - simbolo_ex = self._get_part_export_symbol(db, db_name, num_parte, filters.is_shelter) - item['SimboloEx'] = simbolo_ex - else: - item['SimboloEx'] = None - - item['FechaEmision'] = row[50] # C51 - item['BaseDeDatos'] = db_name - - # Get driver badge - factura = row[0] # C1 - try: - sql_gafete = text(f""" - SELECT TOP 1 NUMGAFETEUNICO - FROM [{db_name}].dbo.GConductor - LEFT JOIN [{db_name}].dbo.QFacImp - ON QFacImp.CONDUCTOR = GConductor.CONDUCTOR - WHERE FacturaImpo = :factura - """) - res_gafete = db.execute(sql_gafete, {"factura": factura}).fetchone() - item['NumGafUni'] = res_gafete[0] if res_gafete and res_gafete[0] else None - except Exception as e: - logger.debug(f"Could not retrieve badge for invoice {factura}: {e}") - item['NumGafUni'] = None - - item['UsuarioCap'] = row[51] # C52 - item['UsuarioAcr'] = row[52] # C53 - item['Transportista'] = row[53] # C54 - item['NumCaja'] = row[54] # C55 - item['Pedimento18'] = row[55] # C56 - item['AduanaCru'] = row[37] # C38 - item['Lote'] = row[56] # C57 - - movements.append(MovementItemDetailed(**item)) - - logger.info(f"Processed {len(movements)} detailed movement items") - return movements - - def _build_main_query(self, db_name: str, where_str: str) -> str: - """Build the main SQL query for fetching invoice data""" - return f""" - SELECT - EqiFim.FacturaImpo AS C1, - EqiFim.PedimentoImpo AS C2, - EqiFim.FechaFactura AS C3, - EqiFim.Estatus AS C4, - EqiPed.ClavePed AS C5, - EqiFim.ValorImpoME AS C6, - EqiFim.ValorImpoMN AS C7, - EqiFim.Proveedor AS C8, - EqiFim.VendidoA AS C9, - EqiPed.Regimen AS C10, - EqiPed.Fecha_Inicio AS C11, - EqiPed.Fecha_Fin AS C12, - EqiPed.Fecha_Pago AS C13, - EqiFim.Remesa AS C14, - EqiFim.TipoCambio AS C15, - EqiFim.Proveedor AS C16, - EqiFim.VendidoA AS C17, - EqiFim.AAduanal AS C18, - '' AS C19, - EqiPim.Clase AS C20, - REPLACE(REPLACE(REPLACE(REPLACE(EqiPim.DescripcionE, CHAR(44), ' '), CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' ') AS C21, - REPLACE(REPLACE(REPLACE(REPLACE(ClaAct.DescripcionI, CHAR(44), ' '), CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' ') AS C22, - EqiPim.CantImpo AS C23, - EqiPim.UnidadMedida AS C24, - EqiPim.ValorImpoMN AS C25, - EqiPim.ValorAduanasMN AS C26, - EqiPim.ValorImpoME AS C27, - EqiPim.ValorAduanasME AS C28, - EqiPim.PesoNeto AS C29, - EqiPim.PesoBruto AS C30, - EqiPim.OrdenCompra AS C31, - EqiPim.Fraccion AS C32, - EqiPim.TipoFraccion AS C33, - EqiPim.AdvImpo AS C34, - EqiPim.Sector AS C35, - EqiPim.MontoIgi AS C36, - EqiPim.PaisOrigen AS C37, - EqiPed.Aduana_Cruce AS C38, - EqiFim.Consecutivo AS C39, - EqiPim.EsSubPartida AS C40, - EqiPed.PedRectifica AS C41, - EqiFim.EDocument AS C42, - EqiFim.NumOperacionVU AS C43, - EqiPim.LineaImpo AS C44, - REPLACE(REPLACE(REPLACE(REPLACE(EqiPim.Marca, CHAR(44), ' '), CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' ') AS C45, - REPLACE(REPLACE(REPLACE(REPLACE(EqiPim.Modelo, CHAR(44), ' '), CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' ') AS C46, - ClaAct.FraccionAme AS C47, - ClaAct.ECCN AS C48, - EqiPim.NumParte AS C49, - EqiFim.TipoCambio AS C50, - EqiFim.FechaEmision AS C51, - EqiFim.UsuarioCap AS C52, - EqiFim.UsuarioAct AS C53, - EqiFim.Transportista AS C54, - EqiFim.Transporte + ' ' + EqiFim.NumTrasporte AS C55, - EqiPed.Pedimento18 AS C56, - EqiPim.LOTE AS C57, - EqiPed.TIPOPEDIMENTOTRANSPORTEE AS C58 - FROM [{db_name}].dbo.QFacImp EqiFim - LEFT JOIN [{db_name}].dbo.QPedimentos EqiPed - ON EqiPed.Pedimento = EqiFim.PedimentoImpo - LEFT JOIN [{db_name}].dbo.QEqiMaq EqiPim - ON EqiPim.Consecutivo = EqiFim.Consecutivo - LEFT JOIN [{db_name}].dbo.QClaAct ClaAct - ON ClaAct.Clase = EqiPim.Clase - WHERE {where_str} - """ - - def _clean_text(self, text: Optional[str]) -> Optional[str]: - """Clean text by removing special characters""" - if not text: - return None - return text.strip() - - def _get_client_provider_info(self, db: Session, db_name: str, client_code: str, is_shelter: bool) -> dict: - """Get provider/client information""" - if not client_code: - return {"nombre": None, "rfc": None, "tax_id": None} - - try: - sql = text(f""" - SELECT TOP 1 Nombre, RFC, TaxID - FROM [{db_name}].dbo.GClientesPro - WHERE Cliente = :cliente - """) - result = db.execute(sql, {"cliente": client_code}).fetchone() - - if result: - return { - "nombre": self._clean_text(result[0]), - "rfc": result[1], - "tax_id": result[2] - } - except Exception as e: - logger.warning(f"Error fetching provider info for {client_code}: {e}") - - return {"nombre": None, "rfc": None, "tax_id": None} - - def _get_client_buyer_info(self, db: Session, db_name: str, client_code: str, is_shelter: bool) -> dict: - """Get buyer information (same structure as provider)""" - return self._get_client_provider_info(db, db_name, client_code, is_shelter) - - def _get_customs_broker_info(self, db: Session, db_name: str, broker_code: str) -> dict: - """Get customs broker information""" - if not broker_code: - return {"nombre": None, "patente": None} - - try: - sql = text(f""" - SELECT TOP 1 Nombre, Patente - FROM [{db_name}].dbo.GAAduanal - WHERE ClaveAA = :clave - """) - result = db.execute(sql, {"clave": broker_code}).fetchone() - - if result: - return { - "nombre": result[0], - "patente": result[1] - } - except Exception as e: - logger.warning(f"Error fetching customs broker info for {broker_code}: {e}") - - return {"nombre": None, "patente": None} - - def _get_customs_office_name(self, db: Session, db_name: str, aduana_code: str) -> Optional[str]: - """Get customs office name""" - if not aduana_code: - return None - - try: - sql = text(f""" - SELECT TOP 1 REPLACE(REPLACE(REPLACE(REPLACE(Nombre, CHAR(44), ' '), CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' ') - FROM [{db_name}].dbo.GAduanaSec - WHERE AduanaSeccion = :aduana - """) - result = db.execute(sql, {"aduana": aduana_code}).fetchone() - return result[0] if result else None - except Exception as e: - logger.warning(f"Error fetching customs office name for {aduana_code}: {e}") - return None - - def _get_series_info(self, db: Session, db_name: str, consecutivo: str, linea_impo: str, is_shelter: bool) -> Optional[str]: - """Get series information for an item""" - if not consecutivo or not linea_impo: - return None - - try: - sql = text(f""" - SELECT SerieImpo, ModeloImpo, ParteImpo - FROM [{db_name}].dbo.QSeriesImpo - WHERE Consecutivo = :consecutivo - AND LineaImpo = :linea - ORDER BY RenImpo - """) - result = db.execute(sql, {"consecutivo": consecutivo, "linea": linea_impo}).fetchall() - - if not result: - return None - - # Build series string - series_parts = [] - for idx, row in enumerate(result, 1): - serie = row[0] - modelo = row[1] - parte = row[2] - - serie_str = f"{idx}) {serie}" - if modelo: - serie_str += f". Modelo: {modelo}" - if parte: - serie_str += f". Parte: {parte}" - - series_parts.append(serie_str) - - return " | ".join(series_parts) if series_parts else None - - except Exception as e: - logger.warning(f"Error fetching series for {consecutivo}/{linea_impo}: {e}") - return None - - def _get_part_export_symbol(self, db: Session, db_name: str, num_parte: str, is_shelter: bool) -> Optional[str]: - """Get export symbol/license for a part number""" - if not num_parte: - return None - - try: - sql = text(f""" - SELECT TOP 1 SimboloExcLic - FROM [{db_name}].dbo.QPartes - WHERE NumParte = :num_parte - """) - result = db.execute(sql, {"num_parte": num_parte}).fetchone() - return result[0] if result else None - except Exception as e: - logger.debug(f"Error fetching export symbol for part {num_parte}: {e}") - return None - - def get_definitive_import_movements( - self, - db: Session, - filters: ImportDefinitiveFilter - ) -> List[MovementItem]: - """ - Retrieve definitive import movements from legacy database (LLENADODEFINITIVO - NORMAL). - Aggregates movements by invoice number. - - Args: - db: Database session - filters: Filter criteria for the query - - Returns: - List of movement items matching the criteria - - Raises: - Exception: If database query fails - """ - logger.info(f"Fetching definitive import movements with filters: {filters.model_dump()}") - - # Read INI configuration for exchange rate logic - met_trans = 0 - try: - config = configparser.ConfigParser() - config.read('Scaii.ini') - met_trans = config.getint('METTRANS', 'TipoCambio', fallback=0) - logger.debug(f"INI met_trans value: {met_trans}") - except Exception as e: - logger.warning(f"Could not read Scaii.ini, using default met_trans=0: {e}") - - # Build WHERE clause - where_conditions = [] - - # Date range filter - if filters.range_type.value == "FF": - where_conditions.append(f"EqiFid.FechaFactura >= '{filters.start_date}' AND EqiFid.FechaFactura <= '{filters.end_date}'") - else: # FP - where_conditions.append(f"EqiPed.Fecha_Pago >= '{filters.start_date}' AND EqiPed.Fecha_Pago <= '{filters.end_date}'") - - # Status filter - if not filters.include_cancelled: - where_conditions.append("EqiFid.Estatus = 'AC'") - - # Provider filter - if filters.provider: - where_conditions.append(f"EqiFid.Proveedor = '{filters.provider}'") - - # Buyer filter - if filters.buyer: - where_conditions.append(f"EqiFid.VendidoA = '{filters.buyer}'") - - # Pedimento code filter - if filters.pedimento_code: - where_conditions.append(f"EqiPed.ClavePed = '{filters.pedimento_code}'") - - # Movement type filter - if filters.movement_type.value == "COMEX": - where_conditions.append("EqiFid.ProvImpoDefCR = 'P'") - elif filters.movement_type.value == "IMPDF": - where_conditions.append("EqiFid.ProvImpoDefCR != 'P'") - # If ALL, no filter added - - where_clause = " AND ".join(where_conditions) - - # Build main SQL query - sql_query = text(f""" - SELECT - EqiFid.FacturaImpoDef AS C1, - EqiFid.PedimentoImpoDef AS C2, - EqiFid.FechaFactura AS C3, - EqiFid.Estatus AS C4, - EqiPed.ClavePed AS C5, - EqiFid.ValorImpoME AS C6, - EqiFid.ValorImpoMN AS C7, - EqiFid.Proveedor AS C8, - EqiFid.VendidoA AS C9, - EqiPed.Regimen AS C10, - EqiPed.Fecha_Inicio AS C11, - EqiPed.Fecha_Fin AS C12, - EqiPed.Fecha_Pago AS C13, - EqiFid.Remesa AS C14, - EqiFid.TipoCambio AS C15, - EqiFid.AAduanal AS C18, - EqiFid.Consecutivo AS C40, - EqiPed.PedRectifica AS C42, - EqiFid.EDocument AS C43, - EqiFid.NumOperacionVU AS C44, - EqiFid.TipoCambio AS C51, - EqiFid.FechaEmision AS C52, - EqiFid.UsuarioCap AS C53, - EqiFid.UsuarioAct AS C54, - EqiFid.Transportista AS C55, - EqiFid.Transporte + ' ' + EqiFid.NumTrasporte AS C56, - EqiPed.Pedimento18 AS C57, - EqiPed.Aduana_Cruce AS C38, - EqiFid.ProvImpoDefCR AS C39, - EqiPed.TIPOPEDIMENTOTRANSPORTEE AS C59 - FROM [{filters.database_name}].dbo.QFacImpDef EqiFid - LEFT JOIN [{filters.database_name}].dbo.QPedimentos EqiPed - ON EqiPed.Pedimento = EqiFid.PedimentoImpoDef - WHERE {where_clause} - """) - - try: - result = db.execute(sql_query) - movements_dict = {} - - for row in result: - factura = row[0] # C1 - prov_impo_def_cr = row[28] # C39 - - # Determine movement type - tipo_mov = "COMEX" if prov_impo_def_cr == 'P' else "IMPDF" - - # Use factura + tipo_mov as key - key = (factura, tipo_mov) - - # If already exists, skip (we only want one entry per invoice in Normal mode) - if key not in movements_dict: - consecutivo = row[16] # C40 - fecha_pago = row[12] # C13 - tipo_pedimento = row[29] # C59 - fecha_inicio = row[10] # C11 - - # Calculate total values for this invoice - valor_me, valor_mn = self._calculate_definitive_totals( - db, filters.database_name, consecutivo - ) - - # Calculate exchange rate and values - tipo_cambio = row[20] # C51 - valor_mp_temp = 0.0 - valor_comercial_mn = 0.0 - - if filters.currency_type.value == "ME": - valor_mp_temp = float(valor_me or 0) - valor_comercial_mn = float(valor_me or 0) - tipo_cambio = float(tipo_cambio or 1.0) - else: # MN - if filters.is_shelter: - # Shelter logic with exchange rate calculation - if filters.exchange_rate_type.value == "FP" and fecha_pago: - # Check if special transport method logic applies - fecha_tc = fecha_pago - if filters.use_transport_method and met_trans == 1: - if tipo_pedimento in ('1', '4', '98E'): - fecha_tc = fecha_inicio - - tc_value = self._obtener_tipo_cambio( - db, filters.database_name, fecha_tc, filters.is_shelter - ) - if tc_value: - valor_mp_temp = float(valor_me or 0) * tc_value - valor_comercial_mn = float(valor_me or 0) * tc_value - tipo_cambio = tc_value - else: - logger.warning(f"Exchange rate not found for date {fecha_tc}, using values from DB") - valor_mp_temp = float(valor_mn or 0) - valor_comercial_mn = float(valor_mn or 0) - else: - valor_mp_temp = float(valor_mn or 0) - valor_comercial_mn = float(valor_mn or 0) - else: - # Non-shelter logic - if filters.exchange_rate_type.value == "FP" and fecha_pago: - fecha_tc = fecha_pago - if filters.use_transport_method and met_trans == 1: - if tipo_pedimento in ('1', '4', '98E'): - fecha_tc = fecha_inicio - - tc_value = self._obtener_tipo_cambio( - db, filters.database_name, fecha_tc, filters.is_shelter - ) - if tc_value: - valor_mp_temp = float(valor_me or 0) * tc_value - valor_comercial_mn = float(valor_me or 0) * tc_value - tipo_cambio = tc_value - else: - valor_mp_temp = float(valor_mn or 0) - valor_comercial_mn = float(valor_mn or 0) - else: - valor_mp_temp = float(valor_mn or 0) - valor_comercial_mn = float(valor_mn or 0) - - # Get driver badge number - num_gaf_uni = self._get_driver_badge(db, filters.database_name, factura) - - # Get rectification pedimento - pedimento = row[1] # C2 - ped_rectifica = row[17] # C42 - pedimento_r1 = self._buscar_rectificacion(pedimento, ped_rectifica) - - # Create movement item - movement = MovementItem( - Factura=factura, - Pedimento=row[1], # C2 - FechaFactura=row[2], # C3 - Estatus=row[3], # C4 - ClavePed=row[4], # C5 - TipoMovTemDef=tipo_mov, - EsCambioRegimen='N', - ValorMPTemp=valor_mp_temp, - ValorComercialMN=valor_comercial_mn, - TipoCambio=tipo_cambio, - ValorAgre=0.0, - TipoExpo='', - PedimentoR1=pedimento_r1, - EDocument=row[18], # C43 - NumOperacionVU=row[19], # C44 - BaseDeDatos=filters.database_name, - NumGafUni=num_gaf_uni, - UsuarioCap=row[22], # C53 - UsuarioAcr=row[23], # C54 - Fecha_Pago=row[12], # C13 - NumCaja=row[25], # C56 - Pedimento18=row[26], # C57 - AduanaCru=row[27], # C38 - Lote=None # Lote comes from EpiDef table, not available in main query - ) - - movements_dict[key] = movement - - movements = list(movements_dict.values()) - logger.info(f"Successfully retrieved {len(movements)} definitive import movements") - return movements - - except Exception as e: - logger.error(f"Error fetching definitive import movements: {e}", exc_info=True) - raise - - def _calculate_definitive_totals(self, db: Session, db_name: str, consecutivo: int) -> tuple: - """ - Calculate total values for a definitive import invoice. - Sums up all partidas (items) excluding sub-partidas. - - Returns: - tuple: (total_valor_me, total_valor_mn) - """ - try: - sql = text(f""" - SELECT SUM(EqiPdf.ValorME), SUM(EqiPdf.ValorMN) - FROM [{db_name}].dbo.QEqiDef EqiPdf - WHERE EqiPdf.Consecutivo = :consecutivo - AND EqiPdf.EsSubpartida = 'P' - """) - result = db.execute(sql, {"consecutivo": consecutivo}).fetchone() - - if result: - return (result[0] or 0, result[1] or 0) - return (0, 0) - except Exception as e: - logger.error(f"Error calculating totals for consecutivo {consecutivo}: {e}") - return (0, 0) - - def _get_driver_badge(self, db: Session, db_name: str, factura: str) -> Optional[str]: - """Get driver's unique badge number (NUMGAFETEUNICO) for a definitive import invoice""" - if not factura: - return None - - try: - sql = text(f""" - SELECT NUMGAFETEUNICO - FROM [{db_name}].dbo.GConductor - LEFT JOIN [{db_name}].dbo.QFacImpDef - ON QFacImpDef.CONDUCTOR = GConductor.CONDUCTOR - WHERE FacturaImpoDef = :factura - """) - result = db.execute(sql, {"factura": factura}).fetchone() - return result[0] if result else None - except Exception as e: - logger.debug(f"Error fetching driver badge for invoice {factura}: {e}") - return None - - def get_definitive_import_movements_detailed( - self, - db: Session, - filters: ImportDefinitiveFilter - ) -> List[MovementItemDetailed]: - """ - Retrieve detailed definitive import movements from legacy database (LLENADODEFINITIVO - DETALLADO). - Returns each line/partida as a separate record with full details. - - Args: - db: Database session - filters: Filter criteria for the query - - Returns: - List of detailed movement items (one per partida/line) - - Raises: - Exception: If database query fails - """ - logger.info(f"Fetching DETAILED definitive import movements with filters: {filters.model_dump()}") - - # Read INI configuration for exchange rate logic - met_trans = 0 - try: - config = configparser.ConfigParser() - config.read('Scaii.ini') - met_trans = config.getint('METTRANS', 'TipoCambio', fallback=0) - logger.debug(f"INI met_trans value: {met_trans}") - except Exception as e: - logger.warning(f"Could not read Scaii.ini, using default met_trans=0: {e}") - - # Build WHERE clause (same as normal mode) - where_conditions = [] - - if filters.range_type.value == "FF": - where_conditions.append(f"EqiFid.FechaFactura >= '{filters.start_date}' AND EqiFid.FechaFactura <= '{filters.end_date}'") - else: - where_conditions.append(f"EqiPed.Fecha_Pago >= '{filters.start_date}' AND EqiPed.Fecha_Pago <= '{filters.end_date}'") - - if not filters.include_cancelled: - where_conditions.append("EqiFid.Estatus = 'AC'") - - if filters.provider: - where_conditions.append(f"EqiFid.Proveedor = '{filters.provider}'") - - if filters.buyer: - where_conditions.append(f"EqiFid.VendidoA = '{filters.buyer}'") - - if filters.pedimento_code: - where_conditions.append(f"EqiPed.ClavePed = '{filters.pedimento_code}'") - - if filters.movement_type.value == "COMEX": - where_conditions.append("EqiFid.ProvImpoDefCR = 'P'") - elif filters.movement_type.value == "IMPDF": - where_conditions.append("EqiFid.ProvImpoDefCR != 'P'") - - where_clause = " AND ".join(where_conditions) - - # Build detailed SQL query (includes partida/line details) - sql_query = text(f""" - SELECT - EqiFid.FacturaImpoDef AS C1, - EqiFid.PedimentoImpoDef AS C2, - EqiFid.FechaFactura AS C3, - EqiFid.Estatus AS C4, - EqiPed.ClavePed AS C5, - EqiPed.Regimen AS C10, - EqiPed.Fecha_Inicio AS C11, - EqiPed.Fecha_Fin AS C12, - EqiPed.Fecha_Pago AS C13, - EqiFid.Remesa AS C14, - EqiFid.TipoCambio AS C15, - EqiFid.Proveedor AS C16, - EqiFid.VendidoA AS C17, - EqiFid.AAduanal AS C18, - EpiDef.Clase AS C20, - REPLACE(REPLACE(REPLACE(REPLACE(EpiDef.DescripcionE, CHAR(44), ' '), CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' ') AS C21, - REPLACE(REPLACE(REPLACE(REPLACE(EqiCla.DescripcionI, CHAR(44), ' '), CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' ') AS C22, - EpiDef.CantImpoDef AS C23, - EpiDef.UnidadMedida AS C24, - EpiDef.ValorMN AS C25, - EpiDef.ValorME AS C27, - EpiDef.PesoNeto AS C29, - EpiDef.PesoBruto AS C30, - EpiDef.OrdenCompra AS C31, - EpiDef.Fraccion AS C32, - EpiDef.TipoFraccion AS C33, - EpiDef.Sector AS C35, - EpiDef.PaisOrigen AS C37, - EqiPed.Aduana_Cruce AS C38, - EqiFid.ProvImpoDefCR AS C39, - EqiFid.Consecutivo AS C40, - EpiDef.EsSubPartida AS C41, - EqiPed.PedRectifica AS C42, - EqiFid.EDocument AS C43, - EqiFid.NumOperacionVU AS C44, - EpiDef.LineaImpoDef AS C45, - REPLACE(REPLACE(REPLACE(REPLACE(EpiDef.Marca, CHAR(44), ' '), CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' ') AS C46, - REPLACE(REPLACE(REPLACE(REPLACE(EpiDef.Modelo, CHAR(44), ' '), CHAR(9), ' '), CHAR(10), ' '), CHAR(13), ' ') AS C47, - EqiCla.FraccionAme AS C48, - EqiCla.ECCN AS C49, - EpiDef.NumParte AS C50, - EqiFid.TipoCambio AS C51, - EqiFid.FechaEmision AS C52, - EqiFid.UsuarioCap AS C53, - EqiFid.UsuarioAct AS C54, - EqiFid.Transportista AS C55, - EqiFid.Transporte + ' ' + EqiFid.NumTrasporte AS C56, - EqiPed.Pedimento18 AS C57, - EpiDef.Lote AS C58, - EqiPed.TIPOPEDIMENTOTRANSPORTEE AS C59 - FROM [{filters.database_name}].dbo.QFacImpDef EqiFid - LEFT JOIN [{filters.database_name}].dbo.QPedimentos EqiPed - ON EqiPed.Pedimento = EqiFid.PedimentoImpoDef - LEFT JOIN [{filters.database_name}].dbo.QEqiDef EpiDef - ON EpiDef.Consecutivo = EqiFid.Consecutivo - LEFT JOIN [{filters.database_name}].dbo.QClaAct EqiCla - ON EqiCla.Clase = EpiDef.Clase - WHERE {where_clause} - """) - - try: - result = db.execute(sql_query) - movements = [] - - for row in result: - linea = row[31] # C45 - factura = row[0] # C1 - pedimento = row[1] # C2 - prov_impo_def_cr = row[25] # C39 - es_subpartida = row[27] # C41 - fecha_pago = row[8] # C13 - tipo_pedimento = row[44] # C59 - fecha_inicio = row[6] # C11 - consecutivo = row[26] # C40 - - # Determine movement type - tipo_mov = "COMEX" if prov_impo_def_cr == 'P' else "IMPDF" - - # Get provider info - proveedor_info = self._get_client_provider_info( - db, filters.database_name, row[11], filters.is_shelter # C16 - ) - - # Get buyer info - buyer_info = self._get_client_buyer_info( - db, filters.database_name, row[12], filters.is_shelter # C17 - ) - - # Get customs broker info - broker_info = self._get_customs_broker_info( - db, filters.database_name, row[13] # C18 - ) - - # Calculate commercial value and exchange rate - tipo_cambio = float(row[10] or 1.0) # C15 - valor_comercial_mn = 0.0 - peso_neto = 0.0 - peso_bruto = 0.0 - - # Only process values if it's a Partida (not Subpartida) - if es_subpartida == 'P': - if filters.currency_type.value == "MN": - if filters.is_shelter: - if filters.exchange_rate_type.value == "FP" and fecha_pago: - fecha_tc = fecha_pago - if filters.use_transport_method and met_trans == 1: - if tipo_pedimento in ('1', '4', '98E'): - fecha_tc = fecha_inicio - - tc_value = self._obtener_tipo_cambio( - db, filters.database_name, fecha_tc, filters.is_shelter - ) - if tc_value: - valor_comercial_mn = float(row[20] or 0) * tc_value # C27 * TC - tipo_cambio = tc_value - else: - valor_comercial_mn = float(row[19] or 0) # C25 - tipo_cambio = float(row[37] or 1.0) # C51 - else: - valor_comercial_mn = float(row[19] or 0) # C25 - tipo_cambio = float(row[37] or 1.0) # C51 - else: - if filters.exchange_rate_type.value == "FP" and fecha_pago: - fecha_tc = fecha_pago - if filters.use_transport_method and met_trans == 1: - if tipo_pedimento in ('1', '4', '98E'): - fecha_tc = fecha_inicio - - tc_value = self._obtener_tipo_cambio( - db, filters.database_name, fecha_tc, filters.is_shelter - ) - if tc_value: - valor_comercial_mn = float(row[20] or 0) * tc_value # C27 * TC - tipo_cambio = tc_value - else: - valor_comercial_mn = float(row[19] or 0) # C25 - tipo_cambio = float(row[37] or 1.0) # C51 - else: - valor_comercial_mn = float(row[19] or 0) # C25 - tipo_cambio = float(row[37] or 1.0) # C51 - else: # ME - valor_comercial_mn = float(row[20] or 0) # C27 - if filters.exchange_rate_type.value == "FP" and fecha_pago: - fecha_tc = fecha_pago - if filters.use_transport_method and met_trans == 1: - if tipo_pedimento in ('1', '4', '98E'): - fecha_tc = fecha_inicio - - tc_value = self._obtener_tipo_cambio( - db, filters.database_name, fecha_tc, filters.is_shelter - ) - if tc_value: - tipo_cambio = tc_value - else: - tipo_cambio = float(row[37] or 1.0) # C51 - else: - tipo_cambio = float(row[37] or 1.0) # C51 - - peso_neto = float(row[21] or 0) # C29 - peso_bruto = float(row[22] or 0) # C30 - # If es_subpartida == 'S', values remain 0 - - # Get customs office name - aduana_nombre = self._get_customs_office_name( - db, filters.database_name, row[24] # C38 - ) - - # Get series information - series_info = self._get_definitive_series_info( - db, filters.database_name, consecutivo, linea, filters.is_shelter - ) - - # Get export symbol - symbolo_ex = self._get_part_export_symbol( - db, filters.database_name, row[36], filters.is_shelter # C50 - ) - - # Get rectification pedimento - pedimento_r1 = self._buscar_rectificacion(pedimento, row[28]) # C42 - - # Get driver badge - num_gaf_uni = self._get_driver_badge(db, filters.database_name, factura) - - # Create detailed movement item - movement = MovementItemDetailed( - Linea=linea, - Factura=factura, - Pedimento=pedimento, - FechaFactura=row[2], # C3 - Estatus=row[3], # C4 - ClavePed=row[4], # C5 - TipoMovTemDef=tipo_mov, - EsCambioRegimen='N', - Regimen=row[5], # C10 - Fecha_Inicio=row[6], # C11 - Fecha_Fin=row[7], # C12 - Fecha_Pago=row[8], # C13 - Remesa=row[9], # C14, - Proveedor=proveedor_info.get("nombre"), - RFCProveedor=proveedor_info.get("rfc"), - ProveedorTaxID=proveedor_info.get("tax_id"), - VendidoA=buyer_info.get("nombre"), - VendidoARFC=buyer_info.get("rfc"), - VendidoATaxID=buyer_info.get("tax_id"), - AgenteAduanal=broker_info.get("nombre"), - Patente=broker_info.get("patente"), - NumParte=row[14], # C20 - DescripcionE=row[15], # C21 - DescripcionI=row[16], # C22 - CantidadIE=float(row[17] or 0), # C23 - UniMed=row[18], # C24 - ValorComercialMN=valor_comercial_mn, - TipoCambio=tipo_cambio, - PesoNeto=peso_neto, - PesoBruto=peso_bruto, - OrdenCompraVenta=row[23], # C31 - FraccionArancelaria=row[29], # C32 - Preferencia=row[30], # C33 - Sector=row[32], # C35 - PaisOrigen=row[33], # C37 - Aduana=aduana_nombre, - Advalorem=row[27], # C41 - TipoExpo='', - PedimentoR1=pedimento_r1, - EDocument=row[34], # C43 - NumOperacionVU=row[35], # C44 - Series=series_info, - Marca=row[40], # C46 - Modelo=row[41], # C47 - FraccionAmericana=row[42], # C48 - ECCN=row[43], # C49 - SimboloEx=symbolo_ex, - FechaEmision=row[38], # C52 - BaseDeDatos=filters.database_name, - NumGafUni=num_gaf_uni, - UsuarioCap=row[39], # C53 - UsuarioAcr=row[40], # C54 - Transportista=row[41], # C55 - NumCaja=row[42], # C56 - Pedimento18=row[43], # C57 - AduanaCru=row[24], # C38 - Lote=row[44] # C58 - ) - - movements.append(movement) - - logger.info(f"Successfully retrieved {len(movements)} detailed definitive movements") - return movements - - except Exception as e: - logger.error(f"Error fetching detailed definitive movements: {e}", exc_info=True) - raise - - def _get_definitive_series_info( - self, - db: Session, - db_name: str, - consecutivo: int, - linea: int, - is_shelter: bool - ) -> Optional[str]: - """ - Get series information for a definitive import partida from QSeriesDef table. - Returns formatted string with series, model, and part info. - """ - if not consecutivo or not linea: - return None - - try: - sql = text(f""" - SELECT SerieImpo, ModeloImpo, ParteImpo - FROM [{db_name}].dbo.QSeriesDef - WHERE Consecutivo = :consecutivo - AND LineaImpoDef = :linea - """) - result = db.execute(sql, {"consecutivo": consecutivo, "linea": linea}).fetchall() - - if not result: - return None - - series_list = [] - for idx, row in enumerate(result, 1): - serie = row[0] - modelo = row[1] - parte = row[2] - - serie_str = f"{idx}) {serie}" - if modelo: - serie_str += f". Modelo: {modelo}" - if parte: - serie_str += f". Parte: {parte}" - - series_list.append(serie_str) - - return " | ".join(series_list) if series_list else None - - except Exception as e: - logger.debug(f"Error fetching definitive series info for consecutivo {consecutivo}, linea {linea}: {e}") - return None - - def get_repair_import_movements( - self, - db: Session, - filters: ImportRepairFilter - ) -> List[MovementItem]: - """ - Retrieve repair import movements from legacy database (LLENADOIMP_REPARACION - NORMAL). - Aggregates movements by invoice number. - - Args: - db: Database session - filters: Filter criteria for the query - - Returns: - List of movement items matching the criteria - - Raises: - Exception: If database query fails - """ - logger.info(f"Fetching repair import movements with filters: {filters.model_dump()}") - - # Get configuration - met_trans = self._get_met_trans_config() - - # Build WHERE clause - where_conditions = [] - - # Date range filter - if filters.range_type.value == "FF": - where_conditions.append(f"FimRep.FechaFactura >= '{filters.start_date}' AND FimRep.FechaFactura <= '{filters.end_date}'") - else: - where_conditions.append(f"EqiPed.Fecha_Pago >= '{filters.start_date}' AND EqiPed.Fecha_Pago <= '{filters.end_date}'") - - # Status filter - if not filters.include_cancelled: - where_conditions.append("FimRep.Estatus = 'AC'") - - # Provider filter - if filters.provider: - where_conditions.append(f"FimRep.Proveedor = '{filters.provider}'") - - # Buyer filter - if filters.buyer: - where_conditions.append(f"FimRep.VendidoA = '{filters.buyer}'") - - # Pedimento code filter - if filters.pedimento_code: - where_conditions.append(f"EqiPed.ClavePed = '{filters.pedimento_code}'") - - # Discharge filter for repair imports - if filters.discharge_filter.value == "SiDes": - where_conditions.append("RepPim.Descarga = 1") - elif filters.discharge_filter.value == "NoDes": - where_conditions.append("RepPim.Descarga = 0") - # If ALL, no filter added - - # Exclude regime changes - where_conditions.append("FimRep.EsCambioRegimen <> 'S'") - - where_clause = " AND ".join(where_conditions) - - # Build main SQL query for repair imports - sql_query = text(f""" - SELECT - FimRep.FacturaImpo AS C1, - FimRep.PedimentoImpo AS C2, - FimRep.FechaFactura AS C3, - FimRep.Estatus AS C4, - EqiPed.ClavePed AS C5, - FimRep.ValorImpoME AS C6, - FimRep.ValorImpoMN AS C7, - FimRep.Proveedor AS C8, - FimRep.VendidoA AS C9, - EqiPed.Regimen AS C10, - EqiPed.Fecha_Inicio AS C11, - EqiPed.Fecha_Fin AS C12, - EqiPed.Fecha_Pago AS C13, - FimRep.Remesa AS C14, - FimRep.TipoCambio AS C15, - FimRep.AAduanal AS C18, - FimRep.Consecutivo AS C39, - EqiPed.PedRectifica AS C41, - FimRep.EDocument AS C42, - FimRep.NumOperacionVU AS C43, - FimRep.TipoCambio AS C49, - FimRep.FechaEmision AS C50, - FimRep.UsuarioCap AS C51, - FimRep.UsuarioAct AS C52, - FimRep.Transportista AS C53, - FimRep.Transporte + ' ' + FimRep.NumTrasporte AS C54, - EqiPed.Pedimento18 AS C55, - EqiPed.Aduana_Cruce AS C38, - EqiPed.TIPOPEDIMENTOTRANSPORTEE AS C56 - FROM [{filters.database_name}].dbo.QFacImpRep FimRep - LEFT JOIN [{filters.database_name}].dbo.QPedimentos EqiPed - ON EqiPed.Pedimento = FimRep.PedimentoImpo - LEFT JOIN [{filters.database_name}].dbo.QEqiMaqRep RepPim - ON RepPim.Consecutivo = FimRep.Consecutivo - LEFT JOIN [{filters.database_name}].dbo.QClaAct ClaAct - ON ClaAct.Clase = RepPim.Clase - WHERE {where_clause} - """) - - try: - result = db.execute(sql_query) - movements_dict = {} - - for row in result: - factura = row[0] # C1 - - # Use factura + IMPRE as key - tipo_mov = "IMPRE" - key = (factura, tipo_mov) - - # If already exists, skip (we only want one entry per invoice in Normal mode) - if key not in movements_dict: - consecutivo = row[16] # C39 - fecha_pago = row[12] # C13 - tipo_pedimento = row[28] # C56 - fecha_inicio = row[10] # C11 - - # Calculate total values for this invoice (with discharge filter) - valor_me, valor_mn = self._calculate_repair_totals( - db, filters.database_name, consecutivo, filters.discharge_filter.value - ) - - # Calculate exchange rate and values using unified method - valor_comercial, tipo_cambio = self._calculate_exchange_rate_and_value( - db=db, - db_name=filters.database_name, - valor_me=valor_me, - valor_mn=valor_mn, - tipo_cambio_db=float(row[20] or 1.0), # C49 - fecha_pago=fecha_pago, - fecha_inicio=fecha_inicio, - tipo_pedimento=tipo_pedimento, - currency_type=filters.currency_type.value, - exchange_rate_type=filters.exchange_rate_type.value, - is_shelter=filters.is_shelter, - use_transport_method=filters.use_transport_method, - met_trans=met_trans - ) - - # Get driver badge number - num_gaf_uni = self._get_driver_badge_repair(db, filters.database_name, factura) - - # Get rectification pedimento - pedimento = row[1] # C2 - ped_rectifica = row[17] # C41 - pedimento_r1 = self._buscar_rectificacion(pedimento, ped_rectifica) - - # Create movement item - movement = MovementItem( - Factura=factura, - Pedimento=row[1], # C2 - FechaFactura=row[2], # C3 - Estatus=row[3], # C4 - ClavePed=row[4], # C5 - TipoMovTemDef=tipo_mov, - EsCambioRegimen='N', - ValorMPTemp=valor_comercial, - ValorComercialMN=valor_comercial, - TipoCambio=tipo_cambio, - ValorAgre=0.0, - TipoExpo='', - PedimentoR1=pedimento_r1, - EDocument=row[18], # C42 - NumOperacionVU=row[19], # C43 - BaseDeDatos=filters.database_name, - NumGafUni=num_gaf_uni, - UsuarioCap=row[22], # C51 - UsuarioAcr=row[23], # C52 - Fecha_Pago=row[12], # C13 - NumCaja=row[25], # C54 - Pedimento18=row[26], # C55 - AduanaCru=row[27], # C38 - Lote=None # Repair imports don't have Lote in main query - ) - - movements_dict[key] = movement - - movements = list(movements_dict.values()) - logger.info(f"Successfully retrieved {len(movements)} repair import movements") - return movements - - except Exception as e: - logger.error(f"Error fetching repair import movements: {e}", exc_info=True) - raise - - def _calculate_repair_totals( - self, - db: Session, - db_name: str, - consecutivo: int, - discharge_filter: str - ) -> tuple: - """ - Calculate total values for a repair import invoice. - Sums up all partidas (items) excluding sub-partidas, with optional discharge filter. - - Args: - db: Database session - db_name: Database name - consecutivo: Invoice consecutive number - discharge_filter: "SiDes", "NoDes", or "ALL" - - Returns: - tuple: (total_valor_me, total_valor_mn) - """ - try: - # Build discharge filter clause - discharge_clause = "" - if discharge_filter == "SiDes": - discharge_clause = " AND RepPim.Descarga = 1" - elif discharge_filter == "NoDes": - discharge_clause = " AND RepPim.Descarga = 0" - - sql = text(f""" - SELECT SUM(RepPim.ValorImpoME), SUM(RepPim.ValorImpoMN) - FROM [{db_name}].dbo.QEqiMaqRep RepPim - WHERE RepPim.Consecutivo = :consecutivo - AND RepPim.EsSubpartida = 'P' - {discharge_clause} - """) - result = db.execute(sql, {"consecutivo": consecutivo}).fetchone() - - if result: - return (result[0] or 0, result[1] or 0) - return (0, 0) - except Exception as e: - logger.error(f"Error calculating repair totals for consecutivo {consecutivo}: {e}") - return (0, 0) - - def _get_driver_badge_repair(self, db: Session, db_name: str, factura: str) -> Optional[str]: - """Get driver's unique badge number for a repair import invoice""" - if not factura: - return None - - try: - sql = text(f""" - SELECT NUMGAFETEUNICO - FROM [{db_name}].dbo.GConductor - LEFT JOIN [{db_name}].dbo.QFacImpRep - ON QFacImpRep.CONDUCTOR = GConductor.CONDUCTOR - WHERE FacturaImpo = :factura - """) - result = db.execute(sql, {"factura": factura}).fetchone() - return result[0] if result else None - except Exception as e: - logger.debug(f"Error fetching driver badge for repair invoice {factura}: {e}") - return None - - def get_repair_import_movements_detailed( - self, - db: Session, - filters: ImportRepairFilter - ) -> List[MovementItemDetailed]: - """ - Retrieve detailed repair import movements from legacy database (LLENADOIMP_REPARACION - DETALLADO). - Returns individual partida lines with full detail. - - Args: - db: Database session - filters: Filter parameters including date range, discharge filter, etc. - - Returns: - List of detailed movement items - """ - try: - logger.info(f"Fetching detailed repair import movements with filters: {filters}") - - # Get database name - db_name = self._get_database_name(db) - if not db_name: - logger.error("Could not determine database name") - return [] - - # Get MetTrans configuration - met_trans = self._get_met_trans_config() - - # Build WHERE clause - where_clause = self._build_where_clause_repair(filters) - - # Build discharge filter for main query - discharge_clause = "" - if filters.discharge_filter == "SiDes": - discharge_clause = " AND RepPim.Descarga = 1" - elif filters.discharge_filter == "NoDes": - discharge_clause = " AND RepPim.Descarga = 0" - - # Build main SQL query with all required fields for detailed mode - sql = text(f""" - SELECT - RepPim.LineaImpo, -- C44: Linea - Rep.FacturaImpo, -- C1: Factura - Ped.PedNumero, -- C2: Pedimento - Rep.FechaFacImpo, -- C3: FechaFactura - Rep.Estatus, -- C4: Estatus - Ped.ClavePedImpo, -- C5: ClavePed - Ped.Regimen, -- C10: Regimen - Ped.FechaEntrada, -- C11: Fecha_Inicio - Ped.FechaPago, -- C13: Fecha_Pago - Rep.Remesa, -- C14: Remesa - Rep.TipoCambio, -- C15: TipoCambio (from header) - Rep.Cliente, -- C16: Cliente/Proveedor - Rep.VendidoA, -- C17: VendidoA - Rep.AgenteAduanal, -- C18: AgenteAduanal clave - RepPim.NumParteImpo, -- C20: NumParte - RepPim.DescripcionE, -- C21: DescripcionE - RepPim.DescripcionI, -- C22: DescripcionI - RepPim.CantidadImpo, -- C23: CantidadIE - RepPim.UnidadMedImpo, -- C24: UniMed - RepPim.ValorImpoMN, -- C25: ValorComercialMN (direct) - RepPim.ValorImpoME, -- C27: ValorComercialME - RepPim.PesoNetoImpo, -- C29: PesoNeto - RepPim.PesoBrutoImpo, -- C30: PesoBruto - RepPim.OrdenCompraVta, -- C31: OrdenCompraVenta - RepPim.FraccionImpo, -- C32: FraccionArancelaria - RepPim.Preferencia, -- C33: Preferencia - RepPim.Sector, -- C35: Sector - RepPim.PaisOrigenImpo, -- C37: PaisOrigen - RepPim.Aduana, -- C38: Aduana seccion - Rep.Consecutivo, -- C39: Consecutivo - RepPim.EsSubpartida, -- C40: Advalorem/EsSubpartida - Ped.PedRectifica, -- C41: PedRectifica - Rep.eDocument, -- C42: EDocument - Rep.NumOperacionVU, -- C43: NumOperacionVU - RepPim.Marca, -- C45: Marca - RepPim.Modelo, -- C46: Modelo - RepPim.FraccionAmericana, -- C47: FraccionAmericana - RepPim.ECCN, -- C48: ECCN - RepPim.TipoCambio AS TipoCambioPartida, -- C49: TipoCambio (from partida) - Rep.FechaEmbarque, -- C50: FechaEmision - Rep.UsuarioCap, -- C51: UsuarioCap - Rep.UsuarioAct, -- C52: UsuarioAcr - Rep.Transportista, -- C53: Transportista - Rep.NumCaja, -- C54: NumCaja - Ped.Pedimento18, -- C55: Pedimento18 - Ped.ClavePedImpo -- C56: ClavePedImpo (for MetTrans check) - FROM [{db_name}].dbo.QFacImpRep Rep - LEFT JOIN [{db_name}].dbo.QPedimentos Ped ON Rep.Pedimento = Ped.PedNumero - LEFT JOIN [{db_name}].dbo.QEqiMaqRep RepPim ON Rep.Consecutivo = RepPim.Consecutivo - WHERE Rep.EsCambioRegimen <> 'S' - {where_clause} - {discharge_clause} - ORDER BY Rep.FacturaImpo, RepPim.LineaImpo - """) - - results = db.execute(sql).fetchall() - logger.info(f"Found {len(results)} detailed repair import partidas") - - movements = [] - for row in results: - # Extract all fields from query - linea = row[0] - factura = row[1] - pedimento = row[2] - fecha_factura = row[3] - estatus = row[4] - clave_ped = row[5] - regimen = row[6] - fecha_inicio = row[7] - fecha_pago = row[8] - remesa = row[9] - tipo_cambio_header = row[10] - cliente = row[11] - vendido_a = row[12] - agente_aduanal_clave = row[13] - num_parte = row[14] - descripcion_e = row[15] - descripcion_i = row[16] - cantidad = row[17] - uni_med = row[18] - valor_mn_direct = row[19] - valor_me = row[20] - peso_neto = row[21] - peso_bruto = row[22] - orden_compra = row[23] - fraccion = row[24] - preferencia = row[25] - sector = row[26] - pais_origen = row[27] - aduana_seccion = row[28] - consecutivo = row[29] - es_subpartida = row[30] - ped_rectifica = row[31] - e_document = row[32] - num_operacion_vu = row[33] - marca = row[34] - modelo = row[35] - fraccion_americana = row[36] - eccn = row[37] - tipo_cambio_partida = row[38] - fecha_emision = row[39] - usuario_cap = row[40] - usuario_acr = row[41] - transportista = row[42] - num_caja = row[43] - pedimento_18 = row[44] - clave_ped_mettrans = row[45] - - # Get client/supplier information (Proveedor) - proveedor_info = self._get_client_info(db, db_name, cliente, is_supplier=True) - - # Get sold-to client information (VendidoA) - vendido_info = self._get_client_info(db, db_name, vendido_a, is_supplier=False) - - # Get customs agent information - agente_info = self._get_customs_agent_info(db, db_name, agente_aduanal_clave) - - # Get customs section name - aduana_nombre = self._get_aduana_seccion_nombre(db, db_name, aduana_seccion) - - # Calculate exchange rate and commercial value - valor_mn, tipo_cambio_final = self._calculate_exchange_rate_and_value( - db=db, - db_name=db_name, - es_subpartida=es_subpartida, - valor_me=valor_me, - valor_mn_direct=valor_mn_direct, - fecha_pago=fecha_pago, - fecha_inicio=fecha_inicio, - clave_ped=clave_ped_mettrans, - tipo_cambio_partida=tipo_cambio_partida, - currency_type=filters.currency_type, - exchange_rate_type=filters.exchange_rate_type, - met_trans=met_trans - ) - - # Set peso values based on subpartida flag - peso_neto_final = peso_neto if es_subpartida == 'P' else 0 - peso_bruto_final = peso_bruto if es_subpartida == 'P' else 0 - - # Get series information for this partida - series = self._get_series_info_repair(db, db_name, consecutivo, linea) - - # Get rectification pedimento - pedimento_r1 = self._buscar_rectificacion(db, db_name, pedimento, ped_rectifica) - - # Get driver badge unique number - num_gaf_uni = self._get_driver_badge_repair(db, db_name, factura) - - # Build movement item - movement = MovementItemDetailed( - linea=linea, - factura=factura, - pedimento=pedimento, - fecha_factura=fecha_factura, - estatus=estatus, - clave_ped=clave_ped, - tipo_mov_tem_def="IMPRE", - es_cambio_regimen="N", - regimen=regimen, - fecha_inicio=fecha_inicio, - fecha_fin=None, # Not available for repair imports - fecha_pago=fecha_pago, - remesa=remesa, - tipo_cambio=tipo_cambio_final, - proveedor=proveedor_info.get("nombre"), - rfc_proveedor=proveedor_info.get("rfc"), - proveedor_tax_id=proveedor_info.get("tax_id"), - vendido_a=vendido_info.get("nombre"), - vendido_a_rfc=vendido_info.get("rfc"), - vendido_a_tax_id=vendido_info.get("tax_id"), - agente_aduanal=agente_info.get("nombre"), - patente=agente_info.get("patente"), - num_parte=num_parte, - descripcion_e=self._remove_commas(descripcion_e), - descripcion_i=self._remove_commas(descripcion_i), - cantidad_ie=cantidad, - uni_med=uni_med, - valor_comercial_mn=valor_mn, - peso_neto=peso_neto_final, - peso_bruto=peso_bruto_final, - orden_compra_venta=orden_compra, - fraccion_arancelaria=fraccion, - preferencia=preferencia, - sector=sector, - pais_origen=pais_origen, - aduana=aduana_nombre, - advalorem=es_subpartida, - tipo_expo=None, - pedimento_r1=pedimento_r1, - e_document=e_document, - num_operacion_vu=num_operacion_vu, - series=series, - marca=marca, - modelo=modelo, - fraccion_americana=fraccion_americana, - eccn=eccn, - fecha_emision=fecha_emision, - base_de_datos=db_name, - num_gaf_uni=num_gaf_uni, - usuario_cap=usuario_cap, - usuario_acr=usuario_acr, - transportista=transportista, - num_caja=num_caja, - pedimento_18=pedimento_18, - aduana_cru=aduana_seccion - ) - - movements.append(movement) - - logger.info(f"Successfully processed {len(movements)} detailed repair import movements") - return movements - - except Exception as e: - logger.error(f"Error fetching detailed repair import movements: {e}", exc_info=True) - raise - - def _get_series_info_repair(self, db: Session, db_name: str, consecutivo: int, linea: int) -> Optional[str]: - """Get series information for repair import partida""" - if not consecutivo or not linea: - return None - - try: - sql = text(f""" - SELECT SerieImpo, ModeloImpo, ParteImpo - FROM [{db_name}].dbo.QSeriesImpoRep - WHERE Consecutivo = :consecutivo - AND LineaImpo = :linea - ORDER BY Renglon - """) - results = db.execute(sql, {"consecutivo": consecutivo, "linea": linea}).fetchall() - - if not results: - return None - - series_list = [] - for idx, row in enumerate(results, 1): - serie = row[0] - modelo = row[1] - parte = row[2] - - serie_str = f"{idx}) {serie}" - if modelo: - serie_str += f". Modelo: {modelo}" - if parte: - serie_str += f". Parte: {parte}" - - series_list.append(serie_str) - - return " | ".join(series_list) if series_list else None - - except Exception as e: - logger.debug(f"Error fetching repair series info for consecutivo {consecutivo}, linea {linea}: {e}") - return None - - def _build_where_clause_repair(self, filters: ImportRepairFilter) -> str: - """Build WHERE clause for repair imports query""" - conditions = [] - - # Date range filter - if filters.range_type == RangeType.INVOICE_DATE: - conditions.append(f"Rep.FechaFacImpo BETWEEN '{filters.date_from}' AND '{filters.date_to}'") - elif filters.range_type == RangeType.PAYMENT_DATE: - conditions.append(f"Ped.FechaPago BETWEEN '{filters.date_from}' AND '{filters.date_to}'") - elif filters.range_type == RangeType.ENTRY_DATE: - conditions.append(f"Ped.FechaEntrada BETWEEN '{filters.date_from}' AND '{filters.date_to}'") - - return " AND " + " AND ".join(conditions) if conditions else "" - - -# Singleton instance -movement_service = MovementService() diff --git a/backend/api/v1/modules/a76/reports/movements/saldos/csv_utils.py b/backend/api/v1/modules/a76/reports/movements/saldos/csv_utils.py index 9ea3bac1..d82cb28b 100644 --- a/backend/api/v1/modules/a76/reports/movements/saldos/csv_utils.py +++ b/backend/api/v1/modules/a76/reports/movements/saldos/csv_utils.py @@ -202,13 +202,13 @@ BASE_SELECT = """ REPLACE(REPLACE(COALESCE(ild.description_spanish,''),CHR(10),''),CHR(13),' ') AS "C14", REPLACE(REPLACE(COALESCE(ild.description_english,''),CHR(10),''),CHR(13),' ') AS "C15", COALESCE(ilc.origin_country,'') AS "C16", - COALESCE(ilq.quantity, 0) AS "C17", - COALESCE(ilq.quantity_returned, 0) AS "C18", + COALESCE(ent.qty_impo, 0) AS "C17", + COALESCE(bal.qty_used, 0) AS "C18", COALESCE(uom.code,'') AS "C19", - COALESCE(ilf.value_mxn, 0) AS "C20", - COALESCE(ilf.value_returned_mxn, 0) AS "C21", - COALESCE(ilf.value_usd, 0) AS "C22", - COALESCE(ilf.value_returned_usd, 0) AS "C23", + COALESCE(ent.val_mn_impo, 0) AS "C20", + COALESCE(bal.val_mn_used, 0) AS "C21", + COALESCE(ent.val_me_impo, 0) AS "C22", + COALESCE(bal.val_me_used, 0) AS "C23", COALESCE(ilq.net_weight, 0) AS "C24", COALESCE(ilc.fraction,'') AS "C26", COALESCE(ilc.fraction_type,'') AS "C27", @@ -220,7 +220,7 @@ BASE_SELECT = """ il.id AS "C34", il.line_number AS "C35", COALESCE(p.part_number,'') AS "C36", - COALESCE(ilq.quantity_returned_temp, 0) AS "C37", + 0 AS "C37", COALESCE(il.location,'') AS "C38", '' AS "C39", COALESCE(icm.edocument,'') AS "C40", @@ -228,13 +228,15 @@ BASE_SELECT = """ COALESCE(cl.material_key,'') AS "C42", CONCAT(ped.year,'-',ped.customs_office,'-',ped.license,'-',ped.pedimento_number) AS "C43", COALESCE(ptc.payment_date_code, 'P') AS "C44", + COALESCE(lm.last_movement_type, '') AS "C_last_movement_type", COALESCE(ilc.octave_fraction,'') AS "C45", '' AS "C47", COALESCE(ped.pedimento_code,'') AS "C48", COALESCE(ilc.rate,'') AS "C49", COALESCE(il.iv32_type_key,'') AS "C50", COALESCE(il.guide_number,'') AS "C_embarque", - COALESCE(c_proj.name, '') AS "C_proyecto" + COALESCE(c_proj.name, '') AS "C_proyecto", + COALESCE(bal.qty_balance, 0) AS "C_balance" """ BASE_JOINS = """ @@ -252,6 +254,72 @@ BASE_JOINS = """ LEFT JOIN a76.item_line_descriptions ild ON ild.item_line_id = il.id LEFT JOIN a76.parts p ON p.id = il.part_number_id LEFT JOIN a76.units_of_measure uom ON uom.id = il.unit_of_measure + LEFT JOIN ( + SELECT DISTINCT ON (import_item_line_id) + import_item_line_id, + movement_type AS last_movement_type + FROM a24.balance_movement + WHERE tenant_id = :tenant_id + ORDER BY import_item_line_id, id DESC + ) lm ON lm.import_item_line_id = il.id + LEFT JOIN ( + SELECT + import_item_line_id, + SUM( + CASE + WHEN movement_type = 'entry' THEN quantity + WHEN movement_type = 'entry_void' THEN -1 * quantity + ELSE 0 + END + ) AS qty_impo, + SUM( + CASE + WHEN movement_type = 'entry' THEN COALESCE(value_me, 0) + WHEN movement_type = 'entry_void' THEN -1 * COALESCE(value_me, 0) + ELSE 0 + END + ) AS val_me_impo, + SUM( + CASE + WHEN movement_type = 'entry' THEN COALESCE(value_mn, 0) + WHEN movement_type = 'entry_void' THEN -1 * COALESCE(value_mn, 0) + ELSE 0 + END + ) AS val_mn_impo + FROM a24.balance_movement + WHERE tenant_id = :tenant_id + GROUP BY import_item_line_id + ) ent ON ent.import_item_line_id = il.id + LEFT JOIN ( + SELECT + import_item_line_id, + SUM( + CASE + WHEN movement_type IN ('consumption', 'waste', 'scrap', 'destruction') THEN quantity + WHEN movement_type = 'return' THEN -1 * quantity + ELSE 0 + END + ) as qty_used, + SUM( + CASE + WHEN movement_type IN ('consumption', 'waste', 'scrap', 'destruction') THEN COALESCE(value_me, 0) + WHEN movement_type = 'return' THEN -1 * COALESCE(value_me, 0) + ELSE 0 + END + ) as val_me_used, + SUM( + CASE + WHEN movement_type IN ('consumption', 'waste', 'scrap', 'destruction') THEN COALESCE(value_mn, 0) + WHEN movement_type = 'return' THEN -1 * COALESCE(value_mn, 0) + ELSE 0 + END + ) as val_mn_used, + SUM(CASE WHEN movement_type IN ('consumption', 'waste', 'scrap', 'destruction', 'neg_adj', 'transfer_out', 'expiration', 'regime_chg_out', 'entry_void') + THEN -1 * quantity ELSE quantity END) as qty_balance + FROM a24.balance_movement + WHERE tenant_id = :tenant_id + GROUP BY import_item_line_id + ) bal ON bal.import_item_line_id = il.id """ # --------------------------------------------------------------------------- @@ -287,6 +355,7 @@ def _query_ped(filters: SaldosFilter) -> tuple: FROM a76.item_lines il {BASE_JOINS} WHERE ih.tenant_id = :tenant_id + AND ih.operation_type = 'imp' {company_filter} {date_filter} {level_filter} @@ -310,6 +379,7 @@ def _query_fpp(filters: SaldosFilter) -> tuple: FROM a76.item_lines il {BASE_JOINS} WHERE ih.tenant_id = :tenant_id + AND ih.operation_type = 'imp' {company_filter} {date_filter} {level_filter} @@ -332,6 +402,7 @@ def _query_ffa(filters: SaldosFilter) -> tuple: FROM a76.item_lines il {BASE_JOINS} WHERE ih.tenant_id = :tenant_id + AND ih.operation_type = 'imp' {company_filter} {date_filter} {level_filter} @@ -362,6 +433,7 @@ def _query_par(filters: SaldosFilter) -> tuple: FROM a76.item_lines il {BASE_JOINS} WHERE ih.tenant_id = :tenant_id + AND ih.operation_type = 'imp' {company_filter} {id_filter} {date_filter} @@ -393,6 +465,7 @@ def _query_cla(filters: SaldosFilter) -> tuple: FROM a76.item_lines il {BASE_JOINS} WHERE ih.tenant_id = :tenant_id + AND ih.operation_type = 'imp' {company_filter} {id_filter} {date_filter} @@ -431,15 +504,19 @@ def _build_row( # CANTIDADES cant_orig = _d(row.get("C17")) - cant_ret = _d(row.get("C18")) + _d(row.get("C37")) - cant_saldo = cant_orig - cant_ret + cant_used = _d(row.get("C18")) + cant_saldo = _d(row.get("C_balance")) + + # Mostrar saldo 0, excepto lotes anulados (último movimiento ENTRY_VOID). + if str(row.get("C_last_movement_type") or "").lower() == "entry_void": + return None if filters.omit_low_balance and cant_saldo <= Decimal(0): return None # PESO peso_neto = _d(row.get("C30")) - peso_usado = (cant_ret * peso_neto / cant_orig) if cant_orig != 0 else Decimal(0) + peso_usado = (cant_used * peso_neto / cant_orig) if cant_orig != 0 else Decimal(0) peso_saldo = peso_neto - peso_usado # TIPO DE CAMBIO: @@ -479,11 +556,11 @@ def _build_row( if cant_orig != 0: if use_mn: if use_fp and fecha_pago: - valor_usado = cant_ret * _d(row.get("C22")) * tc / cant_orig + valor_usado = cant_used * _d(row.get("C22")) * tc / cant_orig else: - valor_usado = cant_ret * _d(row.get("C20")) / cant_orig + valor_usado = cant_used * _d(row.get("C20")) / cant_orig else: - valor_usado = cant_ret * _d(row.get("C22")) / cant_orig + valor_usado = cant_used * _d(row.get("C22")) / cant_orig else: valor_usado = Decimal(0) @@ -566,7 +643,7 @@ def _build_row( "UM": str(row.get("C19") or ""), "PesoNeto": _fmt_num(peso_neto), "ValorOriginal": _fmt_num(valor_orig), - "CantidadUsada": _fmt_num(cant_ret), + "CantidadUsada": _fmt_num(cant_used), "PesoUsado": _fmt_num(peso_usado), "ValorUsado": _fmt_num(valor_usado), "CantidadSaldo": _fmt_num(cant_saldo), diff --git a/backend/api/v1/modules/core/dashboard/service.py b/backend/api/v1/modules/core/dashboard/service.py index 64c00dba..f68015e1 100644 --- a/backend/api/v1/modules/core/dashboard/service.py +++ b/backend/api/v1/modules/core/dashboard/service.py @@ -68,13 +68,13 @@ class DashboardService: ) # Total del mes anterior para comparación - last_month = datetime.utcnow() - timedelta(days=30) + last_month = (datetime.utcnow() - timedelta(days=30)).date() previous_total = ( self.db.query(func.count(InvoiceHeader.id)) .filter( InvoiceHeader.tenant_id == self.tenant_id, InvoiceHeader.company_id == self.company_id, - InvoiceHeader.created_at < last_month, + InvoiceHeader.invoice_date < last_month, ) .scalar() or 0 @@ -91,17 +91,17 @@ class DashboardService: ) # Facturas por mes (últimos 6 meses) - six_months_ago = datetime.utcnow() - timedelta(days=180) + six_months_ago = (datetime.utcnow() - timedelta(days=180)).date() monthly_data = ( self.db.query( - func.date_trunc("month", InvoiceHeader.created_at).label("month"), + func.date_trunc("month", InvoiceHeader.invoice_date).label("month"), func.count(InvoiceHeader.id).label("count"), ) .filter( InvoiceHeader.tenant_id == self.tenant_id, InvoiceHeader.company_id == self.company_id, - InvoiceHeader.created_at >= six_months_ago, + InvoiceHeader.invoice_date >= six_months_ago, ) .group_by("month") .order_by("month") diff --git a/backend/api/v1/modules/public/reference_data/carta_porte/cartaPorte.csv b/backend/api/v1/modules/public/reference_data/carta_porte_codes/cartaPorte.csv similarity index 100% rename from backend/api/v1/modules/public/reference_data/carta_porte/cartaPorte.csv rename to backend/api/v1/modules/public/reference_data/carta_porte_codes/cartaPorte.csv diff --git a/backend/api/v1/modules/public/reference_data/carta_porte/dto.py b/backend/api/v1/modules/public/reference_data/carta_porte_codes/dto.py similarity index 100% rename from backend/api/v1/modules/public/reference_data/carta_porte/dto.py rename to backend/api/v1/modules/public/reference_data/carta_porte_codes/dto.py diff --git a/backend/api/v1/modules/public/reference_data/carta_porte/models.py b/backend/api/v1/modules/public/reference_data/carta_porte_codes/models.py similarity index 95% rename from backend/api/v1/modules/public/reference_data/carta_porte/models.py rename to backend/api/v1/modules/public/reference_data/carta_porte_codes/models.py index 1eb8e158..c23ade0b 100644 --- a/backend/api/v1/modules/public/reference_data/carta_porte/models.py +++ b/backend/api/v1/modules/public/reference_data/carta_porte_codes/models.py @@ -4,7 +4,7 @@ from sqlalchemy.orm import Mapped, mapped_column class CartaPorte(Base): - __tablename__ = "carta_porte" + __tablename__ = "carta_porte_codes" __table_args__ = ( {"schema": "public", "extend_existing": True}, ) diff --git a/backend/api/v1/modules/public/reference_data/carta_porte/routes.py b/backend/api/v1/modules/public/reference_data/carta_porte_codes/routes.py similarity index 100% rename from backend/api/v1/modules/public/reference_data/carta_porte/routes.py rename to backend/api/v1/modules/public/reference_data/carta_porte_codes/routes.py diff --git a/backend/api/v1/modules/public/reference_data/carta_porte/seed.py b/backend/api/v1/modules/public/reference_data/carta_porte_codes/seed.py similarity index 89% rename from backend/api/v1/modules/public/reference_data/carta_porte/seed.py rename to backend/api/v1/modules/public/reference_data/carta_porte_codes/seed.py index ad036fb6..7042748f 100644 --- a/backend/api/v1/modules/public/reference_data/carta_porte/seed.py +++ b/backend/api/v1/modules/public/reference_data/carta_porte_codes/seed.py @@ -39,10 +39,8 @@ def seed_carta_porte(db: Session): if len(batch) >= batch_size: db.bulk_save_objects(batch) db.commit() - batch = [] - print(f"Inserted {batch_size} records...") + batch = [] if batch: db.bulk_save_objects(batch) - db.commit() - print(f"Finished seeding with {len(batch)} remaining records.") + db.commit() diff --git a/backend/api/v1/modules/public/reference_data/router.py b/backend/api/v1/modules/public/reference_data/router.py index 62efddc6..a5d59a64 100644 --- a/backend/api/v1/modules/public/reference_data/router.py +++ b/backend/api/v1/modules/public/reference_data/router.py @@ -6,7 +6,7 @@ Agrega todos los módulos de la aplicación from fastapi import APIRouter from .agency_tariff_codes.routes import router as agency_tariff_codes_router -from .carta_porte.routes import router as carta_porte_router +from .carta_porte_codes.routes import router as carta_porte_router from .code_pedimento_regimens.routes import router as code_pedimento_regimens_router from .containers.routes import router as containers_router from .countries.routes import router as countries_router @@ -108,7 +108,7 @@ router.include_router( router.include_router( carta_porte_router, prefix="/reference_data", - tags=["public / reference_data / carta_porte"], + tags=["public / reference_data / carta_porte_codes"], ) router.include_router( customs_sections_router, diff --git a/backend/api/v1/modules/sitar/common/base_service.py b/backend/api/v1/modules/sitar/common/base_service.py index b4dbe284..e58d62b3 100644 --- a/backend/api/v1/modules/sitar/common/base_service.py +++ b/backend/api/v1/modules/sitar/common/base_service.py @@ -107,16 +107,12 @@ class SitarAPIBaseService: # DEBUG LOGGING for SITAR inspection if "fracciones" in url: import logging - logger = logging.getLogger(__name__) - logger.info(f"SITAR API Response Headers for {url}: {dict(response.headers)}") + logger = logging.getLogger(__name__) try: - data = response.json() - if isinstance(data, dict): - logger.info(f"SITAR API Response Body Keys: {list(data.keys())}") - elif isinstance(data, list) and len(data) > 0: - logger.info(f"SITAR API Response List Item Keys: {list(data[0].keys())}") + data = response.json() return data except Exception: + logger.error(f"Error parsing SITAR API Response Body: {response.text}") pass return response.json() diff --git a/backend/core/celery_app.py b/backend/core/celery_app.py index 1ab2efb7..7137ff77 100644 --- a/backend/core/celery_app.py +++ b/backend/core/celery_app.py @@ -65,6 +65,7 @@ celery_app.conf.update( "api.v1.modules.a76.invoices.imports.process.task", "api.v1.modules.a76.invoices.imports.revert.task", "api.v1.modules.a76.invoices.exports.process.task", + "api.v1.modules.a76.invoices.exports.revert.task", ] # Ruta al módulo donde están las tareas ) diff --git a/backend/main.py b/backend/main.py index 570f7230..c2d060ac 100644 --- a/backend/main.py +++ b/backend/main.py @@ -5,98 +5,18 @@ Backend API con FastAPI + Keycloak + SQLAlchemy import logging import subprocess - -# Importar modelos para registrar con SQLAlchemy - -# Reference Data (Dependencies) -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.public.reference_data.customs_sections.models import CustomsSection -from api.v1.modules.public.reference_data.customs_warehouses.models import CustomsWarehouse -from api.v1.modules.public.reference_data.incoterms.models import Incoterm -from api.v1.modules.public.reference_data.invoice_types.models import InvoiceType -from api.v1.modules.public.reference_data.material_types.models import MaterialType -from api.v1.modules.public.reference_data.payment_methods.models import PaymentMethod -from api.v1.modules.public.reference_data.pedimento_transport_catalog.models import ( - PedimentoTransportCatalog, -) -# Orden: PedimentoCode y RegimenPedimento antes de CodePedimentoRegimen para que -# SQLAlchemy resuelva los nombres en relationship() al configurar el mapper -from api.v1.modules.public.reference_data.pedimento_codes.models import PedimentoCode -from api.v1.modules.public.reference_data.pedimento_regimens.models import RegimenPedimento -from api.v1.modules.public.reference_data.code_pedimento_regimens.models import ( - CodePedimentoRegimen, -) -from api.v1.modules.a76.general_catalogs.sectors.models import Sector -from api.v1.modules.public.reference_data.states.models import State -from api.v1.modules.public.reference_data.transport_modes.models import TransportMode -from api.v1.modules.public.reference_data.transport_types.models import TransportType -from api.v1.modules.public.reference_data.valuation_methods.models import ValuationMethod -from api.v1.modules.public.reference_data.license_exceptions.models import LicenseException -from api.v1.modules.public.reference_data.agency_tariff_codes.models import AgencyTariffCode -from api.v1.modules.public.reference_data.identifiers.models import IdentifierCatalog -from api.v1.modules.public.reference_data.carta_porte.models import CartaPorte -from api.v1.modules.a76.general_catalogs.units_of_measure.models import UnitOfMeasure -from api.v1.modules.a76.general_catalogs.exchange_rate.models import ExchangeRate -from api.v1.modules.a76.general_catalogs.identifiers.models import Identifier -from api.v1.modules.a76.classes.models import Class -from api.v1.modules.a76.general_catalogs.classification_concepts.models import ClassificationConcept -from api.v1.modules.a76.general_catalogs.concepts.models import Concept -from api.v1.modules.a76.general_catalogs.customs_broker_concepts.models import CustomsBrokerConcept -from api.v1.modules.a76.general_catalogs.depreciation_catalog.models import DepreciationCatalog -from api.v1.modules.a76.general_catalogs.doda.models import Doda -from api.v1.modules.a76.general_catalogs.electronic_notices.models import ElectronicNotice -from api.v1.modules.a76.general_catalogs.equivalencies.models import Equivalency -from api.v1.modules.a76.general_catalogs.error_catalogs.models import ErrorCatalog -from api.v1.modules.a76.general_catalogs.fda_catalog.models import FDACatalog -from api.v1.modules.a76.general_catalogs.inpc.models import INPC -from api.v1.modules.a76.general_catalogs.legends.models import Legend -from api.v1.modules.a76.general_catalogs.multi_currency_types.models import MultiCurrencyType -from api.v1.modules.a76.general_catalogs.packages.models import Package -from api.v1.modules.a76.general_catalogs.ports.models import Port -from api.v1.modules.a76.general_catalogs.location.models import Location, FaLocationExt -from api.v1.modules.a76.general_catalogs.prevalidators.models import Prevalidator -from api.v1.modules.a76.general_catalogs.seal.models import Seal -from api.v1.modules.a76.general_catalogs.signatures.models import Signature -from api.v1.modules.a76.general_catalogs.fractions.tariff_fractions.models import ( - TariffFraction, -) -from api.v1.modules.a76.general_catalogs.unit_conversions.models import UnitConversion -from api.v1.modules.a76.general_catalogs.fractions.us_tariff_fractions.models import ( - USTariffFraction, -) - -# Core Modules & Reference Data (Dependencies) -from api.v1.modules.a76.clients_and_providers.models import ClientProvider -from api.v1.modules.a76.customs_brokers.models import CustomsBroker -from api.v1.modules.a76.general_catalogs.company.models import Company - -# Transportation Modules -from api.v1.modules.a76.transportation.trailers.models import Trailer -from api.v1.modules.a76.transportation.transporters.models import Transporter -from api.v1.modules.a76.transportation.vehicles.models import Vehicle - -# Core Modules & Transactional Models -from api.v1.modules.a76.items.models import LineItem -from api.v1.modules.a76.items.series.models import Serie -from api.v1.modules.a76.parts.models import Part -from api.v1.modules.a24.fa.fa_parts.models import FaPart -from api.v1.modules.a24.inv.inv_parts.models import InvPart -from api.v1.modules.a76.manifests.manifest.models import Manifest -from api.v1.modules.a76.manifests.concept_manifestation.models import ConceptManifestation -from api.v1.modules.a76.manifests.value_manifestation.models import ValueManifestation - -# Transactional Primary Models -from api.v1.modules.a76.pedmientos.models.pedimentos import Pedimentos -from api.v1.modules.a76.invoices.models import InvoiceHeader, InvoiceSalesDetails -from api.v1.modules.a76.audit_log.events import register_audit_listeners +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +from fastapi.staticfiles import StaticFiles +from pathlib import Path +from contextlib import asynccontextmanager # Core Modules (Secondary) - import core.celery_app # Initialize Celery App +from api.v1.modules.a76.audit_log.middleware import UserContextMiddleware # Middleware de Contexto de Usuario (Audit Log) +from api.v1.modules.a76.audit_log.register import register_audit from api.v1.router import router as api_v1_router from core.config import settings -from core.database import init_db from core.paths import layout_path from core.error_handlers import register_exception_handlers from core.middleware import ( @@ -104,26 +24,6 @@ from core.middleware import ( RequestLoggingMiddleware, TenantMiddleware, ) -from fastapi import FastAPI, Request, status, HTTPException -from fastapi.middleware.cors import CORSMiddleware -from fastapi.exceptions import RequestValidationError -from fastapi.responses import JSONResponse -from fastapi.staticfiles import StaticFiles -from pathlib import Path - -# Importar modelos para registrar con SQLAlchemy -# IMPORTANT: Import FaLineItem BEFORE LineItem for relationship resolution -from api.v1.modules.a24.fa.fa_item_lines.models import FaLineItem -from api.v1.modules.a76.items.models import LineItem -from api.v1.modules.a76.items.series.models import Serie -from api.v1.modules.a76.parts.models import Part -from api.v1.modules.a24.fa.fa_parts.models import FaPart -from api.v1.modules.a24.inv.inv_parts.models import InvPart -from api.v1.modules.a76.manifests.manifest.models import Manifest -from api.v1.modules.a76.manifests.concept_manifestation.models import ( - ConceptManifestation, -) -from api.v1.modules.a76.manifests.value_manifestation.models import ValueManifestation # Configurar logging logging.basicConfig( @@ -131,7 +31,6 @@ logging.basicConfig( format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", ) - # Crear aplicación FastAPI app = FastAPI( title="Anexo76 API", @@ -147,35 +46,14 @@ logger = logging.getLogger(__name__) # Registrar manejadores de excepciones register_exception_handlers(app) - -def _cors_headers_for_request(request: Request): - """Return CORS headers if request Origin is allowed (so error responses don't get blocked by browser).""" - origin = request.headers.get("origin") - if not origin: - return {} - allowed = settings.cors_origins_list - if origin in allowed: - return { - "Access-Control-Allow-Origin": origin, - "Access-Control-Allow-Credentials": "true", - } - return {} - - - for k, v in _cors_headers_for_request(request).items(): - response.headers[k] = v - return response - - def run_migrations(): subprocess.run(["alembic", "upgrade", "head"], check=True) # Inicializar la base de datos -@app.on_event("startup") async def on_startup(): """Evento de inicio de la aplicación""" logger.info("Iniciando la aplicación Anexo76...") - init_db() + #init_db() run_migrations() logger.info("Base de datos inicializada correctamente.") @@ -195,159 +73,16 @@ if settings.DEBUG: app.add_middleware(LicenseValidationMiddleware) app.add_middleware(TenantMiddleware) - -# Middleware de Contexto de Usuario (Audit Log) -from api.v1.modules.a76.audit_log.middleware import UserContextMiddleware - app.add_middleware(UserContextMiddleware) -# Importar modelos para Audit Log -from api.v1.modules.a76.pedmientos.models.pedimentos import Pedimentos -from api.v1.modules.a76.invoices.models import InvoiceHeader, InvoiceSalesDetails -from api.v1.modules.a76.audit_log.events import register_audit_listeners +@asynccontextmanager +async def lifespan(app: FastAPI): + # Centraliza startup para evitar on_event() (deprecated en FastAPI) + await on_startup() + register_audit() + yield -# Core Modules -from api.v1.modules.a76.clients_and_providers.models import ClientProvider -from api.v1.modules.a76.customs_brokers.models import CustomsBroker -from api.v1.modules.a76.parts.models import Part -from api.v1.modules.a76.items.models import LineItem -from api.v1.modules.a76.general_catalogs.company.models import Company - -# Reference Data -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.public.reference_data.customs_sections.models import CustomsSection -from api.v1.modules.public.reference_data.customs_warehouses.models import ( - CustomsWarehouse, -) -from api.v1.modules.public.reference_data.incoterms.models import Incoterm -from api.v1.modules.public.reference_data.invoice_types.models import InvoiceType -from api.v1.modules.public.reference_data.material_types.models import MaterialType -from api.v1.modules.public.reference_data.payment_methods.models import PaymentMethod -from api.v1.modules.public.reference_data.pedimento_transport_catalog.models import ( - PedimentoTransportCatalog, -) -from api.v1.modules.public.reference_data.pedimento_codes.models import PedimentoCode -from api.v1.modules.public.reference_data.pedimento_regimens.models import ( - RegimenPedimento, -) -from api.v1.modules.public.reference_data.states.models import State -from api.v1.modules.public.reference_data.transport_modes.models import TransportMode -from api.v1.modules.public.reference_data.transport_types.models import TransportType -from api.v1.modules.public.reference_data.valuation_methods.models import ( - ValuationMethod, -) -from api.v1.modules.public.reference_data.license_exceptions.models import LicenseException -from api.v1.modules.public.reference_data.agency_tariff_codes.models import AgencyTariffCode -from api.v1.modules.public.reference_data.identifiers.models import IdentifierCatalog -from api.v1.modules.public.reference_data.carta_porte.models import CartaPorte -from api.v1.modules.a76.general_catalogs.units_of_measure.models import UnitOfMeasure -from api.v1.modules.a76.general_catalogs.exchange_rate.models import ExchangeRate -from api.v1.modules.a76.general_catalogs.identifiers.models import Identifier -from api.v1.modules.a76.classes.models import Class -from api.v1.modules.a76.general_catalogs.classification_concepts.models import ( - ClassificationConcept, -) -from api.v1.modules.a76.general_catalogs.concepts.models import Concept -from api.v1.modules.a76.general_catalogs.customs_broker_concepts.models import ( - CustomsBrokerConcept, -) -from api.v1.modules.a76.general_catalogs.depreciation_catalog.models import ( - DepreciationCatalog, -) -from api.v1.modules.a76.general_catalogs.doda.models import Doda -from api.v1.modules.a76.general_catalogs.electronic_notices.models import ( - ElectronicNotice, -) -from api.v1.modules.a76.general_catalogs.equivalencies.models import Equivalency -from api.v1.modules.a76.general_catalogs.error_catalogs.models import ErrorCatalog -from api.v1.modules.a76.general_catalogs.fda_catalog.models import FDACatalog -from api.v1.modules.a76.general_catalogs.inpc.models import INPC -from api.v1.modules.a76.general_catalogs.legends.models import Legend -from api.v1.modules.a76.general_catalogs.multi_currency_types.models import ( - MultiCurrencyType, -) -from api.v1.modules.a76.general_catalogs.packages.models import Package -from api.v1.modules.a76.general_catalogs.ports.models import Port -from api.v1.modules.a76.general_catalogs.location.models import Location, FaLocationExt -from api.v1.modules.a76.general_catalogs.prevalidators.models import Prevalidator -from api.v1.modules.a76.general_catalogs.seal.models import Seal -from api.v1.modules.a76.general_catalogs.signatures.models import Signature -from api.v1.modules.a76.general_catalogs.fractions.tariff_fractions.models import ( - TariffFraction, -) -from api.v1.modules.a76.general_catalogs.unit_conversions.models import UnitConversion -from api.v1.modules.a76.general_catalogs.fractions.us_tariff_fractions.models import ( - USTariffFraction, -) - - -# Registrar Listeners de Auditoría -@app.on_event("startup") -def register_audit(): - register_audit_listeners( - [ - # Core Transactions - Pedimentos, - InvoiceHeader, - InvoiceSalesDetails, - LineItem, - # Sidebar Core Modules - ClientProvider, - CustomsBroker, - Part, - Company, - # Transportation Modules - Trailer, - Transporter, - Vehicle, - # Reference Data - Country, - CurrencyType, - CustomsSection, - CustomsWarehouse, - Incoterm, - InvoiceType, - MaterialType, - PaymentMethod, - PedimentoTransportCatalog, - PedimentoCode, - RegimenPedimento, - Sector, - State, - TransportMode, - TransportType, - ValuationMethod, - LicenseException, - AgencyTariffCode, - IdentifierCatalog, - CartaPorte, - UnitOfMeasure, - ExchangeRate, - Identifier, - Class, - ClassificationConcept, - Concept, - CustomsBrokerConcept, - DepreciationCatalog, - Doda, - ElectronicNotice, - Equivalency, - ErrorCatalog, - FDACatalog, - INPC, - Legend, - MultiCurrencyType, - Package, - Port, - Prevalidator, - Seal, - Signature, - TariffFraction, - UnitConversion, - USTariffFraction, - ] - ) +app.router.lifespan_context = lifespan # Crear directorio de uploads si no existe y montar archivos estáticos diff --git a/frontend/src/lib/api/dashboard/a76/aphis-catalog.ts b/frontend/src/lib/api/dashboard/a76/aphis-catalog.ts new file mode 100644 index 00000000..900d7614 --- /dev/null +++ b/frontend/src/lib/api/dashboard/a76/aphis-catalog.ts @@ -0,0 +1,59 @@ +import { api } from '$lib/api'; + +export interface AphisCatalogRecord { + id?: number; + // --- Pestaña 1: General --- + program_code?: string; + processing_code?: string; + aphis_type?: string; + disclaimer?: string; + electronic_image?: string; + confidential?: string; + global_product_id?: string; + intended_use_code?: string; + intended_use_description?: string; + item_type?: string; + product_code?: string; + product_code_2?: string; + product_code_3?: string; + scientific_genus_name?: string; + scientific_species_name?: string; + scientific_sub_species_name?: string; + common_name_specific?: string; + common_name_general?: string; + signed_doc?: string; + signed_doc_date?: string; + signed_doc_id?: string; + invoice_number?: string; + quantity_1?: string; + quantity_2?: string; + quantity_3?: string; + inspection?: string; + inspection_date?: string; + inspection_loc_date?: string; + inspection_location?: string; + country_production?: string; + country_source?: string; + + // --- Pestañas 2-7: Detalles --- + characteristics?: any[]; + pitems?: any[]; + lpcos?: any[]; + entities?: any[]; + containers?: any[]; + routing?: any[]; +} + +export const aphisCatalogApi = { + list: (company_id: number) => + api.get(`/v1/a24/aphis-catalog/?company_id=${company_id}`), + + create: (data: AphisCatalogRecord, company_id: number) => + api.post(`/v1/a24/aphis-catalog/?company_id=${company_id}`, data), + + update: (id: number, data: AphisCatalogRecord, company_id: number) => + api.put(`/v1/a24/aphis-catalog/${id}?company_id=${company_id}`, data), + + delete: (id: number, company_id: number) => + api.delete(`/v1/a24/aphis-catalog/${id}?company_id=${company_id}`) +}; diff --git a/frontend/src/lib/api/dashboard/a76/invoices.ts b/frontend/src/lib/api/dashboard/a76/invoices.ts index 371a8159..d0849433 100644 --- a/frontend/src/lib/api/dashboard/a76/invoices.ts +++ b/frontend/src/lib/api/dashboard/a76/invoices.ts @@ -110,7 +110,7 @@ export interface InvoiceFinancials { iva_mn?: number | null; iva_me?: number | null; iva_mc?: number | null; - iva_factor?: string | null; + iva_factor?: number | null; tax_value_me?: number | null; seal_value_2500?: boolean | null; total_quantity?: number | null; @@ -232,13 +232,14 @@ export interface Invoice { download_substance?: boolean | null; download_class?: boolean | null; download_def?: boolean | null; + total_items?: number | null; payment_terms?: string | null; handling_fees?: number | null; option_iv18?: string | null; enajenation_goods?: boolean | null; compliance_mx?: InvoiceComplianceMx | null; financials?: InvoiceFinancials | null; - logistics?: InvoiceLogistics[]; + logistics?: InvoiceLogistics | null; details?: InvoiceSalesDetails[]; collections?: InvoiceCollections[]; // Client-side only properties diff --git a/frontend/src/lib/api/dashboard/a76/items.ts b/frontend/src/lib/api/dashboard/a76/items.ts index 8c5b7326..9fabab3e 100644 --- a/frontend/src/lib/api/dashboard/a76/items.ts +++ b/frontend/src/lib/api/dashboard/a76/items.ts @@ -52,7 +52,6 @@ export interface LineQuantities { // Special quantities quantity_temp_export?: number; - quantity_returned?: number; // Weight net_weight?: number; @@ -338,8 +337,8 @@ export interface ImportLineWithBalance { unit_of_measure_code?: string; // Quantities quantity?: number; - quantity_returned_temp?: number; - quantity_returned?: number; + quantity_used_temp?: number; + quantity_used_def?: number; // Balance available_balance: number; has_balance: boolean; diff --git a/frontend/src/lib/components/dashboard/goods/parts/partForm.svelte b/frontend/src/lib/components/dashboard/goods/parts/partForm.svelte index 2ce54185..8bf4baff 100644 --- a/frontend/src/lib/components/dashboard/goods/parts/partForm.svelte +++ b/frontend/src/lib/components/dashboard/goods/parts/partForm.svelte @@ -1106,34 +1106,41 @@ {isEdit ? 'Editar' : 'Nueva'} + +
+ + + +
+ -

- {#if formType === 'both'} - - AMBOS - Gestión Inventario + Activo Fijo - - {:else if formType === 'fa'} - - SCAF - Gestión de Activo Fijo - - {:else} - - SCAI - Gestión de Inventario - - {/if} -

@@ -2690,6 +2697,27 @@ +
+
+
+ +
+
+

Datos de Extensión

+

Configura datos adicionales, APHIS y certificaciones.

+
+
+ +
+

@@ -2956,7 +2984,7 @@

-
+

Permitir o denegar la descarga

diff --git a/frontend/src/lib/components/dashboard/invoices/columns.ts b/frontend/src/lib/components/dashboard/invoices/columns.ts index 9c4ffd83..7821e55f 100644 --- a/frontend/src/lib/components/dashboard/invoices/columns.ts +++ b/frontend/src/lib/components/dashboard/invoices/columns.ts @@ -6,7 +6,19 @@ import { getInvoiceTypeColor } from "$lib/utils"; function formatDate(date?: string | null): string { if (!date) return '-'; - return new Date(date).toLocaleDateString('es-MX', { + // Avoid timezone shifts (e.g. showing one day earlier) by parsing as local date. + const raw = String(date); + const ymd = raw.includes('T') ? raw.split('T')[0] : raw; + const parts = ymd.split('-').map(Number); + if (parts.length === 3 && parts.every((n) => Number.isFinite(n))) { + const [year, month, day] = parts; + return new Date(year, month - 1, day).toLocaleDateString('es-MX', { + year: 'numeric', + month: '2-digit', + day: '2-digit' + }); + } + return new Date(raw).toLocaleDateString('es-MX', { year: 'numeric', month: '2-digit', day: '2-digit' @@ -243,7 +255,10 @@ export function createColumns( accessorKey: "total_items", header: "Total Partidas", cell: ({ row }) => { - const totalItems = row.original.details?.length || 0; + const totalItems = (row.original as Invoice & { total_items?: number | null }).total_items + ?? row.original.party_count + ?? row.original.details?.length + ?? 0; const itemsSnippet = createRawSnippet<[{ total: number }]>((getTotal) => { const { total } = getTotal(); @@ -291,7 +306,7 @@ export function createColumns( accessorKey: "logistics.weight_type", header: "Tipo Peso", cell: ({ row }) => { - const weightType = row.original.logistics?.[0]?.weight_type; + const weightType = row.original.logistics?.weight_type; const weightSnippet = createRawSnippet<[{ type?: string | null }]>((getType) => { const { type } = getType(); @@ -303,28 +318,6 @@ export function createColumns( return renderSnippet(weightSnippet, { type: weightType }); } }, - { - accessorKey: "status", - header: "Actualizado", - cell: ({ row }) => { - // status can be 'processed' | 'pending' | 'reversed' (string) or legacy boolean - const s = row.original.status; - const isprocessed = s === "processed" || s === true; - - const processedSnippet = createRawSnippet<[{ isprocessed?: boolean | null }]>((getprocessed) => { - const { isprocessed } = getprocessed(); - const colorClass = isprocessed ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'; - const label = isprocessed ? 'Sí' : 'No'; - return { - render: () => - ` - ${label} - ` - }; - }); - return renderSnippet(processedSnippet, { isprocessed }); - } - }, { accessorKey: "compliance_mx.is_mixed", header: "Mixto", diff --git a/frontend/src/lib/components/dashboard/invoices/create-edit-dialog.svelte b/frontend/src/lib/components/dashboard/invoices/create-edit-dialog.svelte index 4237c5e9..8652b8de 100644 --- a/frontend/src/lib/components/dashboard/invoices/create-edit-dialog.svelte +++ b/frontend/src/lib/components/dashboard/invoices/create-edit-dialog.svelte @@ -67,7 +67,7 @@ freight: null as number | null, insurance: null as number | null, iva_mn: null as number | null, - iva_factor: null as string | null, + iva_factor: null as number | null, total_quantity: null as number | null, gross_weight: null as number | null, net_weight: null as number | null, diff --git a/frontend/src/lib/components/dashboard/invoices/data-table.svelte b/frontend/src/lib/components/dashboard/invoices/data-table.svelte index cb2c4dbc..29b7fb10 100644 --- a/frontend/src/lib/components/dashboard/invoices/data-table.svelte +++ b/frontend/src/lib/components/dashboard/invoices/data-table.svelte @@ -83,9 +83,21 @@ {#each table.getHeaderGroups() as headerGroup (headerGroup.id)} + {@const headerList = headerGroup.headers} + {@const lastHeaderColId = headerList[headerList.length - 1]?.column.id} - {#each headerGroup.headers as header (header.id)} - + {#each headerList as header (header.id)} + {@const colId = header.column.id} + {#if !header.isPlaceholder} {#each table.getRowModel().rows as row (row.id)} + {@const visibleCells = row.getVisibleCells()} + {@const lastCellColId = visibleCells[visibleCells.length - 1]?.column.id} onRowClick && onRowClick(row.original)} > - {#each row.getVisibleCells() as cell (cell.id)} - + {#each visibleCells as cell (cell.id)} + {@const colId = cell.column.id} + {/each} diff --git a/frontend/src/lib/components/dashboard/invoices/details-dialog.svelte b/frontend/src/lib/components/dashboard/invoices/details-dialog.svelte index a78dfe99..722571ea 100644 --- a/frontend/src/lib/components/dashboard/invoices/details-dialog.svelte +++ b/frontend/src/lib/components/dashboard/invoices/details-dialog.svelte @@ -266,7 +266,7 @@

Factor IVA

-

{invoice.financials.iva_factor || '-'}

+

{formatCurrency(invoice.financials.iva_factor)}

@@ -296,59 +296,57 @@ - {#if invoice.logistics && invoice.logistics.length > 0} -
- {#each invoice.logistics as logistics, index} -
-

Logística #{index + 1}

-
-
-

Transportista

-

{logistics.carrier_id || '-'}

-
+ {#if invoice.logistics} +
+
+

Logística

+
+
+

Transportista

+

{invoice.logistics.carrier_id || '-'}

+
-
-

Tipo de Transporte

-

{logistics.transport_type || '-'}

-
+
+

Tipo de Transporte

+

{invoice.logistics.transport_type || '-'}

+
-
-

Modo de Transporte

-

{logistics.transport_mode || '-'}

-
+
+

Modo de Transporte

+

{invoice.logistics.transport_mode || '-'}

+
-
-

Conductor

-

{logistics.driver_name || '-'}

-
+
+

Conductor

+

{invoice.logistics.driver_name || '-'}

+
-
-

Número de Vehículo

-

{logistics.vehicle_num || '-'}

-
+
+

Número de Vehículo

+

{invoice.logistics.vehicle_num || '-'}

+
-
-

Placa

-

{logistics.license_plate || '-'}

-
+
+

Placa

+

{invoice.logistics.license_plate || '-'}

+
-
-

Número de Sello

-

{logistics.seal_number || '-'}

-
+
+

Número de Sello

+

{invoice.logistics.seal_number || '-'}

+
-
-

Guía

-

{logistics.guide_number || '-'}

-
+
+

Guía

+

{invoice.logistics.guide_number || '-'}

+
-
-

Fecha Entrada/Salida

-

{formatDate(logistics.entry_exit_date)}

-
+
+

Fecha Entrada/Salida

+

{formatDate(invoice.logistics.entry_exit_date)}

- {/each} +
{:else}

No hay información de logística disponible.

diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/item-sheet-fa.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/item-sheet-fa.svelte index 4dd63e0d..23968e7a 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/item-sheet-fa.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/item-sheet-fa.svelte @@ -69,7 +69,7 @@ editingItem.fa_data.omit_annex31 = false; } if (editingItem.fa_data.discharge === undefined) { - editingItem.fa_data.discharge = false; + editingItem.fa_data.discharge = true; } } }); @@ -271,7 +271,7 @@
{ editingItem.fa_data = editingItem.fa_data || {}; editingItem.fa_data.discharge = v === 'si'; @@ -364,7 +364,7 @@
{ editingItem.fa_data = editingItem.fa_data || {}; editingItem.fa_data.discharge = v === 'si'; @@ -700,10 +700,10 @@ {lineItem.unit_of_measure_code ?? ''} - {lineItem.quantity_returned_temp != null ? lineItem.quantity_returned_temp.toLocaleString('es-MX', { maximumFractionDigits: 4 }) : '-'} + {lineItem.quantity_used_temp != null ? lineItem.quantity_used_temp.toLocaleString('es-MX', { maximumFractionDigits: 4 }) : '-'} - {lineItem.quantity_returned != null ? lineItem.quantity_returned.toLocaleString('es-MX', { maximumFractionDigits: 4 }) : '-'} + {lineItem.quantity_used_def != null ? lineItem.quantity_used_def.toLocaleString('es-MX', { maximumFractionDigits: 4 }) : '-'} {lineItem.available_balance.toLocaleString('es-MX', { maximumFractionDigits: 4 })} diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/main-data.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/main-data.svelte index 8011fde5..f279e7cb 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/main-data.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/main-data.svelte @@ -50,6 +50,84 @@ // Track previous class_id to detect changes let previousClassId = $state(undefined); + function normalizeFractionForBackend(raw: unknown) { + if (raw === null || raw === undefined) return undefined; + return String(raw).replace(/\./g, '').trim(); + } + + async function getUnitByCode(unitCode: string) { + const activeCompanyId = companyStore?.activeCompany?.id; + if (!unitCode || !activeCompanyId) return undefined; + + try { + const res = await fetch( + `/api-sveltekit/units-of-measure?code=${encodeURIComponent(unitCode)}&page=1&page_size=20`, + { + method: 'GET', + credentials: 'include' + } + ); + if (!res.ok) return undefined; + const data = await res.json(); + const units = data?.items || data?.data || data; + if (Array.isArray(units) && units.length > 0) return units[0]; + } catch { + // Ignore unit lookup failures and let the user fix manually. + } + + return undefined; + } + + async function applyClassDefaults(classItem: any) { + if (!classItem) return; + + // Class display fields + (lineItem as any).class_code = classItem.class_code; + (lineItem as any).class_unit_of_measure = classItem.unit_of_measure; + (lineItem as any).class_description = classItem.description_es || classItem.description_en; + + // Descriptions + if ((lineItem as any).description) { + const desc = (lineItem as any).description; + // Use != null (instead of truthy) to support valid empty strings + if (classItem.description_es != null) desc.description_spanish = classItem.description_es; + if (classItem.description_en != null) desc.description_english = classItem.description_en; + } + + // U.M. (display + internal FK id) + const classUnitCode = classItem.unit_of_measure; + if (classUnitCode != null) { + // Always set display code if missing. + if (!(lineItem as any).unit_code) (lineItem as any).unit_code = classUnitCode; + + // Only resolve and override the FK if it isn't set yet. + if (!lineItem.unit_of_measure) { + const unit = await getUnitByCode(String(classUnitCode)); + if (unit) { + lineItem.unit_of_measure = unit.id; + (lineItem as any).unit_code = unit.code; + (lineItem as any).unit_description = unit.description || unit.description_en; + quantities.unit_of_measure = unit.code; + } + } + } + + // Fracción arancelaria (Mex / SCAII) + if (!customs.fraction && classItem.fraction != null) { + customs.fraction = normalizeFractionForBackend(classItem.fraction); + } + + // Tipo de fracción + if (!customs.fraction_type && classItem.import_tariff_type) { + customs.fraction_type = classItem.import_tariff_type; + } + + // Fracción americana (HTS / US) + if (!customs.american_fraction && classItem.us_fraction) { + customs.american_fraction = classItem.us_fraction; + } + } + // Watch for class_id changes and update descriptions automatically $effect(() => { const currentClassId = lineItem.class_id; @@ -72,20 +150,8 @@ const classItem = classes.find((c: any) => c.id === currentClassId); if (classItem) { - // Store the code and description in the lineItem for display - (lineItem as any).class_code = classItem.class_code; - (lineItem as any).class_unit_of_measure = classItem.unit_of_measure; - (lineItem as any).class_description = classItem.description_es || classItem.description_en; - - // Update description fields if description object exists - if ((lineItem as any).description) { - if (classItem.description_es) { - (lineItem as any).description.description_spanish = classItem.description_es; - } - if (classItem.description_en) { - (lineItem as any).description.description_english = classItem.description_en; - } - } + // Apply defaults derived from catalog class selection. + void applyClassDefaults(classItem); } }) .catch(error => { @@ -96,20 +162,7 @@ function handleClassSelect(classItem: any) { lineItem.class_id = classItem.id; - // Store the unit of measure and description for display - (lineItem as any).class_unit_of_measure = classItem.unit_of_measure; - (lineItem as any).class_code = classItem.class_code; - (lineItem as any).class_description = classItem.description_es || classItem.description_en; - - // Update description fields if description object exists - if ((lineItem as any).description) { - if (classItem.description_es) { - (lineItem as any).description.description_spanish = classItem.description_es; - } - if (classItem.description_en) { - (lineItem as any).description.description_english = classItem.description_en; - } - } + void applyClassDefaults(classItem); }; function handleUnitSelect(unit: any) { diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/packages-section.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/packages-section.svelte index d0ba2383..61c68f09 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/packages-section.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/packages-section.svelte @@ -37,10 +37,29 @@ }); // Calculate total package weight - const totalPackageWeight = $derived.by(() => { - const qty = quantities.package_quantity || 0; - const weightPerUnit = package_weight_unit || 0; - return (qty * weightPerUnit).toFixed(4); + const totalPackageWeightNum = $derived.by(() => { + const qty = Number(quantities.package_quantity ?? 0); + const weightPerUnit = Number(package_weight_unit ?? 0); + return qty * weightPerUnit; + }); + + const totalPackageWeight = $derived.by(() => totalPackageWeightNum.toFixed(4)); + const computedGrossWeight = $derived.by(() => { + const net = Number(quantities.net_weight ?? 0); + return net + totalPackageWeightNum; + }); + + // Auto-calculate gross weight = net weight + weight of packages (if gross wasn't provided) + $effect(() => { + const netWeight = quantities.net_weight; + if (netWeight === null || netWeight === undefined) return; + + const currentGross = quantities.gross_weight; + const shouldAutoFill = currentGross === null || currentGross === undefined || Number(currentGross) === 0; + if (!shouldAutoFill) return; + + // Keep precision stable enough for the UI inputs. + quantities.gross_weight = Number(computedGrossWeight.toFixed(8)); }); // Load or sync package data when package_id exists diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/summary-section.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/summary-section.svelte index 554f20d5..60317a0c 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/summary-section.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/summary-section.svelte @@ -87,7 +87,7 @@
Temporary: {formatNumber(quantities.quantity_temp_export)}
Replacement or Change: 0.00000000
-
Definitive: {formatNumber(quantities.quantity_returned)}
+
Definitive: 0.00000000
Returned Values: {formatNumber(financials.value_returned_usd)}
Returned Values: {formatNumber(financials.value_returned_mxn)}
diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/items-tab-form.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/items-tab-form.svelte index 5d76cde3..49b741da 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/items-tab-form.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/items-tab-form.svelte @@ -102,10 +102,12 @@ const invoiceSystem = $derived(invoice?.system || 'scaii'); const itemVisibility = $derived.by(() => getVisibility(invoiceType, operationType)); const showCrTrackingHeader = $derived(itemVisibility.showCrTrackingHeader); - const showTrackingHeaderColumns = $derived(operationType !== 1 && showCrTrackingHeader); + /** Debe coincidir con el orden de celdas en cada rama del tbody (exportación ≠ importación genérica ≠ REP). */ const emptyStateColspan = $derived.by(() => { if (operationType === 1) return 11; - return showTrackingHeaderColumns ? 12 : 10; + if (showCrTrackingHeader) return 12; + if (invoiceType === 'REP' || invoiceType === 'REPAR') return 13; + return 10; }); const invoiceLabel = $derived.by(() => { if (invoice?.invoice_number) return `Factura ${invoice.invoice_number}`; @@ -295,7 +297,6 @@ quantity: undefined, unit_of_measure: undefined, quantity_temp_export: undefined, - quantity_returned: undefined, net_weight: undefined, gross_weight: undefined, package_id: undefined, @@ -333,7 +334,7 @@ search_line: undefined, search_type: undefined, movement_type_import: undefined, - down_equipment: false, + own_equipment: false, omit_annex31: false }, series: [] @@ -1011,6 +1012,28 @@ // Cerrar el sheet showItemSheet = false; } + + /** Sticky sin bordes extra (evitan desalinear thead/tbody en tablas auto-layout). */ + const STICKY_LINE_HEAD = + 'sticky left-0 z-40 bg-background shadow-[3px_0_8px_-4px_rgba(0,0,0,0.12)] dark:shadow-[3px_0_8px_-4px_rgba(0,0,0,0.35)]'; + const STICKY_ACTIONS_HEAD = + 'sticky right-0 z-40 w-[104px] min-w-[104px] bg-background text-right shadow-[-3px_0_8px_-4px_rgba(0,0,0,0.12)] dark:shadow-[-3px_0_8px_-4px_rgba(0,0,0,0.35)]'; + + function stickyLineCellClass(itemId: string) { + const focused = focusedLine?.id === itemId; + return [ + 'sticky left-0 z-30 shadow-[3px_0_8px_-4px_rgba(0,0,0,0.12)] dark:shadow-[3px_0_8px_-4px_rgba(0,0,0,0.35)]', + focused ? 'bg-muted' : 'bg-background group-hover/item-row:bg-muted/50' + ].join(' '); + } + + function stickyActionsCellClass(itemId: string) { + const focused = focusedLine?.id === itemId; + return [ + 'sticky right-0 z-30 w-[104px] min-w-[104px] text-right shadow-[-3px_0_8px_-4px_rgba(0,0,0,0.12)] dark:shadow-[-3px_0_8px_-4px_rgba(0,0,0,0.35)]', + focused ? 'bg-muted' : 'bg-background group-hover/item-row:bg-muted/50' + ].join(' '); + }
@@ -1052,20 +1075,54 @@ - Línea - {#if showTrackingHeaderColumns} + Línea + {#if operationType === 1} Factura Impo Línea + P/S + Cant. Importada + Clase + Número Parte + Descripción + Contiene Subpartida + Partida Principal + Acciones + {:else if showCrTrackingHeader} + Factura Impo + Línea + P/S + Clase + Descripcion Clase + Cant. Importada + U.M. + Preferencia + Contiene Subpartida + Partida Principal + Acciones + {:else if invoiceType === 'REP' || invoiceType === 'REPAR'} + Factura Impo + Línea + P/S + Clase + Número Parte + Descripcion Clase + Cant. Importada + U.M. + Preferencia + Contiene Subpartida + Partida Principal + Acciones + {:else} + P/S + Clase + Descripcion Clase + Cant. Importada + U.M. + Preferencia + Contiene Subpartida + Partida Principal + Acciones {/if} - P/S - Clase - Descripcion Clase - Cant. Importada - U.M. - Preferencia - Contiene Subpartida - Partida Principal - Acciones @@ -1082,13 +1139,13 @@ {#each displayedItems as item (item.id)} handleRowClick(item)} - class="cursor-pointer transition-colors hover:bg-muted/50 {focusedLine?.id === + class="group/item-row cursor-pointer transition-colors hover:bg-muted/50 {focusedLine?.id === item.id ? 'bg-muted ring-1 ring-primary/20 ring-inset' : ''}" > {#if operationType === 1} - {item.line_number} + {item.line_number} {item.fa_data?.search_invoice || '-'} {item.fa_data?.search_line || '-'} {item.is_subitem ? 'S' : 'P'} @@ -1104,7 +1161,7 @@ {item.fa_data?.contains_subitems ? 'Sí' : 'No'} {item.warehouse || '-'} {:else if showCrTrackingHeader} - {item.line_number} + {item.line_number} {item.fa_data?.search_invoice || '-'} {item.fa_data?.search_line || '-'} {item.is_subitem ? 'S' : 'P'} @@ -1121,7 +1178,7 @@ {item.fa_data?.contains_subitems ? 'Sí' : 'No'} {item.warehouse || '-'} {:else if invoiceType === 'REP' || invoiceType === 'REPAR'} - {item.line_number} + {item.line_number} {item.fa_data?.search_invoice || '-'} {item.fa_data?.search_line || '-'} {item.is_subitem ? 'S' : 'P'} @@ -1139,7 +1196,7 @@ {item.fa_data?.contains_subitems ? 'Sí' : 'No'} {item.warehouse || '-'} {:else} - {item.line_number} + {item.line_number} {item.is_subitem ? 'S' : 'P'} {item.class_code || '-'} {item.class_description || '-'} @@ -1149,12 +1206,28 @@ {item.fa_data?.contains_subitems ? 'Sí' : 'No'} {item.warehouse || '-'} {/if} - +
- -
diff --git a/frontend/src/lib/components/dashboard/invoices/edit/save-invoice.ts b/frontend/src/lib/components/dashboard/invoices/edit/save-invoice.ts index 141a1fbd..8cd4db12 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/save-invoice.ts +++ b/frontend/src/lib/components/dashboard/invoices/edit/save-invoice.ts @@ -238,7 +238,7 @@ function buildFinancialsData(generalFormData: any, observationFormData: any, oth currency_type: generalFormData?.currency_type || (generalFormData?.currency === 'foreign' ? 'USD' : null), currency: generalFormData?.currency || null, exchange_rate: generalFormData?.exchange_rate ? Number(generalFormData.exchange_rate) : null, - iva_factor: (InvoiceTopFieldsFormData?.iva_factor || generalFormData?.iva_factor) ? String(InvoiceTopFieldsFormData?.iva_factor || generalFormData.iva_factor) : null, + iva_factor: (InvoiceTopFieldsFormData?.iva_factor || generalFormData?.iva_factor) ? Number(InvoiceTopFieldsFormData?.iva_factor || generalFormData.iva_factor) : null, // Costs & increments from observationFormData freight: observationFormData?.freight || null, insurance: observationFormData?.insurance || null, diff --git a/frontend/src/routes/dashboard/goods/parts/extension/[[id]]/+page.svelte b/frontend/src/routes/dashboard/goods/parts/extension/[[id]]/+page.svelte index 4e2fb994..83e0d96e 100644 --- a/frontend/src/routes/dashboard/goods/parts/extension/[[id]]/+page.svelte +++ b/frontend/src/routes/dashboard/goods/parts/extension/[[id]]/+page.svelte @@ -44,6 +44,8 @@ import AgencyTariffCodeSelector from '$lib/components/dashboard/goods/parts/AgencyTariffCodeSelector.svelte'; import IdentifierSelector from '$lib/components/dashboard/goods/parts/IdentifierSelector.svelte'; import CartaPorteSelector from '$lib/components/dashboard/goods/parts/CartaPorteSelector.svelte'; + import ClientSelectorDialog from '$lib/components/dashboard/goods/modales/client-selector-dialog.svelte'; + import { aphisCatalogApi, type AphisCatalogRecord } from '$lib/api/dashboard/a76/aphis-catalog'; const idParam = $page.params.id; const isNew = $derived(idParam === 'new'); @@ -54,6 +56,10 @@ // Catálogo de clientes let clients = $state([]); + let isClientSearchOpen = $state(false); + + // Catálogo global de APHIS (independiente de las partes) + let aphisCatalogRecords = $state([]); // Estado del formulario de extensión let formData = $state({ @@ -97,11 +103,10 @@ use_rule_8: false, // Sustitutos (Pestaña Continuación) substitute_parts: [] as { substitute_part_number: string }[], - // Aphis (Sección en Continuación) + // Aphis - solo los dos inputs de referencia aphis_data: { input1: '', - input2: '', - table: [] as any[] + input2: '' } }); @@ -111,6 +116,7 @@ let clientFormData = $state({ part_number: '', client_id: '', + client_name: '', part_name: '' }); @@ -273,9 +279,13 @@ loading = true; try { - // Cargar catálogo de clientes primero - const clientsRes = await clientsProvidersApi.list(companyId, 1, 1000); + // Cargar catálogo de clientes y catálogo global APHIS en paralelo + const [clientsRes, aphisRes] = await Promise.all([ + clientsProvidersApi.list(companyId, 1, 1000), + aphisCatalogApi.list(companyId) + ]); clients = clientsRes.data?.items || []; + aphisCatalogRecords = (aphisRes as any).data || []; if (isNew) { loading = false; @@ -322,13 +332,8 @@ formData.part_identifiers = (part.inv_data as any).part_identifiers || []; formData.substitute_parts = (part.inv_data as any).substitute_parts || []; - formData.aphis_data = (part.inv_data as any).aphis_data || { input1: '', input2: '', table: [] }; - - // Si existen registros relacionales, usarlos para la tabla - const aphis_recs = (part.inv_data as any).aphis_records; - if (aphis_recs && aphis_recs.length > 0) { - formData.aphis_data.table = aphis_recs; - } + const aphisStored = (part.inv_data as any).aphis_data || {}; + formData.aphis_data = { input1: aphisStored.input1 || '', input2: aphisStored.input2 || '' }; } } } catch (e) { @@ -354,7 +359,6 @@ try { loading = true; - // Preparar update const updateData = { part_number: formData.part_number, description_spanish: formData.description_spanish, @@ -379,8 +383,8 @@ client_part_names: formData.client_part_names, part_identifiers: formData.part_identifiers, substitute_parts: formData.substitute_parts, - aphis_data: formData.aphis_data, - aphis_records: formData.aphis_data.table + // Guardamos solo los dos inputs de referencia APHIS (el catálogo es independiente) + aphis_data: formData.aphis_data } }; @@ -400,6 +404,7 @@ clientFormData = { part_number: formData.part_number || '', client_id: '', + client_name: '', part_name: '' }; isClientDialogOpen = true; @@ -411,6 +416,7 @@ clientFormData = { part_number: row.part_number || '', client_id: row.client_id.toString(), + client_name: row.client_name || '', part_name: row.part_name || '' }; isClientDialogOpen = true; @@ -423,10 +429,10 @@ } const selectedClient = clients.find(c => c.id.toString() === clientFormData.client_id); - const newRow = { + const newRow: { part_number: string, client_id: string, client_name: string, part_name: string } = { part_number: clientFormData.part_number, client_id: clientFormData.client_id, - client_name: selectedClient ? selectedClient.name : 'Desconocido', + client_name: clientFormData.client_name, part_name: clientFormData.part_name }; @@ -440,6 +446,12 @@ toast.success(editingClientIndex !== null ? 'Registro actualizado' : 'Registro agregado'); } + function handleClientSelect(client: ClientProvider) { + clientFormData.client_id = client.id.toString(); + clientFormData.client_name = client.name; + isClientSearchOpen = false; + } + function removeClientRow(index: number) { formData.client_part_names = formData.client_part_names.filter((_, i) => i !== index); toast.info('Registro eliminado'); @@ -513,7 +525,7 @@ function openAphisEdit(index: number) { editingAphisIndex = index; - const row = formData.aphis_data.table[index]; + const row = aphisCatalogRecords[index]; aphisFormData = { general: { program_code: row.program_code || '', @@ -549,7 +561,7 @@ country_source: row.country_source || '' }, characteristics: row.characteristics || [], - stype_pitems: row.stype_pitems || [], + stype_pitems: row.pitems || [], lpcos: row.lpcos || [], entities: row.entities || [], containers: row.containers || [], @@ -559,30 +571,57 @@ isAphisDetailDialogOpen = true; } - function saveAphisDetail() { - const consolidatedRow = { + async function saveAphisDetail() { + const companyId = companyStore.activeCompany?.id; + if (!companyId) return; + + // Consolidar todos los datos (General + 6 pestañas de detalle) + const payload: any = { ...aphisFormData.general, characteristics: aphisFormData.characteristics, - stype_pitems: aphisFormData.stype_pitems, + pitems: aphisFormData.stype_pitems, lpcos: aphisFormData.lpcos, entities: aphisFormData.entities, containers: aphisFormData.containers, routing: aphisFormData.routing }; - if (editingAphisIndex !== null) { - formData.aphis_data.table[editingAphisIndex] = consolidatedRow; - } else { - formData.aphis_data.table = [...formData.aphis_data.table, consolidatedRow]; - } + // Limpiar campos vacíos (convertir "" a null para evitar errores de validación en el backend) + Object.keys(payload).forEach(key => { + if (payload[key] === "") payload[key] = null; + }); - isAphisDetailDialogOpen = false; - toast.success(editingAphisIndex !== null ? 'Registro Aphis actualizado' : 'Registro Aphis agregado'); + try { + if (editingAphisIndex !== null) { + const existingId = aphisCatalogRecords[editingAphisIndex].id; + await aphisCatalogApi.update(existingId!, payload, companyId); + } else { + await aphisCatalogApi.create(payload, companyId); + } + // Recargar el catálogo completo para reflejar los datos reales del backend + const reloaded = await aphisCatalogApi.list(companyId); + aphisCatalogRecords = (reloaded as any).data || reloaded || []; + isAphisDetailDialogOpen = false; + toast.success(editingAphisIndex !== null ? 'Registro Aphis actualizado' : 'Registro Aphis agregado'); + } catch (e) { + console.error('Error guardando APHIS:', e); + toast.error('Error al guardar el registro Aphis'); + } } - function removeAphisRow(index: number) { - formData.aphis_data.table = formData.aphis_data.table.filter((_, i) => i !== index); - toast.info('Registro Aphis eliminado'); + + async function removeAphisRow(index: number) { + const companyId = companyStore.activeCompany?.id; + if (!companyId) return; + const record = aphisCatalogRecords[index]; + try { + if (record.id) await aphisCatalogApi.delete(record.id, companyId); + aphisCatalogRecords = aphisCatalogRecords.filter((_: AphisCatalogRecord, i: number) => i !== index); + toast.info('Registro Aphis eliminado'); + } catch (e) { + console.error('Error eliminando APHIS:', e); + toast.error('Error al eliminar el registro'); + } } // --- Funciones para Aphis Characteristic --- @@ -1168,15 +1207,20 @@
-
+
+
-
- +
+ + +
+
+ +
-
@@ -1203,17 +1247,20 @@
- - + +
+ isClientSearchOpen = true} + class="cursor-pointer bg-muted/30" + /> + +
@@ -1269,9 +1316,9 @@
- Gestión de Registros Aphis + Catálogo Aphis - Lista maestra de especificaciones Aphis para esta parte. + Gestiona tus registros Aphis. Al seleccionar uno, se usará su Program Code en el formulario.
@@ -1601,7 +1660,7 @@ {:else} - + No hay características asignadas a este registro. @@ -1610,7 +1669,7 @@
- +
Stype_Pitems @@ -1661,7 +1720,7 @@ {:else} - + No hay registros de stype asignados. @@ -1729,7 +1788,7 @@ {:else} - + No hay registros LPCO asignados. @@ -2184,4 +2243,5 @@ + diff --git a/frontend/src/routes/dashboard/invoices/+page.svelte b/frontend/src/routes/dashboard/invoices/+page.svelte index f099060b..3574dbf7 100644 --- a/frontend/src/routes/dashboard/invoices/+page.svelte +++ b/frontend/src/routes/dashboard/invoices/+page.svelte @@ -983,11 +983,13 @@ - Des-actualizar Factura de Importación Temporal + + Des-actualizar Factura de {selectedInvoice?.operation_type === 'exp' ? 'Exportación' : 'Importación'} + Se va a des-actualizar la factura {selectedInvoice?.invoice_number}. - Esta operación revertirá los saldos de inventario y los cupos de Regla Octava - registrados al procesar la factura. ¿Desea continuar? + Esta operación revertirá los registros de saldos/descargos generados al procesar la factura. + ¿Desea continuar? diff --git a/frontend/src/routes/dashboard/invoices/edit/[id]/+page.svelte b/frontend/src/routes/dashboard/invoices/edit/[id]/+page.svelte index 0368c8b6..af17d17f 100644 --- a/frontend/src/routes/dashboard/invoices/edit/[id]/+page.svelte +++ b/frontend/src/routes/dashboard/invoices/edit/[id]/+page.svelte @@ -1,5 +1,6 @@