diff --git a/backend/alembic/versions/531bf8cdae06_create_material_types_table.py b/backend/alembic/versions/531bf8cdae06_create_material_types_table.py index bfdc01b1..2629548a 100644 --- a/backend/alembic/versions/531bf8cdae06_create_material_types_table.py +++ b/backend/alembic/versions/531bf8cdae06_create_material_types_table.py @@ -167,12 +167,154 @@ def upgrade() -> None: sa.PrimaryKeyConstraint("id", name="clave_pedimento_regimens_pkey"), schema="public", ) + + # Tablas de Unidades de Medida (A76) + + # 1. Tabla: unit_of_measure_ace (ACE Units) + 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('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.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='uq_uom_ace_code'), + schema='a76' + ) + + # 2. Tabla: unit_of_measure_oma (OMA Units) + 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('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.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='uq_uom_oma_code'), + schema='a76' + ) + + # 3. Tabla: unit_of_measure_american (American Units) + 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('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.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='uq_uom_american_code'), + schema='a76' + ) + + # 4. Tabla: unit_of_measure_customs (Customs Units) + op.create_table( + 'unit_of_measure_customs', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('code', sa.String(length=2), nullable=False), + sa.Column('description', sa.String(length=20), nullable=True), + sa.Column('scaii_unit_code', 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.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='uq_uom_customs_code'), + schema='a76' + ) + + # 5. Tabla: units_of_measure (Main Unit of Measure) + 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=2), 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.ForeignKeyConstraint( + ['customs_code', 'tenant_id', 'company_id'], + ['a76.unit_of_measure_customs.code', 'a76.unit_of_measure_customs.tenant_id', 'a76.unit_of_measure_customs.company_id'], + name='fk_uom_customs' + ), + sa.ForeignKeyConstraint( + ['american_code', 'tenant_id', 'company_id'], + ['a76.unit_of_measure_american.code', 'a76.unit_of_measure_american.tenant_id', 'a76.unit_of_measure_american.company_id'], + name='fk_uom_american' + ), + sa.ForeignKeyConstraint( + ['ace_code', 'tenant_id', 'company_id'], + ['a76.unit_of_measure_ace.code', 'a76.unit_of_measure_ace.tenant_id', 'a76.unit_of_measure_ace.company_id'], + name='fk_uom_ace' + ), + sa.ForeignKeyConstraint( + ['oma_code', 'tenant_id', 'company_id'], + ['a76.unit_of_measure_oma.code', 'a76.unit_of_measure_oma.tenant_id', 'a76.unit_of_measure_oma.company_id'], + name='fk_uom_oma' + ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='uq_uom_code'), + schema='a76' + ) + + # 6. Tabla: units_of_measure_general (General/Conversion Units) + 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=5), nullable=True), + sa.Column('customs_code', sa.String(length=2), 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.ForeignKeyConstraint( + ['customs_code', 'tenant_id', 'company_id'], + ['a76.unit_of_measure_customs.code', 'a76.unit_of_measure_customs.tenant_id', 'a76.unit_of_measure_customs.company_id'], + name='fk_uom_general_customs' + ), + sa.ForeignKeyConstraint( + ['ace_code', 'tenant_id', 'company_id'], + ['a76.unit_of_measure_ace.code', 'a76.unit_of_measure_ace.tenant_id', 'a76.unit_of_measure_ace.company_id'], + name='fk_uom_general_ace' + ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='uq_uom_general_code'), + schema='a76' + ) # ### end Alembic commands ### def downgrade() -> None: """Downgrade schema.""" # ### commands auto generated by Alembic - please adjust! ### + # Eliminar tablas de unidades de medida + op.drop_table('units_of_measure_general', schema='a76') + op.drop_table('units_of_measure', 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_oma', schema='a76') + op.drop_table('unit_of_measure_ace', schema='a76') + + # Eliminar tablas públicas op.drop_table("code_pedimento_regimens", schema="public") op.drop_table("valuation_methods", schema="public") op.drop_table("transport_types", schema="public") diff --git a/backend/alembic/versions/7937209f9718_seed_initial_data.py b/backend/alembic/versions/7937209f9718_seed_initial_data.py index 864f6c72..75a6ce9b 100644 --- a/backend/alembic/versions/7937209f9718_seed_initial_data.py +++ b/backend/alembic/versions/7937209f9718_seed_initial_data.py @@ -53,12 +53,13 @@ from api.v1.modules.public.reference_data.transport_types.seed import ( from api.v1.modules.public.reference_data.valuation_methods.seed import ( seed as valuation_methods_seed, ) +from api.v1.modules.a76.general_catalogs.units_of_measure.seed import seed as units_of_measure_seed # revision identifiers, used by Alembic. revision: str = "7937209f9718" down_revision: Union[str, Sequence[str], None] = "531bf8cdae06" branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = "531bf8cdae06" +depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: @@ -288,6 +289,46 @@ def upgrade() -> None: """ ) + # Insertar seeds de Unidades de Medida + # Preparar valores únicos para cada tabla auxiliar + ace_codes = set() + oma_codes = set() + american_codes = set() + customs_codes = set() + + for code, desc, desc_en, customs, american, ace, oma in units_of_measure_seed: + if ace and ace.strip(): + ace_codes.add(ace.strip()) + if oma and oma.strip(): + oma_codes.add(oma.strip()) + if american and american.strip(): + american_codes.add(american.strip()) + if customs and customs.strip(): + customs_codes.add(customs.strip()) + + # Insertar seeds (con tenant_id=1, company_id=1 por defecto) + # IMPORTANTE: En producción necesitarás ajustar estos valores + + def format_value(val): + """Formatea valores para SQL, manejando None y strings vacíos""" + if not val or val.strip() == '': + return 'NULL' + return f"'{val.replace(chr(39), chr(39)*2)}'" + + values_uom = ", ".join([ + f"('{code}', {format_value(desc)}, {format_value(desc_en)}, " + f"{format_value(customs)}, {format_value(american)}, " + f"{format_value(ace)}, {format_value(oma)}, 1, 1)" + for code, desc, desc_en, customs, american, ace, oma in units_of_measure_seed + ]) + + op.execute(f""" + INSERT INTO a76.units_of_measure + (code, description, description_en, customs_code, american_code, ace_code, oma_code, tenant_id, company_id) + VALUES {values_uom} + ON CONFLICT (code, tenant_id, company_id) DO NOTHING; + """) + def downgrade() -> None: """Downgrade schema.""" @@ -308,3 +349,4 @@ def downgrade() -> None: op.execute("DELETE FROM public.code_pedimento_regimens;") op.execute("DELETE FROM public.pedimento_regimens;") op.execute("DELETE FROM public.pedimento_codes;") + op.execute("DELETE FROM a76.units_of_measure;") diff --git a/backend/alembic/versions/d9b22e8e8cd3_create_units_of_measure_tables.py b/backend/alembic/versions/d9b22e8e8cd3_create_units_of_measure_tables.py new file mode 100644 index 00000000..89113e85 --- /dev/null +++ b/backend/alembic/versions/d9b22e8e8cd3_create_units_of_measure_tables.py @@ -0,0 +1,163 @@ +"""create_units_of_measure_tables + +Revision ID: d9b22e8e8cd3 +Revises: 7937209f9718 +Create Date: 2026-01-09 14:51:56.669491 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'd9b22e8e8cd3' +down_revision: Union[str, Sequence[str], None] = '7937209f9718' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + + # 1. Tabla: unit_of_measure_ace (ACE Units) + 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('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.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='uq_uom_ace_code'), + schema='a76' + ) + + # 2. Tabla: unit_of_measure_oma (OMA Units) + 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('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.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='uq_uom_oma_code'), + schema='a76' + ) + + # 3. Tabla: unit_of_measure_american (American Units) + 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('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.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='uq_uom_american_code'), + schema='a76' + ) + + # 4. Tabla: unit_of_measure_customs (Customs Units) + op.create_table( + 'unit_of_measure_customs', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('code', sa.String(length=2), nullable=False), + sa.Column('description', sa.String(length=20), nullable=True), + sa.Column('scaii_unit_code', 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.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='uq_uom_customs_code'), + schema='a76' + ) + + # 5. Tabla: units_of_measure (Main Unit of Measure) + 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=2), 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(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True), + sa.ForeignKeyConstraint( + ['customs_code', 'tenant_id', 'company_id'], + ['a76.unit_of_measure_customs.code', 'a76.unit_of_measure_customs.tenant_id', 'a76.unit_of_measure_customs.company_id'], + name='fk_uom_customs' + ), + sa.ForeignKeyConstraint( + ['american_code', 'tenant_id', 'company_id'], + ['a76.unit_of_measure_american.code', 'a76.unit_of_measure_american.tenant_id', 'a76.unit_of_measure_american.company_id'], + name='fk_uom_american' + ), + sa.ForeignKeyConstraint( + ['ace_code', 'tenant_id', 'company_id'], + ['a76.unit_of_measure_ace.code', 'a76.unit_of_measure_ace.tenant_id', 'a76.unit_of_measure_ace.company_id'], + name='fk_uom_ace' + ), + sa.ForeignKeyConstraint( + ['oma_code', 'tenant_id', 'company_id'], + ['a76.unit_of_measure_oma.code', 'a76.unit_of_measure_oma.tenant_id', 'a76.unit_of_measure_oma.company_id'], + name='fk_uom_oma' + ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='uq_uom_code'), + schema='a76' + ) + + # 6. Tabla: units_of_measure_general (General/Conversion Units) + 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=5), nullable=True), + sa.Column('customs_code', sa.String(length=2), 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(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.Column('updated_at', sa.DateTime(timezone=True), nullable=True), + sa.ForeignKeyConstraint( + ['customs_code', 'tenant_id', 'company_id'], + ['a76.unit_of_measure_customs.code', 'a76.unit_of_measure_customs.tenant_id', 'a76.unit_of_measure_customs.company_id'], + name='fk_uom_general_customs' + ), + sa.ForeignKeyConstraint( + ['ace_code', 'tenant_id', 'company_id'], + ['a76.unit_of_measure_ace.code', 'a76.unit_of_measure_ace.tenant_id', 'a76.unit_of_measure_ace.company_id'], + name='fk_uom_general_ace' + ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('code', 'tenant_id', 'company_id', name='uq_uom_general_code'), + schema='a76' + ) + + +def downgrade() -> None: + """Downgrade schema.""" + op.drop_table('units_of_measure_general', schema='a76') + op.drop_table('units_of_measure', 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_oma', schema='a76') + op.drop_table('unit_of_measure_ace', schema='a76') diff --git a/backend/api/v1/modules/public/reference_data/material_types/routes.py b/backend/api/v1/modules/public/reference_data/material_types/routes.py index a25d3fb9..a90f7b06 100644 --- a/backend/api/v1/modules/public/reference_data/material_types/routes.py +++ b/backend/api/v1/modules/public/reference_data/material_types/routes.py @@ -15,11 +15,17 @@ router = APIRouter(prefix="/material-types") async def list_material_types( page: int = Query(1, ge=1, description="Número de página"), page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"), + type: str = Query(None, description="Filtrar por tipo (ACTIVO FIJO, MATERIALES, PRODUCTOS)"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(MaterialType) + + # Aplicar filtro por tipo si se proporciona + if type: + query = query.filter(MaterialType.type == type) + items = query.offset(skip).limit(page_size).all() total = query.count() return { diff --git a/frontend/src/lib/api/dashboard/refrence_data/material_types.ts b/frontend/src/lib/api/dashboard/refrence_data/material_types.ts index e9457401..3e9e1671 100644 --- a/frontend/src/lib/api/dashboard/refrence_data/material_types.ts +++ b/frontend/src/lib/api/dashboard/refrence_data/material_types.ts @@ -37,11 +37,20 @@ export const materialTypesApi = { * Lista todos los tipos de material con paginación * @param page - Número de página (por defecto 1) * @param pageSize - Tamaño de página (por defecto 50) + * @param type - Filtrar por tipo (ACTIVO FIJO, MATERIALES, PRODUCTOS) */ - list: (page = 1, pageSize = 50) => - api.get( - `/v1/public/refrence_data/material-types?page=${page}&page_size=${pageSize}` - ), + list: (page = 1, pageSize = 50, type?: string) => { + const params = new URLSearchParams({ + page: page.toString(), + page_size: pageSize.toString() + }); + if (type) { + params.append('type', type); + } + return api.get( + `/v1/public/refrence_data/material-types?${params.toString()}` + ); + }, /** * Obtiene un tipo de material por key